0.出发点
现在的项目需要设置多套动画组合,全部是由策划在XML文件中设置完成,如果完全的手动在AnimatorController中去做不但工作量大而且如果将来有配置修改了还要一个个去找到对应的自状态机并且修改。因此就萌生了用代码去生成状态机的想法,而且在网上也有了很多的教程可以参考,只是每个项目都不同,且对于一些参数和属性的设置也不尽相同,因此还是把自己的代码进行一些修改后分享出来,基本上应该是包含了状态机常用的功能。

1.数据来源

一个典型的XML文件

<?xml version="1.0" encoding="ISO-8859-1"?>
<config>
    <datas>
        <data INDEX="1" Clip1="jump" Clip1Count="1" Clip2="BackLeap" Clip2Count="2" Clip3="die" Clip3Count="1"></data>
        <data INDEX="2" Clip1="BackLeap" Clip1Count="3" Clip2="jump" Clip2Count="0" Clip3="jump" Clip3Count="0"></data>
        <data INDEX="3" Clip1="BackLeap" Clip1Count="1" Clip2="ForwardLeap" Clip2Count="1" Clip3="jump" Clip3Count="1"></data>
    </datas>
</config>            

2.动画控制器中的主要元素

2.1 Unity中editor的功能十分的强大,能够加载项目中的各种资源,而AnimatorController就是其中之一。

2.2 一个AnimatorController的结构基本如下:

2.3 一些说明:

- AnimatorControllerLayer:一个AnimatorController由多个Layer组成,但是除了BaseLayer外其它的Layer并不主要负责动画逻辑,而是多用于动画遮罩。

- AnimatorControllerParameter:顾名思义是状态机中使用的参数,这个参数可以在不同的Layer和子状态机中使用。在代码添加参数时会选择参数类型,它是个枚举`AnimatorControllerParameterType`。

- AnimatorStateMachine:动画状态机,核心逻辑实线层。在一个状态机中可以有多个state,也可以有多个Sub AnimatorStateMachine。通过AddStateMachine方法来生成并添加子状态机。

- AnimatorState:动画状态,也是这个系统中的基础单元。其可以设定各种属性,比较常用的是AnimationClip和Speed等。

- AnimatorStateTransition:也就是动画转换,其中可以设定触发参数,而且其中还有一个很重要的东西就是动画过度的设定。

