【UEFN】Verseのアニメーションコントローラーで複雑なアニメーションを実現する (Verseの学習⑧)

AnimationControllerを使用すれば
Verseから複雑なアニメーションが実現可能!

UEFNのAnimationControllerによるアニメーション

UEFNでは、LevelSequenceでのキーフレームアニメーションをGUI上で行うかと思います。

LevelSequenceでは、ゲーム上のオブジェクトに対して、どういった時間でどういった状態になっているかをキーフレームとして事前登録しておき、これを再生することで、ゲーム上のオブジェクトのアニメーションを作成します。

LevelSequenceでアニメーション設定を行っている画面
事前にアニメーションを作成しておく必要がある

ただ、LevelSequenceは、すべて事前にEditor上で行わなければならず、ゲーム中にキーフレームを変更することはできません。

これを解決するのがAnimationControllerです。

VerseのAnimationController機能を使えば、ゲームの状態に応じて自由にキーフレームを与え、複雑なアニメーションをさせることができます

実は、今までこのブログでも何度か使用してきた、MoveTo関数(「指定した位置、回転、スケールに、指定した時間で移動するための関数)も、内部的にはAnimationControllerの機能を使用して実装されているようです。

MoveToだけでは、できることが限られていますが、AnimationController自体を使いこなせるようになれば、できることが大幅に広がるようになります。

そこで、今回は、このAnimationControllerの基本を簡単に紹介していきます。

AnimationControllerを使用する流れ

AnimationControllerは以下の流れで使用していきます。

  1. (位置情報、回転情報、時間情報など)といった情報をひとまとめにしたkeyframe_delta型の配列を作成
  2. creative_propについているAnimationControllerの情報を取得
  3. 1で作成したキーフレーム配列を、AnimationControllerにセットし、再生を開始。

Verse上から制御できるアイテムは、現状CreativePropだけかと思いますが、CreativePropは特に何も設定しなくても、内部的にAnimationControllerが設定されています

このAnimationControllerを取得し、そこにキーフレームを与えて動作させる、といった流れになります。

AnimationControllerのイベント(特定のキーフレームまでアニメーションが再生された、アニメーションが終了したなど)で何らかの処理をしたい場合は、それぞれのイベントに対して、実行する処理を登録しておくことも可能です。

AnimationController基本形

もっとも基本的なAnimationControllerを使用したサンプルは以下のようになります。

ボタンを押したら、RingoPropで設定したものを回転させながら、X軸方向に200cm動かすサンプルになります。

using { /Fortnite.com/Devices }
using { /Verse.org/Simulation }
using { /UnrealEngine.com/Temporary/Diagnostics }

### Added
using { /Fortnite.com/Devices/CreativeAnimation } 
using { /UnrealEngine.com/Temporary/SpatialMath }

## Ringo Means Apple in Japanese
animation_controller_test_device := class(creative_device):
    @editable
    RingoProp : creative_prop = creative_prop{}

    @editable
    ActionButton : button_device = button_device{}

    OnBegin<override>()<suspends>:void=
        ActionButton.InteractedWithEvent.Subscribe(OnButtonInteracted)
        return

    OnButtonInteracted<public>(Agent : agent) : void = 
        if:
            AnimationController := RingoProp.GetAnimationController[] ### *01
        then:
            ## make keyframe ### *02
            KeyFrame := keyframe_delta :
                  DeltaLocation := vector3{X:=200.0}
                  DeltaRotation := MakeRotationFromYawPitchRollDegrees(180.0, 0.0, 0.0)
                  Time := 10.0

            ## make keyframe array ### *03
            KeyFrames : []keyframe_delta = array { KeyFrame }
            
            ## SetAnimation To Animation Controller ### *04
            AnimationController.SetAnimation(KeyFrames, ?Mode:=animation_mode.OneShot)
        
            ## Play Animation ### *05
            AnimationController.Play()

        return
  • まず、プロップについているAnimationControllerを取得します(*01)。 これは失敗する可能性があるため、失敗コンテクストの中で実施します。基本的にCreativePropには、特別な設定をしなくても、最初からAnimationControllerが与えられています。

  • 次にkeyframe_delta型でキーフレーム情報を一つ作っています(*02)。keyframe_delta型は以下の形になっています。

        keyframe_delta<native><public> := struct:
            # Target position of the `creative_prop`. 
            # This is a world-space coordinate in cm, 
            # with the initial position of the `creative_prop` acting as coordinate (0,0).
            DeltaLocation<native><public>:vector3

            # Target rotation for the `creative_prop`. 
            # Rotations are relative to the starting rotation ofthe `creative_prop`
            DeltaRotation<native><public>:rotation

            # Target scale for the `creative_prop`. 
            # Scale is multiplicative to the starting Scale of the `creative_prop`
            DeltaScale<native><public>:vector3 = external {}

            # Time in seconds the `creative_prop` should animate between
            #  its last frame and this frame.
            Time<native><public>:float

            # Interpolation mode for this `keyframe_delta`.
            # Default = `InterpolationTypes.Linear`
            Interpolation<native><public>:cubic_bezier_parameters = external {}
  • 移動、回転、スケールの情報を入力します。DeltaLocation,DeltaRotation,DeltaScaleとなっているように、現在値からの差分値(Delta)で指定する必要があります。

  • 今回の場合は、初期値が与えられているDeltaScaleとInterpolationに関しては省略可能なため、省略しています。

  • 回転に関しては、MakeRotationFromYawPitchRollDegreesを使用して回転情報の作成を行っています。

  • この場合はキーフレームを一つしか作成していませんが、実際には複数のキーフレームを設定してアニメーションすることになります。

  • SetAnimation関数の引数はkeyframe_deltaの配列にする必要があるため、配列にしています(*03)。

  • 作成した配列をアニメーションコントローラにセットします(*04)。

  • このキーフレームをどういった再生モードで再生するかanimation_mode型で指定することで選択することができます。animation_modeは列挙型になっており、以下の3つのパターンから選択することができます。

        # Animation play modes.
        animation_mode<native><public> := enum:
            # Stop after playing the animation once.
            OneShot
            # Reverse direction after reaching the final `keyframe_delta`,
            # then play the animation in reverse.
            PingPong
            # Play the animation in a loop. 
            # This requires the animation ends exactly where it began.
            Loop
  • これを指定している引数 Modeは先頭に?マークが付いています。

  • これは名前付き引数と呼ばれる文法で、名前付き引数に値を与えるときはこのような書き方をする必要があります。

