search
LoginSignup
10

More than 5 years have passed since last update.

posted at

Organization

[Unity]アニメーションのコールバック

環境

 ・Unity 2017.1.0p5
 ・.NET 4.6
 ・UniRx

そもそもUnityのAnimator細かいところが足りてない

 ・PlayしたフレームでAnimatorStateInfoとか取得できない
 ・再生中かどうかのプロパティが存在しない

アニメーションの終了フラグ

 とりあえず共通で使える終了判定が欲しかった

AnimatorExtension.cs
    public static bool IsEnd(this Animator animator, int layerIndex = 0)
    {
        //オブジェクトがアクティブではない時は終了と同義とする
        if (!animator.gameObject.activeInHierarchy)
            return true;

        return animator.GetCurrentAnimatorStateInfo(layerIndex).normalizedTime >= 1;
    }

Reactiveな終了フラグ

 UniRxに慣れるとReactiveな終了フラグも欲しくなる

AnimatorExtension.cs
    private static Dictionary<UniRx.Tuple<Animator,int>, ReactiveProperty<bool>> _reactiveProperties = new Dictionary<UniRx.Tuple<Animator, int>, ReactiveProperty<bool>>();

    public static ReactiveProperty<bool> GetIsEndReactiveProperty(this Animator animator, int layerIndex = 0)
    {
        var key = new UniRx.Tuple<Animator, int>(animator, layerIndex);
        if (_reactiveProperties.ContainsKey(key))
            return _reactiveProperties[key];

        _reactiveProperties.Add(key, animator.ObserveEveryValueChanged(_ => animator.IsEnd(layerIndex)).ToReactiveProperty());

        //解放処理
        animator.OnDestroyAsObservable().Subscribe(_ => _reactiveProperties.Remove(key));

        return _reactiveProperties[key];
    }

Playのオーバーロード

 Play時に色々設定したい!

AnimatorExtension.cs

    /// <summary>
    /// アニメーション再生 コールバック指定可
    /// </summary>
    /// <param name="animator"></param>
    /// <param name="stateName"></param>
    /// <param name="layer"></param>
    /// <param name="normalizedTime"></param>
    /// <param name="nextFrameAction">再生開始の次フレーム時コールバック</param>
    /// <param name="endAction">終了時コールバック</param>
    public static void Play(this Animator animator, string stateName, int layer = 0, float normalizedTime = 0f, Action nextFrameAction = null, Action endAction = null)
    {
        animator.Play(stateName, layer, normalizedTime);

        if (endAction != null || nextFrameAction != null)
            Observable.NextFrame().Subscribe(_ =>
            {
                nextFrameAction?.Invoke();
                if (endAction != null)
                    animator.GetIsEndReactiveProperty()
                    .First(isEnd => isEnd)
                    .Subscribe(__ => endAction())
                    .AddTo(animator);

            }).AddTo(animator);

    }

    /// <summary>
    /// アニメーション再生 コールバック指定可
    /// </summary>
    /// <param name="animator"></param>
    /// <param name="stateNameHash"></param>
    /// <param name="layer"></param>
    /// <param name="normalizedTime"></param>
    /// <param name="nextFrameAction">再生開始の次フレーム時コールバック</param>
    /// <param name="endAction">終了時コールバック</param>
    public static void Play(this Animator animator, int stateNameHash, int layer = 0, float normalizedTime = 0f, Action nextFrameAction = null, Action endAction = null)
    {
        animator.Play(stateNameHash, layer, normalizedTime);

        if (endAction != null || nextFrameAction != null)
            Observable.NextFrame().Subscribe(_ =>
            {
                nextFrameAction?.Invoke();

                if (endAction != null)
                    animator.GetIsEndReactiveProperty()
                        .First(isEnd => isEnd)
                        .Subscribe(__ => endAction())
                        .AddTo(animator);

            }).AddTo(animator);

    }

使い方

hoge.cs
    GetComponent<Animator>().Play("アニメーション名",
        nextFrameAction:()=>{
            Debug.Log("再生開始の次フレーム!アニメーションの各種情報取得できるよ!");
        },
        endAction:()=>{
            Debug.Log("アニメーション終了!");
        }
    );

nextFrameActionはアニメーションスピード調整したい時によく使う
endActionはオブジェクトプール管理してるエフェクトとかで終了時にプールに戻すときにめっちゃ使う

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
What you can do with signing up
10