3.完整代码

 using UnityEngine;
 using System.Collections;
 using UnityEditor;
 using System;
 using UnityEditor.Animations;
 using System.IO;
 using System.Xml;
 using System.Text.RegularExpressions;
 using System.Xml.Serialization;
 using System.Collections.Generic;
 using System.Linq;

 //[CustomEditor(typeof(EditorTools))]
 public class EditorTools : MonoBehaviour
 {
     #region 创建动画控制器

     /// <summary>
     ///  记录上一个state,用于自状态机中
     /// </summary>
     static AnimatorState lastAnimatorState = null;
     static string ParameterName;
     // 动画片段
     static AnimationClip die;
     static AnimationClip jump;
     static AnimationClip BackLeap;
     static AnimationClip ForwardLeap;
     /// <summary>
     /// base layer AnimatorStateMachine
     /// </summary>
     static AnimatorStateMachine mainASM;
     ;
     ;

     /// <summary>
     /// 根据配置文件创建特技组
     /// </summary>
     [MenuItem ("Tools/CreateAnimatorState")]
     static void CreateAnimatorState ()
     {
         // 获取动画片段
         List<object> allAssets = new List<object> (AssetDatabase.LoadAllAssetsAtPath ("Assets/Charactors/player2.FBX"));
         var animationClips = allAssets.Where (o => o.GetType () == typeof(AnimationClip)).ToList ();
         foreach (var item in animationClips) {
             AnimationClip x = item as AnimationClip;
             switch (x.name) {
             case "die":
                 die = x;
                 break;
             case "jump":
                 jump = x;
                 break;
             case "BackLeap":
                 BackLeap = x;
                 break;
             case "ForwardLeap":
                 ForwardLeap = x;
                 break;
             default:
                 break;
             }
         }

         // 当每个动画是一个单独的FBX文件中时可以用下面的方法来获取
         //die = AssetDatabase.LoadAssetAtPath ("Assets/Charactors/player2.FBX", typeof(AnimationClip)) as AnimationClip;

         // 获取状态机
         AnimatorController animatorController = AssetDatabase.LoadAssetAtPath ("Assets/AnimatorController/demo.controller", typeof(AnimatorController)) as AnimatorController;
         AnimatorControllerLayer layer = animatorController.layers [];
         mainASM = layer.stateMachine;

         // 获取当前所有的参数
         AnimatorControllerParameter[] paras = animatorController.parameters;
         List<AnimatorControllerParameter> listParas = new List<AnimatorControllerParameter> (paras);

         // 删除指定的参数
         var acps = listParas.Where (p => p.name.Contains ("GroupParameter")).ToArray ();
         foreach (AnimatorControllerParameter item in acps) {
             animatorController.RemoveParameter (item);
         }

         // 删除指定的子状态机
         ChildAnimatorStateMachine[] childASM = mainASM.stateMachines;
         List<ChildAnimatorStateMachine> listCASM = new List<ChildAnimatorStateMachine> (childASM);
         var casms = listCASM.Where (c => c.stateMachine.name.Contains ("Group")).ToArray ();
         foreach (ChildAnimatorStateMachine item in casms) {
             mainASM.RemoveStateMachine (item.stateMachine);
         }

         // 读配置文件
         XmlConfig xc = ReadXml ();

         Vector3 startPos = mainASM.anyStatePosition;

         // 根据配置创建自状态机
         ; index < xc.datas.Count; index++) {
             Data data = xc.datas [index];

             // 设置特技参数,
             ParameterName = "GroupParameter" + data.INDEX.ToString ();
             animatorController.AddParameter (ParameterName, AnimatorControllerParameterType.Trigger);

             // 创建子状态机
             AnimatorStateMachine sub = AddSubStateMachine<AnimatorEvent> ());
             // 创建子状态机中的state
             SetStateInSubMachine (sub, data);
             lastAnimatorState = null;
         }
     }

     /// <summary>
     ///  创建sub state machine用于放置特效组中的动画
     /// </summary>
     /// <typeparam name="T"></typeparam>
     /// <param name="stateName"></param>
     /// <param name="sm"></param>
     /// <param name="position"></param>
     /// <param name="data"></param>
     /// <returns></returns>
     private static AnimatorStateMachine AddSubStateMachine<T> (string stateName, string para, AnimatorStateMachine sm, Vector3 position) where T : StateMachineBehaviour
     {
         AnimatorStateMachine sub = sm.AddStateMachine (stateName, position);
         sub.AddStateMachineBehaviour<T> ();
         AnimatorStateTransition transition = mainASM.defaultState.AddTransition (sub, false);
         transition.AddCondition (AnimatorConditionMode.If, , para);
         return sub;
     }

     /// <summary>
     ///  根据配置数据在子状态机中创建state
     /// </summary>
     /// <typeparam name="T"></typeparam>
     /// <param name="subSM"></param>
     /// <param name="data"></param>
     private static void SetStateInSubMachine (AnimatorStateMachine subSM, Data data)
     {
         AnimatorState newState;
         string stateName;
         Vector3 pos;
         List<AnimationClip> acArray = new List<AnimationClip> ();
         SetAnimationClip (data.Clip1, data.Clip1Count, ref acArray);
         SetAnimationClip (data.Clip2, data.Clip2Count, ref acArray);
         SetAnimationClip (data.Clip3, data.Clip3Count, ref acArray);

         ; x <= acArray.Count; x++) {
             stateName = "GroupState_" + data.INDEX + "_" + x.ToString ();
             pos = subSM.entryPosition + );
             newState = AddState (stateName, subSM, pos, acArray [x - ], x, acArray.Count);
             lastAnimatorState = newState;
         }
     }

     static void SetAnimationClip (string clipName, int count, ref List<AnimationClip> acArray)
     {
         ; i < count; i++) {
             if (clipName == die.name) {
                 acArray.Add (die);
             }
             if (clipName == jump.name) {
                 acArray.Add (jump);
             }
             if (clipName == BackLeap.name) {
                 acArray.Add (BackLeap);
             }
             if (clipName == ForwardLeap.name) {
                 acArray.Add (ForwardLeap);
             }
         }
     }

     static AnimatorState AddState<T> (string stateName, AnimatorStateMachine sm, float threshold, string parameter, Vector3 position,
                                       AnimationClip clip, bool first = false, bool last = false) where T : StateMachineBehaviour
     {
         AnimatorStateTransition animatorStateTransition;
         //  生成AnimatorState
         AnimatorState animatorState = sm.AddState (stateName, position);
         // 设置动画片段
         animatorState.motion = clip;
         // 创建AnimatorStateTransition
         // entry连接到特技组的第一个动画
         if (first) {
             animatorStateTransition = sm.AddAnyStateTransition (animatorState);
             animatorStateTransition.AddCondition (AnimatorConditionMode.Equals, threshold, parameter);
         }
         // 最后一个动画连接到stand
         if (last) {
             animatorStateTransition = animatorState.AddTransition (mainASM.defaultState);
         }

         // 特技组内的连接创建
         if (!first && !last) {
             animatorStateTransition = animatorState.AddTransition (mainASM.defaultState);
         }

         animatorStateTransition = lastAnimatorState.AddTransition (animatorState, true);

         //AnimatorStateTransition 的设置
         animatorStateTransition.canTransitionToSelf = false;
         animatorState.AddStateMachineBehaviour<T> ();
         return animatorState;
     }

     static AnimatorState AddState (string stateName, AnimatorStateMachine sm, Vector3 position, AnimationClip clip, int index, int count)
     {
         AnimatorStateTransition animatorStateTransition = null;
         //  生成AnimatorState
         AnimatorState animatorState = sm.AddState (stateName, position);
         // 设置动画片段
         animatorState.motion = clip;
         // 创建AnimatorStateTransition
         // AnyState连接到特技组的第一个动画
         ) {
             //animatorStateTransition = sm.AddAnyStateTransition(animatorState);
             //animatorStateTransition.canTransitionToSelf = false;
         }
         // 最后一个动画连接到main animator machine的default state
         if (index == count) {
             animatorStateTransition = animatorState.AddTransition (mainASM.defaultState);
             animatorStateTransition.hasExitTime = true;
         }

         // 特技组内的连接创建
         if (lastAnimatorState != null) {
             animatorStateTransition = lastAnimatorState.AddTransition (animatorState, true);
         }

         return animatorState;
     }

     #endregion

     #region public method

     static XmlConfig ReadXml ()
     {
         //string xmlStr = File.ReadAllText(Application.dataPath.ToString() + "/StreamingAssets/XMLConfigFiles/Stunt.xml");
         //Debug.Log(xmlStr);
         //string objTxt = Regex.Replace(xmlStr, @"<!--[^-]*-->", string.Empty, RegexOptions.IgnoreCase);
         //Debug.Log(objTxt);
         return DeserializeFromXml<XmlConfig> (Application.dataPath.ToString () + "/StreamingAssets/XMLConfigFiles/data.xml");
     }

     /// <summary>
     /// 从某一XML文件反序列化到某一类型
     /// </summary>
     /// <param name="filePath">待反序列化的XML文件名称</param>
     /// <param name="type">反序列化出的</param>
     /// <returns></returns>
     public static T DeserializeFromXml<T> (string filePath)
     {
         try {
             if (!System.IO.File.Exists (filePath))
                 throw new ArgumentNullException (filePath + " not Exists");

             using (System.IO.StreamReader reader = new System.IO.StreamReader (filePath)) {
                 System.Xml.Serialization.XmlSerializer xs = new System.Xml.Serialization.XmlSerializer (typeof(T));
                 T ret = (T)xs.Deserialize (reader);
                 return ret;
             }
         }
         catch (Exception ex) {
             return default(T);
         }
     }

     #endregion
 }

 #region 序列化需要的model
 [XmlType (TypeName = "config")]
 public class XmlConfig
 {
     [XmlArray ("datas")]
     public List<Data> datas { get; set; }
 }

 [XmlType (TypeName = "data")]
 public class Data
 {
     [XmlAttribute]
     public int INDEX;
     [XmlAttribute]
     public string Clip1;
     [XmlAttribute]
     public int Clip1Count;
     [XmlAttribute]
     public string Clip2;
     [XmlAttribute]
     public int Clip2Count;
     [XmlAttribute]
     public string Clip3;
     [XmlAttribute]
     public int Clip3Count;
 }

 #endregion

