📗

【Unity】AnimationManagerについて

2024/08/29に公開

はじめに

初めての方も、そうでない方もこんにちは!
現役ゲームプログラマーのたむぼーです。
自己紹介を載せているので、気になる方は見ていただければ嬉しいです!

今回は
 AnimationManager
について紹介します

https://zenn.dev/tmb/articles/1072f8ea010299

アニメーション統合クリップ

前回の記事のアニメーション統合クリップを使用した、アニメーションマネージャーを実装しました。

https://zenn.dev/tmb/articles/4d4fb5a48e2982

使い方

// 対象のオブジェクト(target)のShowアニメーションを実行
target.SetActive(false); // Showの場合は先に非表示にする
AnimationManager.Instance.AddAnimationObject(target, AnimationType.Show);

// 対象のオブジェクト(target)のHideアニメーションを実行
AnimationManager.Instance.AddAnimationObject(target, AnimationType.Hide);
変数 説明
GameObject obj AnimatorControllerをアタッチしたオブジェクト
AnimationType type どのアニメーションをさせるか
Action beforeAction アニメーション開始前に実行するアクション
Action afterAction アニメーション終了後に実行するアクション

スクリプト

アニメーションマネージャー
AnimationManager.cs
using System;
using System.Collections.Generic;
using UnityEngine;
using UniRx;
using Cysharp.Threading.Tasks;
using System.Threading;
using System.Linq;

namespace Utils
{
    sealed public class AnimationManager
    {
        private static AnimationManager _instance = null;
        public static AnimationManager Instance
        {
            get
            {
                _instance ??= new AnimationManager();
                return _instance;
            }
        }

        private List<AnimationObject> _animationObjects = new List<AnimationObject>();

        /// <summary>
        /// アニメーションする対象を追加
        /// </summary>
        public AnimationObject AddAnimationObject(GameObject obj, AnimationType type, Action beforeAction = null, Action afterAction = null)
        {
            Animator animator = obj.GetComponent<Animator>();
            if (animator == null)
            {
                return null;
            }

            AnimationObject existingAnimationObject = _animationObjects.FirstOrDefault(a => a.obj == obj);
            if (existingAnimationObject != null)
            {
                CancelAnimation(existingAnimationObject);
                _animationObjects.Remove(existingAnimationObject);
            }

            AnimationObject animationObject = new AnimationObject()
            {
                obj = obj,
                animator = animator,
                type = type,
                beforeAction = beforeAction,
                afterAction = afterAction,
                ctsHelper = new CancellationTokenHelper()
            };
            _animationObjects.Add(animationObject);

            ApplyAndPlayAnimation(animationObject).Forget();

            return animationObject;
        }

        /// <summary>
        /// アニメーションする対象を事前読み込み
        /// </summary>
        public AnimationObject PreloadAddAnimationObject(GameObject obj, AnimationType type, Action beforeAction = null, Action afterAction = null)
        {
            Animator animator = obj.GetComponent<Animator>();
            if (animator == null)
            {
                return null;
            }

            AnimationObject existingAnimationObject = _animationObjects.FirstOrDefault(a => a.obj == obj);
            if (existingAnimationObject != null)
            {
                CancelAnimation(existingAnimationObject);
                _animationObjects.Remove(existingAnimationObject);
            }

            AnimationObject animationObject = new AnimationObject()
            {
                obj = obj,
                animator = animator,
                type = type,
                beforeAction = beforeAction,
                afterAction = afterAction,
                ctsHelper = new CancellationTokenHelper()
            };
            _animationObjects.Add(animationObject);

            ApplyAnimationProperties(animationObject.animator, animationObject.obj, animationObject.type);

            return animationObject;
        }

        /// <summary>
        /// 事前に読み込んだアニメーションオブジェクトを実行
        /// </summary>
        public void ActivatePreloadedAnimation(AnimationObject animationObject)
        {
            ApplyAndPlayAnimation(animationObject).Forget();
        }

        /// <summary>
        /// アニメーションプロパティの適用
        /// </summary>
        public void ApplyAnimationProperties(Animator animator, GameObject target, AnimationType type)
        {
            AnimationController.ApplyProperties(animator, target, type);
        }

        /// <summary>
        /// アニメーションを再生
        /// </summary>
        public async UniTask PlayAnimation(Animator animator, AnimationType type, Action beforeAction, Action afterAction, CancellationToken token)
        {
            await AnimationController.Play(animator, type, beforeAction, afterAction, token);
        }

