歓迎会
今年度の新人歓迎会に顔を出したら去年の新人に顔を覚えられていなかった影の薄い皆さんこんにちは。そりゃぁ普段、新人の子と顔を合わすような場所にはいませんが、一応忘年会などには顔を出してアピールしているはずなんですがね、おっかしいなぁ・・・
AnimatorControllerが面倒くさい
Unityで3Dモデルに対して何かしらのアニメーションを再生させたい場合、AnimatorControllerにAnimationClipを設定したState(状態)を定義し、それぞれのStateへの遷移条件を設定、スクリプト側で遷移条件に応じた処理を実施、という流れで実装する必要があります。
これはまぁ慣れてしまえばさっさと作ることができますが、再生したいアニメーション定義が増えていくと次第に目で追うのも大変になっていき、その後に改修作業が入ってくると遷移条件を把握するだけでも時間をとられたりします。また、逆に走ってジャンプしたいだけだとか、お試しで動かしてみたいだけだとか、そんな場合でもAnimatorControllerを一から作成して制御スクリプトを組むとなると少し面倒だったりします。
例えばアセットストアからキャラクターのアセットを購入したら結構多くのAnimationClipが付随していて有難いものの、ここからAnimatorControllerでステートマシンを組もうとすると結構時間が取られます。
こんな感じでAnimationClipが大量にあったり
なのでアニメーション制御をできるだけシンプルに実装するための手段の一つとして「Playable API」を導入してみることにしました。
Playable API
Playable APIはUnity2017あたりから搭載された機能でアニメーション、オーディオ、ビデオ、その他再生したいものをPlayableGraphというモジュールを介して制御・再生することができる機能です。有名どころではUnity Timelineはこの技術を使って実現されていたります。
最初に「再生したいものをPlayable Graphというモジュールを介して制御・再生する」と書きましたが、Playable APIを使うにあたってはこの概念が非常に重要で、Playable APIとは「再生したいもの=Input」を「再生するもの=Output」へ橋渡しをするための機能だと言えます。
例えばアニメーションの場合、AnimationClipをAnimatorを使って再生するので、AnimationClipがInput、AnimatorがOutputだと捉えることになります。
よってPlayable APIを使ってアニメーションの再生するには以下の手順を踏みます。
- PlayableGraphを作成する
- InputとなるPlayable(=AnimationClip)を作成する
- OutputとなるPlayable(=Animator)を作成する
- InputをOutputに接続する
手順が多いと感じるかもしれませんがスクリプト側の実装は非常にシンプルです。
[SerializeField] private AnimationClip MoveClip; [SerializeField] private Animator TargetAnimator; private PlayableGraph PenguinGraph; private void Init_TestPlayabeGraph() { // 1.PlayableGraphの作成 PenguinGraph = PlayableGraph.Create("TestGraph"); // 2.Playable(インプット)の作成 AnimationClipPlayable playable = AnimationClipPlayable.Create(PenguinGraph, MoveClip); // 3.PlayableOutput(アウトプット)の作成 AnimationPlayableOutput output = AnimationPlayableOutput.Create(PenguinGraph, "AnimationOutput", TargetAnimator); // 4.PlayableOutputにPlayableを接続 output.SetSourcePlayable(playable); }
上記の設定を行った後にPlayableGraphを使って再生します。
void Update() { if (Input.GetKeyDown(KeyCode.S)) { // 5.PlayableGraphを通して再生 PenguinGraph.Play(); } } private void OnDestroy() { // 6.使い終わったらPlayableGraphを破棄 PenguinGraph.Destroy(); }
実際の動作例として灰色のペンギンモデルに対して上記のスクリプトをアタッチして途中から別のアニメーションを再生してみます。
別レイヤーのアニメーションも再生する
先ほどのペンギンのモデルには動作のアニメーション以外にもキャラクターの表情を変えるアニメーションも付随しているので、そういった場合にはどちらも同時に再生したいものです。
表情を変えるAnimationClipたち
この場合、再生したいもの=Inputが複数のAnimationClipで再生するもの=Outputが一つのAnimatorとなるので、Input側ではInput(Playable)を複合して格納するAnimationMixerPlayableへ接続し、OutputへはこのAnimationMixerPlayableを接続するようにします。
先ほどのソースと比べると少し複雑ですが変更点は「2.InputとなるPlayableを作成する」の部分だけで、その他は今まで紹介した手順と同じです。
[SerializeField] private AnimationClip MoveClip1; [SerializeField] private AnimationClip MoveClip2; [SerializeField] private Animator TargetAnimator; private PlayableGraph PenguinGraph; AnimationMixerPlayable MixerPlayable; private void Init_TestPlayabeGraph() { // 1.PlayableGraphの作成 PenguinGraph = PlayableGraph.Create("TestGraph"); // 2.Playable(インプット)の作成 :2つのPlayableが入力として接続されるので引数に「2」 MixerPlayable = AnimationMixerPlayable.Create(PenguinGraph, 2); AnimationClipPlayable movePlayable1 = AnimationClipPlayable.Create(PenguinGraph, MoveClip1); AnimationClipPlayable movePlayable2 = AnimationClipPlayable.Create(PenguinGraph, MoveClip2); MixerPlayable.ConnectInput(0, movePlayable1, 0); MixerPlayable.ConnectInput(1, movePlayable2, 0); // 3.PlayableOutput(アウトプット)の作成 AnimationPlayableOutput output = AnimationPlayableOutput.Create(PenguinGraph, "AnimationOutput", TargetAnimator); // 4.PlayableOutputにPlayableを接続 output.SetSourcePlayable(MixerPlayable); MixerPlayable.SetInputWeight(0, 1.0f); MixerPlayable.SetInputWeight(1, 1.0f); }
上記のソースの一番最後の部分でAnimationMixerPlayableへ接続した二つのAnimationClipに対してアニメーションのブレンドをするための重みづけの設定を行っています。
今回の場合、動作のAnimationClipと表情のAnimationClipはどちらも同時に再生するので、どちらにも「1.0f」のウェイトを設定しています。
実際の動作がこちら、灰色のペンギンが別アニメーションへ遷移するのと同時に表情も変わることが分かります。
今回のまとめ
Playable APIはアニメーションの再生に拘わらず汎用的に使える機能ですが多くの場合はアニメーションの制御に使用され、AnimatorControllerステートマシン地獄から脱出することができます。
また、Playable APIにはInputとOutputを定義する必要があり、Inputが複数あるケースにも対応しています。
ゲーム仕様を実装するための一つの手法として役立つのではないでしょうか。