白泽图

  • 文章
    • Unity渲染
    • Unity项目开发
    • 工具
    • 数学
    • 算法
    • 网站搭建
    • 网络&操作系统
蒋程个人博客
互联网技术经验总结&分享
  1. 首页
  2. Unity项目开发
  3. 正文

Unity Animator实现同一个动作重复打断播放

2021-05-10 3667点热度 0人点赞 2条评论

最近发现一个动作播放的细节问题,策划在技能编辑器中配置了在极短的时间中连续造成伤害的技能,间隔时间比受击动作时间还要短,这就意味着播放同一个动作(受击)时,需要打断之前的播放,注意,我这里说的是同一个动作的打断,并不是同一个动作的播放

经测试,在Animator中同一个状态在播放过程中无法再次播放自己,只有切到其他状态,再切回来这一个方法,但显然这样会导致结果并不是我们想要的,那么如果将两个状态设置成一样动作,然后在这两个状态之间来回切换呢?答案是可以的

于是这里又出现另一个问题,项目已经到后期了,让美术同学把每个AnimatorController挨个改一遍不太现实,所以想到是否可以通过代码的方式动态添加Animator中的状态,可惜答案是否定的,Unity并没有给我们提供相应的接口,为了在不污染的原来美术资源的前提下实现这个功能,第一时间想到了AnimatorOverrideController,当前AnimatorOverrideController也没有添加AnimationClip的功能,不过它却提供了一个覆盖的功能ApplyOverrides,于是我想到了一个方法,首先创建一个包含所有状态的AnimatorController,然后在它里面添加更多的扩展状态,如下图

其中Extend1与Extend2是额外的用于扩展的状态(即然不能动态添加,那么提前加几个,在运行时覆盖),至于Extend1与Extend引用的动作文件,可以通过右键Create/Animation,创建出一个空白的动作文件,(此步是必须的,不可为空,因为AnimatorOverrideController是覆盖,必须要有AnimtionClip)如下图

最后我封装了一个AnimatorEx来代替Animator,这样也不会影响上层的使用

using System.Collections;
using System.Collections.Generic;
using UnityEngine;


namespace AnimationEx
{
    public abstract class AnimatorEx
    {
        private Animator mAnimator;
        private AnimatorOverrideController mOverrideController;

        //模板动画控制器(状态最全的动画控制器)
        private RuntimeAnimatorController mTemplateAnimatorController;
        //模板动画控制器中所有的AnimationClip
        private Dictionary<string, AnimationClip> mTemplateClips = new Dictionary<string, AnimationClip>();
        private Dictionary<string, AnimationClip> mOriginClips = new Dictionary<string, AnimationClip>();

        //所有的替换AnimationClip
        private List<KeyValuePair<AnimationClip, AnimationClip>> mOverrideClips = new List<KeyValuePair<AnimationClip, AnimationClip>>();


        private int mExtendClipCount = 2;
        private int mCurUseExtendClipCount = 0;
        protected AnimatorEx() {}

        private bool Init(Animator animator, UnityEngine.RuntimeAnimatorController origin) 
        {
            if (origin == null)
            {
                Debug.LogError("the origin runtime animator controller is null");
                return false;
            }

            mTemplateAnimatorController = Resources.Load<RuntimeAnimatorController>("AnimatorControllerEx");
            foreach (AnimationClip clip in mTemplateAnimatorController.animationClips)
                mTemplateClips.Add(clip.name, clip);
            
            foreach (AnimationClip clip in origin.animationClips)
            {
                var templateClip = GetTemplateClip(clip.name);
                if (templateClip == null)
                {
                    Resources.UnloadAsset(mTemplateAnimatorController);
                    Debug.LogErrorFormat("template animator controller has no clip {0}", templateClip);
                    return false;
                }
                var replaceInfo = new KeyValuePair<AnimationClip, AnimationClip>(templateClip, clip);
                mOverrideClips.Add(replaceInfo);
                mOriginClips.Add(clip.name, clip);
            }
            mOverrideController = new AnimatorOverrideController();
            mAnimator = animator;
            return OnInit();
        }


        protected AnimationClip GetTemplateClip(string clipName)
        {
            AnimationClip clip = null;
            mTemplateClips.TryGetValue(clipName, out clip);
            return clip;
        }

        protected AnimationClip GetOriginClip(string clipName)
        {
            AnimationClip clip = null;
            mOriginClips.TryGetValue(clipName, out clip);
            return clip;
        }

        protected abstract void OnChangeClips();