AnimationController.SetAnimation(KeyFrames, ?Mode:=animation_mode.OneShot)
  • 通常、名前付き引数は変数としての初期値が与えられている場合が多いですが、SetAnimation関数では特に与えられていません。かつ、名前付き引数も一つしかないため、これを名前付き引数にする意味が現状では、あまりないように思われます。不思議な形になっていますが、将来的な拡張のために、あえて名前付き引数にしてあるのではないかと予想しています

最後にこれをプレイすることで動作します。

シーン上にボタンと適当なプロップを配置し、実行します。

ゲーム上のものを適宜設定

ボタンを押したタイミングで、プロップが横に動けば正常に動作しています。

ボタンを押すとRingoが動く

AnimationController発展形

では、次に、発展形として、ボタンを押した際に、Propが大きくなるアニメーションを組んでみたいと思います。

イメージとしては、マリオがキノコを食べたときに大きくなる時のように、瞬間的に膨らんでから少し縮むを繰り返しながら大きくなるイメージを目指します。

ボタンを押したときのコードを以下のように書き換えます。

        if:
            AnimationController := RingoProp.GetAnimationController[] ### *01
        then:
            ## make empty keyframes 
            var KeyFrames : []keyframe_delta = array {}
            
            ## append keyframes
            for(Index := 0..2):
                set KeyFrames += array{
                    keyframe_delta :
                                DeltaLocation := vector3{X:=0.0}
                                DeltaRotation := rotation{}
                                DeltaScale := vector3{X:=1.3, Y:=1.3, Z:=1.3}
                                Interpolation := InterpolationTypes.EaseOut
                                Time := 0.05
                    keyframe_delta :
                                DeltaLocation := vector3{X:=0.0}
                                DeltaRotation := rotation{}
                                DeltaScale := vector3{X:=0.95, Y:=0.95, Z:=0.95}
                                Interpolation := InterpolationTypes.EaseInOut
                                Time := 0.15
                }
            
            ## SetAnimation To Animation Controller ###
            AnimationController.SetAnimation(KeyFrames, ?Mode:=animation_mode.OneShot)
        
            ## Play Animation ###
            AnimationController.Play()
  • まず、keyframe_deltaを入れておく空の配列を準備します。for文を使用し、これに対してキーフレームを追加していきます。

  • 追加しているキーフレームは、0.07秒後に大きさを一度1.3倍にするキーフレームと、その0.15秒後に0.95倍に縮めるキーフレームです。この2つのキーフレームで大きさが1.235倍(1.3 x 0.95)になることになります。

  • これを3回繰り返しています。

  • キーフレーム補完モードもInterpolationで設定しています。

  • 手動で設定することもできますが、今回はUEFNで準備されているInterpolationTypesを使用して簡単に設定しています。

  • InterpolationTypesとしては以下が準備されており、この中からEaseOutとEaseInOutをそれぞれ選択しました。

        InterpolationTypes<public> := module:
            # `Linear` animations move at a constant speed.
            Linear<public>:cubic_bezier_parameters = external {}

            # `Ease` animations start slowly, speed up, then end slowly.
            Ease<public>:cubic_bezier_parameters = external {}

            # `EaseIn` animations start slow, then speed up towards the end.
            EaseIn<public>:cubic_bezier_parameters = external {}

            # `EaseOut` animations start fast, then slow down towards the end.
            EaseOut<public>:cubic_bezier_parameters = external {}

            # `EaseInOut` animations are similar to `Ease`
            # but the start and end animation speed is symmetric.
            EaseInOut<public>:cubic_bezier_parameters = external {}

さきほどと同じように実行してみます。

ボタンを押したタイミングで、リンゴがアニメーションしながら大きくなります。

グン、グン、グンといった感じで大きくなるアニメーション

(Gifではただのノイズに見えてしまっていますが・・・(涙))

まとめ

今回は簡単なサンプルで、AnimationControllerの使用方法を解説しました。

MoveTo関数のみではあまり複雑なことはできませんが(現状の仕様だとできてもあまり滑らかにならない)、AnimationControllerを使用すれば、Verse上からキーフレームアニメーションを自由に作ることができます。

UEFNのVerse制御では、このAnimationControllerが現状では、複雑なアニメーションを組むための唯一の選択になるため、ぜひ一度使用されることをお勧めします。

UEFN関連の投稿がかなりたまってきたため、UEFN投稿のまとめページを作ってみました。

ringogames.hatenablog.com

ぜひほかの記事も見ていただけると嬉しいです。

RingoGamesではUEFNに関するさまざまな情報を発信していきます。Twitterでお知らせしていきますので、よろしければ、Twitterのフォローをしていただけると幸いです。

Twitterはこちら

twitter.com