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;
static int stateHeight = ;
static int stateWidth = ; /// <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; // 根据配置创建自状态机
for (int index = ; index < xc.datas.Count; index++) {
Data data = xc.datas [index]; // 设置特技参数,
ParameterName = "GroupParameter" + data.INDEX.ToString ();
animatorController.AddParameter (ParameterName, AnimatorControllerParameterType.Trigger); // 创建子状态机
AnimatorStateMachine sub = AddSubStateMachine<AnimatorEvent> ("Group_" + data.INDEX, ParameterName, mainASM, startPos + new Vector3 (stateWidth * index, -stateHeight, ));
// 创建子状态机中的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); for (int x = ; x <= acArray.Count; x++) {
stateName = "GroupState_" + data.INDEX + "_" + x.ToString ();
pos = subSM.entryPosition + new Vector3 (stateWidth, -stateHeight * x, );
newState = AddState (stateName, subSM, pos, acArray [x - ], x, acArray.Count);
lastAnimatorState = newState;
}
} static void SetAnimationClip (string clipName, int count, ref List<AnimationClip> acArray)
{
for (int i = ; 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连接到特技组的第一个动画
if (index == ) {
//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. 关于Unity中Mecanim动画的动画状态代码控制与代码生成动画控制器

    对于多量的.复杂的.有规律的控制器使用代码生成 动画状态代码控制 1:每个动画状态,比如进入状态,离开状态, 等都有可能需要代码来参与和处理,比如,进入这个动画单元后做哪些事情,来开这个动画单元后做哪 ...

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

随机推荐

  1. Git很好的教程

    本文地址:http://www.cnblogs.com/yhLinux/p/4067064.html 很好的Git教程,作为初学者,跟着作者的教程走了一遍之后,基本熟悉了Git的常用操作,此教程简洁明 ...

  2. a questions

    1.2520 is the smallest nuber that can be diveded by each of the number from 1 to 10 without any rema ...

  3. Codeforces Zip-line 650D 345Div1D(LIS)

    传送门 大意:给出一个序列,求修改一个数过后的最长上升子序列. 思路:可以用主席树在线搞,也可以用树状数组离线搞,明显后者好写得多.我们首先读取所有的询问,然后就把询问绑在给出的位置,然后我们正向做一 ...

  4. 父窗口window.showModalDialog传值 子窗口window.returnValue返回值

    父窗口打开子窗口页面: var fatherWindow = document.all.dealReason;//想传的值 win = window.showModalDialog(strUrl, f ...

  5. C#学习感悟

    上周虽然没上课,课上的内容是部分同学展示大作业成果,但是对于我来说,看了一些同学辛勤劳动的成果,听了他们对C#学习的一些感悟,我受益匪浅. 在这里我想谈谈我的收获.老师给的模板是todolist,但是 ...

  6. STL练习题续

    //zjnu 1399 //sort 数组可用//vector sort(vector) #include<iostream> #include<algorithm> usin ...

  7. TFT-LCD的相关概念

    显示尺寸(display size) 是指实际可视区域的对角线长度,单位是英寸,简称寸(1英寸=2.54厘米). 长宽比(aspect ratio) 是指TFT-LCD可视区域的长度和宽度之比,也叫做 ...

  8. linux安装oracle 11g rac

    安装oracle 11gR2 RAC 一.网络规划及安装虚拟主机 主机名 主机版本 Ip rac1.localdomain Redhat 6.5 RAC节点1 192.168.100.11 rac2. ...

  9. hadoop 笔记(hbase)

    hbase 基础: hbase是基于列的数据,其数据模式如下: 1.安装 1.1)hbase安装分为单机.伪分布式.分布式,单机下安装不依赖于hadoop:因为不需要分布式文件系统支持: 1.2)安装 ...

  10. UWP中的Direct2D

    介绍 DirectX一直是Windows平台中高性能图形的代名词,自Win7开始,微软又推出了Direct2D技术,包装于Direct3D,但专注于2D图形,并且准备取代GDI这样的传统2D图形技术. ...