        protected string AddClip(AnimationClip clip)
        {
            if (clip == null)
            {
                Debug.LogWarningFormat("add clip fail,the clip is null!");
                return null;
            }
            if (mCurUseExtendClipCount < mExtendClipCount)
            {
                var extendClipName = string.Format("Extend{0}", ++mCurUseExtendClipCount);
                var extendClip = GetTemplateClip(extendClipName);
                if (extendClip == null)
                    Debug.LogErrorFormat("template animator controller has no clip {0}", extendClipName);
                else
                {
                    mOverrideClips.Add(new KeyValuePair<AnimationClip, AnimationClip>(extendClip, clip));
                    return extendClipName;
                }
            }
            else
            {
                Debug.LogErrorFormat("template animator controller has no enough extend clip to use, the max entend num is {0}", mExtendClipCount);
            }
            return null;
        }

        protected void ApplyChangeClips()
        {
            mOverrideController.runtimeAnimatorController = mTemplateAnimatorController;
            mOverrideController.ApplyOverrides(mOverrideClips);
            mAnimator.runtimeAnimatorController = mOverrideController;
        }


        public Animator animator
        {
            get{ return mAnimator; }
        }


        public virtual bool OnInit()
        {
            return true;
        }


        public virtual void Play(int stateNameHash, int layer)
        {
            mAnimator.Play(stateNameHash, layer);
        }


        public static AnimatorEx Instantiate<T>(Animator animator, UnityEngine.RuntimeAnimatorController origin) where T : AnimatorEx, new()
        {
            AnimatorEx ex = new T();
            var ret = ex.Init(animator, origin) ? ex : null;
            if (ret != null)
                ret.OnChangeClips();
            return ret;
        }

        public static AnimatorEx Instantiate<T>(Animator animator) where T : AnimatorEx, new()
        {
            return Instantiate<T>(animator, animator.runtimeAnimatorController);
        }


    }
}

然后我们针对需求进行继承与扩展

using System.Collections;
using System.Collections.Generic;
using UnityEngine;
namespace AnimationEx
{
    public class PFAnimator : AnimatorEx
    {
        private AnimationClip mHit1;
        private int mHit1PlayIndex;
        private int[] mHitHashCodeArray; 

        public override bool OnInit()
        {
            mHit1 = GetOriginClip("Hit1");
            if (mHit1 != null)
            {
                mHitHashCodeArray = new int[2];
                mHitHashCodeArray[0] = Animator.StringToHash(mHit1.name);
            }
            return true;
        }

        protected override void OnChangeClips()
        {
            if (mHitHashCodeArray != null) //只处理战斗的动画控制器
            {
                string extendName = this.AddClip(GetOriginClip("Hit1"));
                if (!string.IsNullOrEmpty(extendName))
                    mHitHashCodeArray[1] = Animator.StringToHash(extendName);
                this.ApplyChangeClips();
            }
        }


        public override void Play(int stateNameHash, int layer)
        {
            if (mHitHashCodeArray != null)
            {
                if (stateNameHash == mHitHashCodeArray[0])
                {
                    mHit1PlayIndex = ++mHit1PlayIndex % 2;
                    stateNameHash = mHitHashCodeArray[mHit1PlayIndex];
                }
            }
            base.Play(stateNameHash, layer);
        }
    }

}

使用方法如下

using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using AnimationEx;
public class Demo : MonoBehaviour
{
    public GameObject mEntity;
    private AnimatorEx mAnimator;
    private int mHit1HashCode;
    void Start()
    {
        var animator = mEntity.GetComponent<Animator>();
        mAnimator = AnimatorEx.Instantiate<PFAnimator>(animator);

        mHit1HashCode = Animator.StringToHash("Hit1");
    }

    void OnGUI()
    {
        if (GUI.Button(new Rect(0, 0, 150, 50), "TEST"))
        {
            mAnimator.Play(mHit1HashCode, 0);
        }
    }
}

最终效果如下:

标签: 暂无
最后更新:2021-05-10

蒋程

这个人很懒,什么都没留下

点赞
< 上一篇
下一篇 >

文章评论

  • 姚大

    大神牛逼 喜欢你的技术教程

    2022-03-23
    登录以回复
  • 姚大

    大神可以的话 建一个github 可以吧示例工程分享给大家

    2022-03-23
    登录以回复
  • 您需要 登录 之后才可以评论

    COPYRIGHT © 2023 白泽图. ALL RIGHTS RESERVED.

    Theme Kratos Made By Seaton Jiang

    登录
    注册|忘记密码?