zoukankan      html  css  js  c++  java
  • 代码创建动画状态机

    最近开始了新的项目,主要负责小怪部分的功能实现。

    在做的过程中发现,所有小怪的动画状态机绝大部分的状态是相同的。如果每个小怪的动画状态机都手动创建的话,非常繁琐。正好之前看了一篇介绍代码创建动画状态机的方法--unity5.x代码创建AnimatorController状态机,为了偷懒,学习一小代码创建AnimatorController的方法。

    1、创建AnimatorController

            AnimatorController animatorController = AnimatorController.CreateAnimatorControllerAtPath("Assets/Test.controller");
    

      这里主要的方法就是public static AnimatorController CreateAnimatorControllerAtPath(string path);传递给它一个string类型的参数即可,path即是创建的AnimatorController 的保存位置。

    2、获取AnimatorController的层layer和AnimatorStateMachine

            AnimatorControllerLayer layer = animatorController.layers[0];
            AnimatorStateMachine animatorStateMachine = layer.stateMachine;
    

      第一步是获取AnimatorController的层,即当前层;第二步是获取当前层的AnimatorStateMachine,获取的AnimatorStateMachine在后面有用处。

    3、设置一下AnyState和Entry的坐标位置(为了美观起见。。。)

            animatorStateMachine.anyStatePosition = new Vector3(0, 0, 0);
            animatorStateMachine.entryPosition = new Vector3(0, 0, 0);
    

      这里要说的是,经过测试发现,Z坐标没有影响,主要有影响的是X,Y坐标。规律是:X从左往右增大;Y从上往下增大。具体的变化幅度需要自己试验体会。

    4、添加动画Parameters

        private static void AddParamter(AnimatorController controller, string name, AnimatorControllerParameterType type)
        {
            controller.AddParameter(name, type);
        }
    

      这里主要想说的是public void AddParameter(string name, AnimatorControllerParameterType type);方法。第一个参数是Parameters的名字,第二个参数是Parameters的类型,类型主要有Float、Int、Bool、Trigger。跟面板上面的一一对应。

    5、创建AnimatorState

            AnimatorState back = AddState<BackState>("Back", animatorStateMachine, 3, new Vector3(0, -200, 0));
            back.speed = -1;
    

      

        private static AnimatorState AddState<T>(string stateName, AnimatorStateMachine sm, float threshold, Vector3 position) where T : StateMachineBehaviour
        {
            AnimatorState animatorState = sm.AddState(stateName, position);
            AnimatorStateTransition animatorStateTransition = sm.AddAnyStateTransition(animatorState);
            animatorStateTransition.canTransitionToSelf = false;
            animatorStateTransition.AddCondition(AnimatorConditionMode.Equals, threshold, "StateI");
            animatorState.AddStateMachineBehaviour<T>();
            return animatorState;
        }
    

      首先看AddState方法,它是一个泛型方法。这里需要说明一下,本工程是使用基于UNITY的Animator自带的动画状态机实现的。T是继承自StateMachineBehaviour的类,可以直接添加到动画状态机上面。如果不需要使用动画状态机的话,可以不使用泛型方法。

          第一行:

    AnimatorState animatorState = sm.AddState(stateName, position);
    

      参数是AnimatorState的名称和坐标位置,其中,坐标位置和上面第3点的坐标系是相同的。

    第二行:

    AnimatorStateTransition animatorStateTransition = sm.AddAnyStateTransition(animatorState);
    

      添加一个AnyState到本状态的过渡Transition。

    第三行:

    animatorStateTransition.canTransitionToSelf = false;

      设置过渡的canTransitionToSelf 为false。

    第四行:

     animatorStateTransition.AddCondition(AnimatorConditionMode.Equals, threshold, "StateI");
    

      主要的方法是public void AddCondition(AnimatorConditionMode mode, float threshold, string parameter);方法,有三个参数,第一个是AnimatorConditionMode类型的枚举参数,可能的值有If、IfNot、Greater、Less、Equals、NotEqual,与面板上面设置时一致;第二个参数是threshold,即阈值;最后一个参数是parameter,即Animator的Parameters参数名。这里需要说明的是,我们在面板上面创建Animator的时候,会遇到一个过渡有多个Conditions的情况,在代码中解决这种问题的方法,即是多次调用public void AddCondition(AnimatorConditionMode mode, float threshold, string parameter);方法创建Conditions。

    第五行:

    animatorState.AddStateMachineBehaviour<T>();
    

      主要是为了使用Animator的自带状态机。为创建的AnimatorState添加脚本。

    这样,就创建了一个Back状态。这里遇到一个问题,在面板上面创建Back状态的话,我需要倒着播放动画,直接将Back状态的speed设为-1即可,而使用代码创建的话,要达到相同的目标,可使用如下方法:

    back.speed = -1;
    

      如下图所示,即是Back的Inspector面板属性:

    需要设置某些属性的话,可以使用类似设置speed的方法设置。

    6、创建Sub_state Machine

            AnimatorStateMachine attack = AddStateMachine<AttackState>("Attack", animatorStateMachine, new Vector3(0, -150, 0));
            AddSubState(attack, animatorStateMachine, "Attack", 3, 8);

      

        private static AnimatorStateMachine AddStateMachine<T>(string stateName, AnimatorStateMachine sm, Vector3 position) where T : StateMachineBehaviour
        {
            AnimatorStateMachine sub = sm.AddStateMachine(stateName, position);
            sub.AddStateMachineBehaviour<T>();
            return sub;
        }
    

      

        private static void AddSubState(AnimatorStateMachine stateMachine, AnimatorStateMachine sm, string nameStr, int length, float threshold)
        {
            Vector3[] positions = new Vector3[length];
            int startY = -100;
            for (int j = 0; j < length; ++j)
            {
                positions[j] = new Vector3(300, startY, 0);
                startY += 70;
            }
            for (int i = 0; i < length; ++i)
            {
                AnimatorState animatorState = stateMachine.AddState(nameStr + i.ToString(), positions[i]);
                AnimatorStateTransition animatorStateTransition = sm.AddAnyStateTransition(animatorState);
                animatorStateTransition.canTransitionToSelf = false;
                animatorStateTransition.AddCondition(AnimatorConditionMode.Equals, threshold, "StateI");
                animatorStateTransition.AddCondition(AnimatorConditionMode.Equals, i, nameStr + "I");
            }
        }
    

      AddStateMachine()方法与AddState()方法基本相同,这里不再赘述。这里有一点需要注意一下,AddSubState()方法中,创建AnimatorState使用的是Sub_state Machine,而创建过渡条件,使用的则是2中获取到的AnimatorStateMachine。可以试验一下,使用Sub_state Machine设置过渡条件是什么样的问题。

    7、创建BlendTree

            BlendTree blendTree = null;
            animatorController.CreateBlendTreeInController("Move", out blendTree);
    

      这一步还没有深入研究,先放在这里。

    最后,完整的工程代码如下所示:

    using UnityEngine;
    using System.Collections;
    using UnityEditor;
    using UnityEditor.Animations;
    
    public class CreateAnimator : Editor
    {
        [MenuItem("Tool/CreateController")]
        static void DoCreateAnimationAssets()
        {
            //创建Controller
            AnimatorController animatorController = AnimatorController.CreateAnimatorControllerAtPath("Assets/Test.controller");
            //得到它的Layer
            AnimatorControllerLayer layer = animatorController.layers[0];
            AnimatorStateMachine animatorStateMachine = layer.stateMachine;
            animatorStateMachine.anyStatePosition = new Vector3(0, 0, 0);
            animatorStateMachine.entryPosition = new Vector3(0, 0, 0);
            AddParamter(animatorController, "Forward", AnimatorControllerParameterType.Float);
            AddParamter(animatorController, "StateI", AnimatorControllerParameterType.Int);
            AddParamter(animatorController, "MoveSpeed", AnimatorControllerParameterType.Float);
            AddParamter(animatorController, "AttackI", AnimatorControllerParameterType.Int);
            //将动画保存到 AnimatorController中
            AnimatorState back = AddState<BackState>("Back", animatorStateMachine, 3, new Vector3(0, -200, 0));
            back.speed = -1;
            AnimatorStateMachine attack = AddStateMachine<AttackState>("Attack", animatorStateMachine, new Vector3(0, -150, 0));
            AddSubState(attack, animatorStateMachine, "Attack", 3, 8);
            BlendTree blendTree = null;
            animatorController.CreateBlendTreeInController("Move", out blendTree);
        }
    
        private static AnimatorState AddState<T>(string stateName, AnimatorStateMachine sm, float threshold, Vector3 position) where T : StateMachineBehaviour
        {
            AnimatorState animatorState = sm.AddState(stateName, position);
            AnimatorStateTransition animatorStateTransition = sm.AddAnyStateTransition(animatorState);
            animatorStateTransition.canTransitionToSelf = false;
            animatorStateTransition.AddCondition(AnimatorConditionMode.Equals, threshold, "StateI");
            animatorState.AddStateMachineBehaviour<T>();
            return animatorState;
        }
    
        private static AnimatorStateMachine AddStateMachine<T>(string stateName, AnimatorStateMachine sm, Vector3 position) where T : StateMachineBehaviour
        {
            AnimatorStateMachine sub = sm.AddStateMachine(stateName, position);
            sub.AddStateMachineBehaviour<T>();
            return sub;
        }
    
        private static void AddSubState(AnimatorStateMachine stateMachine, AnimatorStateMachine sm, string nameStr, int length, float threshold)
        {
            Vector3[] positions = new Vector3[length];
            int startY = -100;
            for (int j = 0; j < length; ++j)
            {
                positions[j] = new Vector3(300, startY, 0);
                startY += 70;
            }
            for (int i = 0; i < length; ++i)
            {
                AnimatorState animatorState = stateMachine.AddState(nameStr + i.ToString(), positions[i]);
                AnimatorStateTransition animatorStateTransition = sm.AddAnyStateTransition(animatorState);
                animatorStateTransition.canTransitionToSelf = false;
                animatorStateTransition.AddCondition(AnimatorConditionMode.Equals, threshold, "StateI");
                animatorStateTransition.AddCondition(AnimatorConditionMode.Equals, i, nameStr + "I");
            }
        }
    
        private static void AddParamter(AnimatorController controller, string name, AnimatorControllerParameterType type)
        {
            controller.AddParameter(name, type);
        }
    }
    

      

    最后有一点需要说明的是,本文开头的参考博客里面在创建AnimatorController的时候,会将动画片段也设置好。但考虑到本工程中的动画文件的命名不是很规范,就省略了这一步骤。如果感兴趣的话,也可以扩展这一功能。

  • 相关阅读:
    mysql 索引
    redis持久化
    redis发布订阅
    django 信号
    paramiko模块
    23种设计模式python实现
    几种浏览器存储数据的方式
    关于传参
    对字符串里的四则运算进行计算2020/10/12
    动手动脑2020/10/9
  • 原文地址:https://www.cnblogs.com/bzyzhang/p/5721238.html
Copyright © 2011-2022 走看看