zoukankan      html  css  js  c++  java
  • 代码生成AnimatorController

    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.完整代码

      1 using UnityEngine;
      2 using System.Collections;
      3 using UnityEditor;
      4 using System;
      5 using UnityEditor.Animations;
      6 using System.IO;
      7 using System.Xml;
      8 using System.Text.RegularExpressions;
      9 using System.Xml.Serialization;
     10 using System.Collections.Generic;
     11 using System.Linq;
     12 
     13 //[CustomEditor(typeof(EditorTools))]
     14 public class EditorTools : MonoBehaviour
     15 {
     16     #region 创建动画控制器
     17 
     18     /// <summary>
     19     ///  记录上一个state,用于自状态机中
     20     /// </summary>
     21     static AnimatorState lastAnimatorState = null;
     22     static string ParameterName;
     23     // 动画片段
     24     static AnimationClip die;
     25     static AnimationClip jump;
     26     static AnimationClip BackLeap;
     27     static AnimationClip ForwardLeap;
     28     /// <summary>
     29     /// base layer AnimatorStateMachine
     30     /// </summary>
     31     static AnimatorStateMachine mainASM;
     32     static int stateHeight = 100;
     33     static int stateWidth = 220;
     34 
     35     /// <summary>
     36     /// 根据配置文件创建特技组
     37     /// </summary>
     38     [MenuItem ("Tools/CreateAnimatorState")]
     39     static void CreateAnimatorState ()
     40     {
     41         // 获取动画片段
     42         List<object> allAssets = new List<object> (AssetDatabase.LoadAllAssetsAtPath ("Assets/Charactors/player2.FBX"));
     43         var animationClips = allAssets.Where (o => o.GetType () == typeof(AnimationClip)).ToList ();
     44         foreach (var item in animationClips) {
     45             AnimationClip x = item as AnimationClip;
     46             switch (x.name) {
     47             case "die":
     48                 die = x;
     49                 break;
     50             case "jump":
     51                 jump = x;
     52                 break;
     53             case "BackLeap":
     54                 BackLeap = x;
     55                 break;
     56             case "ForwardLeap":
     57                 ForwardLeap = x;
     58                 break;
     59             default:
     60                 break;
     61             }
     62         }
     63 
     64         // 当每个动画是一个单独的FBX文件中时可以用下面的方法来获取
     65         //die = AssetDatabase.LoadAssetAtPath ("Assets/Charactors/player2.FBX", typeof(AnimationClip)) as AnimationClip;
     66 
     67 
     68         // 获取状态机
     69         AnimatorController animatorController = AssetDatabase.LoadAssetAtPath ("Assets/AnimatorController/demo.controller", typeof(AnimatorController)) as AnimatorController;
     70         AnimatorControllerLayer layer = animatorController.layers [0];
     71         mainASM = layer.stateMachine;
     72 
     73         // 获取当前所有的参数
     74         AnimatorControllerParameter[] paras = animatorController.parameters;
     75         List<AnimatorControllerParameter> listParas = new List<AnimatorControllerParameter> (paras);
     76 
     77         // 删除指定的参数
     78         var acps = listParas.Where (p => p.name.Contains ("GroupParameter")).ToArray ();
     79         foreach (AnimatorControllerParameter item in acps) {
     80             animatorController.RemoveParameter (item);
     81         }
     82 
     83         // 删除指定的子状态机
     84         ChildAnimatorStateMachine[] childASM = mainASM.stateMachines;
     85         List<ChildAnimatorStateMachine> listCASM = new List<ChildAnimatorStateMachine> (childASM);
     86         var casms = listCASM.Where (c => c.stateMachine.name.Contains ("Group")).ToArray ();
     87         foreach (ChildAnimatorStateMachine item in casms) {
     88             mainASM.RemoveStateMachine (item.stateMachine);
     89         }
     90 
     91         // 读配置文件
     92         XmlConfig xc = ReadXml ();
     93 
     94         Vector3 startPos = mainASM.anyStatePosition;
     95 
     96 
     97         // 根据配置创建自状态机
     98         for (int index = 0; index < xc.datas.Count; index++) {
     99             Data data = xc.datas [index];
    100 
    101             // 设置特技参数,
    102             ParameterName = "GroupParameter" + data.INDEX.ToString ();
    103             animatorController.AddParameter (ParameterName, AnimatorControllerParameterType.Trigger);
    104 
    105             // 创建子状态机
    106             AnimatorStateMachine sub = AddSubStateMachine<AnimatorEvent> ("Group_" + data.INDEX, ParameterName, mainASM, startPos + new Vector3 (stateWidth * index, -stateHeight, 0));
    107             // 创建子状态机中的state
    108             SetStateInSubMachine (sub, data);
    109             lastAnimatorState = null;
    110         }
    111     }
    112 
    113     /// <summary>
    114     ///  创建sub state machine用于放置特效组中的动画
    115     /// </summary>
    116     /// <typeparam name="T"></typeparam>
    117     /// <param name="stateName"></param>
    118     /// <param name="sm"></param>
    119     /// <param name="position"></param>
    120     /// <param name="data"></param>
    121     /// <returns></returns>
    122     private static AnimatorStateMachine AddSubStateMachine<T> (string stateName, string para, AnimatorStateMachine sm, Vector3 position) where T : StateMachineBehaviour
    123     {
    124         AnimatorStateMachine sub = sm.AddStateMachine (stateName, position);
    125         sub.AddStateMachineBehaviour<T> ();
    126         AnimatorStateTransition transition = mainASM.defaultState.AddTransition (sub, false);
    127         transition.AddCondition (AnimatorConditionMode.If, 0, para);
    128         return sub;
    129     }
    130 
    131     /// <summary>
    132     ///  根据配置数据在子状态机中创建state
    133     /// </summary>
    134     /// <typeparam name="T"></typeparam>
    135     /// <param name="subSM"></param>
    136     /// <param name="data"></param>
    137     private static void SetStateInSubMachine (AnimatorStateMachine subSM, Data data)
    138     {
    139         AnimatorState newState;
    140         string stateName;
    141         Vector3 pos;
    142         List<AnimationClip> acArray = new List<AnimationClip> ();
    143         SetAnimationClip (data.Clip1, data.Clip1Count, ref acArray);
    144         SetAnimationClip (data.Clip2, data.Clip2Count, ref acArray);
    145         SetAnimationClip (data.Clip3, data.Clip3Count, ref acArray);
    146 
    147         for (int x = 1; x <= acArray.Count; x++) {
    148             stateName = "GroupState_" + data.INDEX + "_" + x.ToString ();
    149             pos = subSM.entryPosition + new Vector3 (stateWidth, -stateHeight * x, 0);
    150             newState = AddState (stateName, subSM, pos, acArray [x - 1], x, acArray.Count);
    151             lastAnimatorState = newState;
    152         }
    153     }
    154 
    155     static void SetAnimationClip (string clipName, int count, ref List<AnimationClip> acArray)
    156     {
    157         for (int i = 0; i < count; i++) {
    158             if (clipName == die.name) {
    159                 acArray.Add (die);
    160             }
    161             if (clipName == jump.name) {
    162                 acArray.Add (jump);
    163             }
    164             if (clipName == BackLeap.name) {
    165                 acArray.Add (BackLeap);
    166             }
    167             if (clipName == ForwardLeap.name) {
    168                 acArray.Add (ForwardLeap);
    169             }
    170         }
    171     }
    172 
    173     static AnimatorState AddState<T> (string stateName, AnimatorStateMachine sm, float threshold, string parameter, Vector3 position,
    174                                       AnimationClip clip, bool first = false, bool last = false) where T : StateMachineBehaviour
    175     {
    176         AnimatorStateTransition animatorStateTransition;
    177         //  生成AnimatorState
    178         AnimatorState animatorState = sm.AddState (stateName, position);
    179         // 设置动画片段
    180         animatorState.motion = clip;
    181         // 创建AnimatorStateTransition
    182         // entry连接到特技组的第一个动画
    183         if (first) {
    184             animatorStateTransition = sm.AddAnyStateTransition (animatorState);
    185             animatorStateTransition.AddCondition (AnimatorConditionMode.Equals, threshold, parameter);
    186         }
    187         // 最后一个动画连接到stand
    188         if (last) {
    189             animatorStateTransition = animatorState.AddTransition (mainASM.defaultState);
    190         }
    191 
    192         // 特技组内的连接创建
    193         if (!first && !last) {
    194             animatorStateTransition = animatorState.AddTransition (mainASM.defaultState);
    195         }
    196 
    197         animatorStateTransition = lastAnimatorState.AddTransition (animatorState, true);
    198 
    199         //AnimatorStateTransition 的设置
    200         animatorStateTransition.canTransitionToSelf = false;
    201         animatorState.AddStateMachineBehaviour<T> ();
    202         return animatorState;
    203     }
    204 
    205     static AnimatorState AddState (string stateName, AnimatorStateMachine sm, Vector3 position, AnimationClip clip, int index, int count)
    206     {
    207         AnimatorStateTransition animatorStateTransition = null;
    208         //  生成AnimatorState
    209         AnimatorState animatorState = sm.AddState (stateName, position);
    210         // 设置动画片段
    211         animatorState.motion = clip;
    212         // 创建AnimatorStateTransition
    213         // AnyState连接到特技组的第一个动画
    214         if (index == 1) {
    215             //animatorStateTransition = sm.AddAnyStateTransition(animatorState);
    216             //animatorStateTransition.canTransitionToSelf = false;
    217         }
    218         // 最后一个动画连接到main animator machine的default state
    219         if (index == count) {
    220             animatorStateTransition = animatorState.AddTransition (mainASM.defaultState);
    221             animatorStateTransition.hasExitTime = true;
    222         }
    223 
    224         // 特技组内的连接创建
    225         if (lastAnimatorState != null) {
    226             animatorStateTransition = lastAnimatorState.AddTransition (animatorState, true);
    227         }
    228 
    229         return animatorState;
    230     }
    231 
    232     #endregion
    233 
    234     #region public method
    235 
    236     static XmlConfig ReadXml ()
    237     {
    238         //string xmlStr = File.ReadAllText(Application.dataPath.ToString() + "/StreamingAssets/XMLConfigFiles/Stunt.xml");
    239         //Debug.Log(xmlStr);
    240         //string objTxt = Regex.Replace(xmlStr, @"<!--[^-]*-->", string.Empty, RegexOptions.IgnoreCase);
    241         //Debug.Log(objTxt);
    242         return DeserializeFromXml<XmlConfig> (Application.dataPath.ToString () + "/StreamingAssets/XMLConfigFiles/data.xml");
    243     }
    244 
    245     /// <summary>
    246     /// 从某一XML文件反序列化到某一类型
    247     /// </summary>
    248     /// <param name="filePath">待反序列化的XML文件名称</param>
    249     /// <param name="type">反序列化出的</param>
    250     /// <returns></returns>
    251     public static T DeserializeFromXml<T> (string filePath)
    252     {
    253         try {
    254             if (!System.IO.File.Exists (filePath))
    255                 throw new ArgumentNullException (filePath + " not Exists");
    256 
    257             using (System.IO.StreamReader reader = new System.IO.StreamReader (filePath)) {
    258                 System.Xml.Serialization.XmlSerializer xs = new System.Xml.Serialization.XmlSerializer (typeof(T));
    259                 T ret = (T)xs.Deserialize (reader);
    260                 return ret;
    261             }
    262         }
    263         catch (Exception ex) {
    264             return default(T);
    265         }
    266     }
    267 
    268     #endregion
    269 }
    270 
    271 
    272 #region 序列化需要的model
    273 [XmlType (TypeName = "config")]
    274 public class XmlConfig
    275 {
    276     [XmlArray ("datas")]
    277     public List<Data> datas { get; set; }
    278 }
    279 
    280 [XmlType (TypeName = "data")]
    281 public class Data
    282 {
    283     [XmlAttribute]
    284     public int INDEX;
    285     [XmlAttribute]
    286     public string Clip1;
    287     [XmlAttribute]
    288     public int Clip1Count;
    289     [XmlAttribute]
    290     public string Clip2;
    291     [XmlAttribute]
    292     public int Clip2Count;
    293     [XmlAttribute]
    294     public string Clip3;
    295     [XmlAttribute]
    296     public int Clip3Count;
    297 }
    298 
    299 #endregion

    4.最后的说明

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

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

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

  • 相关阅读:
    运营平台——效率型后台管理类产品交互设计心得
    代码在线编译器(下)- 用户代码安全检测
    代码在线编译器(上)- 编辑及编译
    SimpleDateFormat并发隐患及其解决
    Spring-SpringMVC父子容器&AOP使用总结
    PaaS服务之路漫谈(三)
    PaaS服务之路漫谈(二)
    PaaS服务之路漫谈(一)
    Spring 属性配置
    如何通过临时表来加快数据库查询的性能
  • 原文地址:https://www.cnblogs.com/klkucan/p/6057237.html
Copyright © 2011-2022 走看看