        /// <summary>
        /// アニメーションの時間を取得
        /// </summary>
        public float GetAnimationTime(AnimationObject animationObject)
        {
            return AnimationController.GetAnimationTime(animationObject.animator, animationObject.type);
        }

        /// <summary>
        /// アニメーションをキャンセル
        /// </summary>
        public void CancelAnimation()
        {
            foreach (var animationObject in _animationObjects)
            {
                CancelAnimation(animationObject);
            }
            _animationObjects.Clear();
        }

        /// <summary>
        /// 特定のアニメーションをキャンセル
        /// </summary>
        private void CancelAnimation(AnimationObject animationObject)
        {
            animationObject.ctsHelper.Dispose();
        }

        /// <summary>
        /// アニメーションを適用し再生する処理
        /// </summary>
        private async UniTaskVoid ApplyAndPlayAnimation(AnimationObject animationObject)
        {
            ApplyAnimationProperties(animationObject.animator, animationObject.obj, animationObject.type);
            try
            {
                if (animationObject.type == AnimationType.Show)
                {
                    if (animationObject != null && animationObject.obj != null)
                    {
                        animationObject.obj.SetActive(true);
                    }
                }
                await PlayAnimation(animationObject.animator, animationObject.type, animationObject.beforeAction, animationObject.afterAction, animationObject.ctsHelper.Token);
                if (animationObject.type == AnimationType.Hide)
                {
                    if (animationObject != null && animationObject.obj != null)
                    {
                        animationObject.obj.SetActive(false);
                    }
                }
            }
            catch (OperationCanceledException)
            {
                // キャンセル時の処理
            }
            _animationObjects.Remove(animationObject);
        }
    }
}
アニメーションコントローラー
AnimationController.cs
using System;
using UnityEngine;
using Cysharp.Threading.Tasks;
using System.Threading;
using System.Linq;

namespace Utils
{
    [Serializable]
    sealed public class AnimationController
    {
        /// <summary>
        /// 最初のフレームのプロパティを適用
        /// </summary>
        static public void ApplyProperties(Animator animator, GameObject target, AnimationType type)
        {
            // アニメーションクリップを取得
            string stateName = type.ToString();
            AnimationClip animationClip = animator.runtimeAnimatorController.animationClips.FirstOrDefault(clip => clip.name == stateName);
            if (animationClip != null)
            {
                // アニメーションの初期フレームをターゲットに適用
                animator.enabled = false;
                animationClip.SampleAnimation(target, 0);
            }
        }

        /// <summary>
        /// 再生処理
        /// </summary>
        static public async UniTask Play(Animator animator, AnimationType type, Action beforeAction, Action afterAction, CancellationToken token)
        {
            string stateName = type.ToString();

            beforeAction?.Invoke();

            animator.enabled = true;

            // アニメーションの再生
            await PlayAnimation(animator, stateName, token);

            afterAction?.Invoke();
        }

        /// <summary>
        /// アニメーションを実行し、完了を待つ
        /// </summary>
        static private async UniTask PlayAnimation(Animator animator, string stateName, CancellationToken token)
        {
            if (animator == null)
            {
                return;
            }

            animator.Play(stateName);

            await UniTask.WaitUntil(() =>
            {
                if (animator == null || animator.gameObject == null)
                {
                    return true;
                }

                return animator.GetCurrentAnimatorStateInfo(0).IsName(stateName) &&
                       animator.GetCurrentAnimatorStateInfo(0).normalizedTime >= 1.0f;
            }, cancellationToken: token);
        }

        /// <summary>
        /// 指定されたステート名のアニメーションの時間を取得
        /// </summary>
        static public float GetAnimationTime(Animator animator, AnimationType type)
        {
            if (animator == null)
            {
                return 0.0f;
            }

            string stateName = type.ToString();

            AnimationClip animationClip = animator.runtimeAnimatorController.animationClips.FirstOrDefault(clip => clip.name == stateName);
            return animationClip != null ? animationClip.length : 0f;
        }
    }
}
アニメーション対象のオブジェクト
AnimationObject.cs
using System;
using UnityEngine;

namespace Utils
{
    sealed public class AnimationObject
    {
        public GameObject obj;
        public Animator animator;
        public AnimationType type;
        public Action beforeAction;
        public Action afterAction;
        public CancellationTokenHelper ctsHelper;
    }
}
アニメーションタイプ
AnimationType.cs
namespace Utils
{
    public enum AnimationType
    {
        /// <summary> なし </summary>
        None,
        /// <summary> 表示アニメーション </summary>
        Show,
        /// <summary> 非表示アニメーション </summary>
        Hide,
    }
}

Discussion