代码创建动画状态机

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

在做的过程中发现,所有小怪的动画状态机绝大部分的状态是相同的。如果每个小怪的动画状态机都手动创建的话,非常繁琐。正好之前看了一篇介绍代码创建动画状态机的方法--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的时候,会将动画片段也设置好。但考虑到本工程中的动画文件的命名不是很规范,就省略了这一步骤。如果感兴趣的话,也可以扩展这一功能。

原文地址:https://www.cnblogs.com/bzyzhang/p/5721238.html