vaguely

和歌山に戻りました。ふらふらと色々なものに手を出す毎日。

UnityのAnimatorを今さらながら触ってみた

はじめに

まず懺悔からですが、これまでUnityでアニメーションを扱うとき、レガシーシステムのAnimationを使用していました(..)_。

とはいえそろそろこのままでもマズいってんで、Animatorを使ってみることにしました。

やりたいこと

  • 3Dモデル(fbx形式でインポート)のアニメーションを再生・逆再生する
  • アニメーションは3Dモデルに付与した状態でUnityにインポート
  • uGUI のボタンを押したときにアニメーションを再生する

準備

まず3Dモデルを用意します。

今回はBlenderを使って、このあたりの情報を参考に簡単なアニメーションを付与しました。

blender-cg.net

インポートした3Dモデルは、 Animations タブで下記のようにフレームとアニメーション名を設定しておきます。
今回はデフォルトの状態(0 ~ 1フレーム)と、デフォルト位置から移動先への移動アニメーション(1 ~ 20フレーム)を設定しました。

f:id:mslGt:20171029164702j:plain

Animator Controller

各アニメーションの設定は、 Animator Controller を使用することにします。

まずメニューの Create > Animator Controller で Animator Controller を作成します。

Animator ウインドウを開き、 Create State で State を3つ追加します。

その内の一つはデフォルトの状態を保持する State とします。

State を選択した状態で Inspector を開き、名前と Motion に先ほど3Dモデルに設定した StartPosition を指定します。
また、起動時に自動で実行されてほしいので、 Entry State の上で右クリック > Make Transition で、 この State に紐づけます。

オブジェクトを移動させるアニメーションは、それぞれ Motion に Moved を指定し、
逆再生(移動先から元の位置に戻す)は、 Speed を -1 に設定します。

今回は State の状態に合わせてアニメーションを再生するのではなく、
ボタン押下時に再生するので他の State には紐づけません。

f:id:mslGt:20171029164728j:plain

アニメーションを再生する

アニメーションを再生するのは単純に Play("Animator Controller で設定した State 名") で実行可能です。

public Animator CubeAnimator;

public void PlayMoveAnime()
{
    CubeAnimator.Play("Move");
}
public void PlayReturnAnime()
{
    CubeAnimator.Play("ReturnToStart");
}
public void ResetAnime()
{
    // 再生中のアニメーションを一旦止める.
    CubeAnimator.enabled = false;
    // デフォルトのアニメーションを再生.
    CubeAnimator.Play("DefaultPosition");
    CubeAnimator.enabled = true;
}
  • 一応デフォルトのアニメーションに戻す前にアニメーションを止めていますが、
    今回の内容であれば無くても問題なく動作します。

アニメーションが終わったか確認する

レガシーシステムの Animation と違い、アニメーションが再生されているかを確認する IsPlaying などのメソッドは無いようです。

これを実現する方法はいくつかあるようですが、
今回は normalizedTime を見ることにしました。

CubeAnimator.GetCurrentAnimatorStateInfo(0).normalizedTime で、
アニメーションが再生中なら 0 ~ 1 未満、再生が完了していたら1以上の値が取得できます。

なお GetCurrentAnimatorStateInfo(0) の 0 は Animator Controller のレイヤー番号です。

今回はデフォルトのまま Base Layer を使用しているので、 0 を指定しています。

using System.Collections;
using UnityEngine;

public class AnimeController : MonoBehaviour
{

    public Animator CubeAnimator;

    public void PlayMoveAnime()
    {
        CubeAnimator.Play("Move");
        StartCoroutine(CheckIsAnimeFinished());
    }

    public void PlayReturnAnime()
    {
        CubeAnimator.Play("ReturnToStart");
        StartCoroutine(CheckIsAnimeFinished());
    }
    public void ResetAnime()
    {
        // 実行中の Coroutine を止めているが効いてない?
        StopCoroutine(CheckIsAnimeFinished());
        CubeAnimator.enabled = false;
        CubeAnimator.Play("DefaultPosition");
        CubeAnimator.enabled = true;
    }

    private IEnumerator CheckIsAnimeFinished()
    {
        // Play 実行直後はアニメーションが再生されていないため、 0.3 秒待ってからチェック開始.
        yield return new WaitForSeconds(0.3f);
        while (true)
        {
            // normalizedTime が 1 以上ならチェック完了.
            if (CubeAnimator.GetCurrentAnimatorStateInfo(0).normalizedTime > 1f)
            {
                Debug.Log("Finished");
                break;
            }
            // 0.1 秒ごとにチェックする.
            yield return new WaitForSeconds(0.1f);
        }
    }
}
  • ResetAnime() にて、実行中の Coroutine を止めていますが、
    処理が止まるより先にアニメーション終了の処理が実行されてしまうため、もう一つフラグを追加するなどの対策が必要そうです。

終わりに

今回のようにシンプルな再生・逆再生を行うだけであれば、Animation より断然シンプルに描くことができて良いですね。

ただ、アニメーションの完了をチェックするなど直接チェックできない部分もあり、
もう少し慣れないとな~、という気持ちもあります。

Animator では uGUI なども触れるようなので、 Tween 系や UniRx などを使って実装していた GUI アニメーションでも使ってみようかと思います。

参照

Blender

Unity