4.最后的说明

- 其实整个过程基本就是读取XML文件内容,然后按照第二部分中描述的结构来一点一点构建状态机。

- 在设定具体属性时需要按照具体情况来做。

- 有个天坑,就是如果在Base Layer界面多次点击CreateAnimatorState按钮时会出现Unity的crash,或者出现界面所有元素消失并报错。我找了很多资料应该是UnityEditor的bug。有一个很简单的解决办法,就是创建一个新的Layer,切换到新Layer的界面,然后点击CreateAnimatorState按钮,再切回Base Layer,这样就不会出错了。

代码生成AnimatorController的更多相关文章

  1. JeeSite学习笔记~代码生成原理

    1.建立数据模型[单表,一对多表,树状结构表] 用ERMaster建立数据模型,并设定对应表,建立关联关系 2.系统获取对应表原理 1.怎样获取数据库的表 genTableForm.jsp: < ...

  2. Map工具系列-01-Map代码生成工具说明

    所有cs端工具集成了一个工具面板 -打开(IE) Map工具系列-01-Map代码生成工具说明 Map工具系列-02-数据迁移工具使用说明 Map工具系列-03-代码生成BySQl工具使用说明 Map ...

  3. StartUML反向(逆向)Java工程通过代码生成类图

     在软件工程中,通过都是先了详细设计,然后按照详细设计来进行开发.在编写详细设计的时候,通常都会画一些类图.时序图.流程图等等UML设计,然后通过uml类图生成代码,这个属于正向工程生成代码,然而在实 ...

  4. YbSoftwareFactory 代码生成插件【二十五】:Razor视图中以全局方式调用后台方法输出页面代码的三种方法

    上一篇介绍了 MVC中实现动态自定义路由 的实现,本篇将介绍Razor视图中以全局方式调用后台方法输出页面代码的三种方法. 框架最新的升级实现了一个页面部件功能,其实就是通过后台方法查询数据库内容,把 ...

  5. YbSoftwareFactory 代码生成插件【十四】:通过 DynamicLinq 简单实现 N-Tier 部署下的服务端数据库通用分页

    YbSoftwareFactory 的 YbRapidSolution for WinForm 插件使用CSLA.NET作为业务层,CSLA.NET的一个强大的特性是支持 N-Tiers 部署.只需非 ...

  6. 与VS集成的若干种代码生成解决方案[博文汇总(共8篇)]

    http://www.cnblogs.com/artech/archive/2010/11/17/CodeGeneration.html [第1篇] 通过CodeDOM定义生成代码的结构 我 不知道大 ...

  7. mybatis实战教程(mybatis in action)之九:mybatis 代码生成工具的使用

    mybatis 应用程序,需要大量的配置文件,对于一个成百上千的数据库表来说,完全手工配置,这是一个很恐怖的工作量. 所以mybatis 官方也推出了一个mybatis代码生成工具的jar包. 今天花 ...

  8. C语言乱谈(一) 20行代码生成BMP

    在学习图形图像的过程中,最简单和常见的格式是BMP和PPM.下面将给出生成BMP的极度精简代码,然后讲解BMP格式. #include <stdio.h> #include <std ...

  9. 第二章 Mybatis代码生成工具

    1.mybatis-generator作用 1).生成pojo 与 数据库结构对应 2).如果有主键,能匹配主键 3).如果没有主键,可以用其他字段去匹配 4).动态select,update,del ...

随机推荐

  1. C#创建、安装、卸载、调试Windows Service(Windows 服务)的简单教程

    前言:Microsoft Windows 服务能够创建在它们自己的 Windows 会话中可长时间运行的可执行应用程序.这些服务可以在计算机启动时自动启动,可以暂停和重新启动而且不显示任何用户界面.这 ...

  2. [Intel Edison开发板] 05、Edison开发基于MRAA实现IO控制,特别是UART通信

    一.前言 下面是本系列文章的前几篇: [Intel Edison开发板] 01.Edison开发板性能简述 [Intel Edison开发板] 02.Edison开发板入门 [Intel Edison ...

  3. 畅通工程续——E

    E. 畅通工程续 某省自从实行了很多年的畅通工程计划后,终于修建了很多路.不过路多了也不好,每次要从一个城镇到另一个城镇时,都有许多种道路方案可以选择,而某些方案要比另一些方案行走的距离要短很多.这让 ...

  4. memset 的实现分析

    memset 是 msvcrt 中的一个函数,其作用和用途是显而易见的,通常是对一段内存进行填充,就其作用本身不具有任何歧义性.但就有人一定要纠结对数组的初始化一定要写成如下形式: int a[... ...

  5. Javascript-jQuery【1】-用promise()实现html()回调函数

    $('#divId').html(someText).promise().done(function(){ //your callback logic / code here });

  6. 为OpenResty增加nginx_upload_module模块

    1.下载 http://www.grid.net.ru/nginx/download/nginx_upload_module-2.2.0.tar.gz 或 http://pan.baidu.com/s ...

  7. Tcpdump使用常用9实例

    以下将给出9个使用tcpdump的例子,以说明tcpdump的具体使用方法. 1.针对特定网口抓包(-i选项) 当我们不加任何选项执行tcpdump时,tcpdump将抓取通过所有网口的包:使用-i选 ...

  8. zookeeper 笔记

    http://www.biaodianfu.com/zookeeper.html ======= ---- zookeeper这种数据结构有如下这些特点: 1,每个子目录如NameService都被作 ...

  9. am,pm时间转换

    写在前面 最近遇到的一个问题,在英文操作系统上,获取到的时间是带am或者pm的.但是数据库是datetime类型,存储的时候竟然都变成0000-00-00 00:00:00.但是在中文操作系统上又是正 ...

  10. http://jingyan.baidu.com/album/d8072ac47baf0eec95cefdca.html?picindex=4

    http://jingyan.baidu.com/album/d8072ac47baf0eec95cefdca.html?picindex=4