unity5之代码创建状态机,玩的666

http://blog.csdn.net/litaog00/article/details/50483189

最近做项目的时候用到了状态机,网上搜了一下帖子,大部分都是简单介绍使用方法的,讲解的详细的很少。作者

好好研究了一番,感觉很有必要和大家分享一下。技术和灵感,来源于网络,共享于网络~

好多个human模型有好多个动作片段,这些片段又分出若干大类和若干小类,一个主角可能会应用到所有的这些片

段,当满足一定条件时从一类动作中随机取出一个播放,进游戏的时候有的动作条件满足有的不满足随着玩的进度

又可能会满足#@#@#%@!!&*&,,,,略复杂,总之就是有好多动作(200个左右),有的能播,有的不能播

,作者(头硬)一心想用一个状态机来解决所有问题,所以就用代码创建吧,毕竟这么多动作,让策划去摆,费时

费力还容易出错,特别在设置transition的条件时,呵呵,那就代码创建吧,既能体现情怀又能提高自己。


首先网上搜了搜,只有雨松momo的一篇http://www.xuanyusong.com/archives/2811(冒犯引用一下),介绍了大

概思想和用法,但是比较旧,要知道作者用的可是unity3d5.2.2版的,不同及需要注意的地方依次列出:

1.AnimatorController所在的库由UnityEditorInternal变为UnityEditor.Animations


2.AnimatorController里可以创建layer,每个layer有个statemachine(我们通常叫‘某个角色的状态机’好

像不合适,应该叫‘某个角色的AnimatorController’

[csharp] view plain copy
  1. //创建controller  
  2. AnimatorController controller=AnimatorController.CreateAnimatorControllerAtPath("Assets/Resources/CharacterAnim/Play2Controllor_2.controller");  
  3. AnimatorControllerLayer layer=controller.layers[0];  
  4. AnimatorStateMachine machine=layer.stateMachine;  
这样取到了base layer的statemachine


3.创建参数,

[csharp] view plain copy
  1. controller.AddParameter("zhan->pa",AnimatorControllerParameterType.Trigger);  

参数类型有4种:int、float、bool、trigger(作者之前用4.2版的时候还没有trigger,只有int、float

、bool、vector)

有了参数,就有判断条件了,判断条件的类型有

[csharp] view plain copy
  1. AnimatorConditionMode.If;  
  2. AnimatorConditionMode.IfNot;  
  3. AnimatorConditionMode.Equals;  
  4. AnimatorConditionMode.NotEqual;  
  5. AnimatorConditionMode.Greater;  
  6. AnimatorConditionMode.Less;  
其中if既可以来判断bool又可以判断trigger(其实trigger就是触发一下就重置了,可以理解为自动重置的bool)


4.添加内容

添加oneState

[csharp] view plain copy
  1. oneState=machine.AddState(名字,位置)  
添加anyState到oneState的transition
[csharp] view plain copy
  1. AnimatorStateTransition defaultTransition=machine.AddAnyStateTransition(oneState);  


创建二级子状态机
[csharp] view plain copy
  1. AnimatorStateMachine sub2Machine=machine.AddStateMachine(名字,位置)  
作者这里创建了三级子状态机,子状态机跟父状态机一样,都可以添加state、transition。有一点需要注意,
[csharp] view plain copy
  1. AnimatorStateTransition transition1=machine.AddAnyStateTransition(oneState);  
  2. AnimatorStateTransition transition2=sub2Machine.AddAnyStateTransition(twoState);  

transition1和transition2不是从同一个anyState出发的,而是从各自状态机的anyState出发的(子状态

机也有个anyState,但是在controller编辑器里看不出来)

5.雨松momo的帖子里有网友问到:如果fbx里有多个clip,用AssetDatabase.LoadAssetAtPath(xxx.fbx)

如何取到呢?那么答案来了

[csharp] view plain copy
  1. Object[] objects=AssetDatabase.LoadAllAssetsAtPath("Assets/Art/Animation/"+fbxName);  
  2. for(int m=0;m<objects.Length;m++)  
  3. {  
  4. <span style="white-space:pre">    </span>if(objects[m].GetType()==typeof(AnimationClip) && !objects[m].name.Contains("Take 001"))  
  5.     {  
  6.         AnimationClip clip=(AnimationClip)objects[m];  
  7.   
  8.         if(clip.name.Equals("chushidongzuo001"))  
  9.         {  
  10.             defaultState.motion=clip;  
  11.         }  
  12.         else  
  13.         {  
  14.             addAnimationClip(clip,actionDetails,machine,haveSub3);  
  15.         }  
  16.     }  
  17. }  

注意到了吗?AssetDatabase.LoadAllAssetsAtPath可以把fbx里的所有信息取到,clip、骨骼节点、mesh。。。,

然后过滤一下下。而AssetDatabase.LoadAssetAtPath<AnimationClip>("xxx.FBX")只能取到此fbx下第一个clip。



好了,就写到这里,下面把整个代码贴出来,供大家参考~

[csharp] view plain copy
  1. using UnityEngine;  
  2. using System.Collections.Generic;  
  3. using UnityEditor;  
  4. //using UnityEditorInternal;  
  5. using UnityEditor.Animations;  
  6. using System.IO;  
  7.   
  8. public class Play2ControllerGenerator : EditorWindow {  
  9.   
  10.     public enum ActionType:int  
  11.     {  
  12.         undefine=0,  
  13.         zhan,  
  14.         tang,  
  15.         cewo,  
  16.         pa,  
  17.         guodu,  
  18.         kaichang,  
  19.         jieshu,  
  20.         wu  
  21.     }  
  22.   
  23.     public enum ActionStyle:int  
  24.     {  
  25.         normal=0,  
  26.         qingchun,  
  27.         keai,  
  28.         gaoleng,  
  29.         yundong,  
  30.         chengshu,  
  31.         xinggan  
  32.     }  
  33.   
  34.     private static string[] sub2MachineNames=new string[]{"undefine","zhan","tang","cewo","pa","guodu","kaichang","jieshu","wu"};  
  35.     private static string[] sub3MachineNames=new string[]{"normal","qingchun","keai","gaoleng","yundong","chengshu","xinggan"};  
  36.   
  37.     private const string ParamNameTransitionAction="transitionAction";  
  38.     private const string ParamNameActionId="actionId";  
  39.   
  40.     private const float TransitionDuring=0.4f;  
  41.   
  42.     //  
  43.     [MenuItem("LoveLive/核心玩法2/生成状态机")]  
  44.     private static void addWindow()  
  45.     {  
  46.         Rect rect=new Rect(0,0,600,300);  
  47.   
  48.         Play2ControllerGenerator window=EditorWindow.GetWindowWithRect<Play2ControllerGenerator>(rect,true,"生成核心玩法状态机(省得策划自己摆了)");  
  49.         window.minSize=new Vector2(300,100);  
  50.         window.maxSize=new Vector2(600,300);  
  51.         window.Show();  
  52.     }  
  53.   
  54.   
  55.     void OnGUI()  
  56.     {  
  57.         EditorGUILayout.BeginVertical();  
  58.         EditorGUILayout.LabelField("需要"Assets/Resources/Config/action.txt"");  
  59.         EditorGUILayout.LabelField("需要"Assets/Art/Animation"下的动作文件");  
  60.         EditorGUILayout.EndVertical();  
  61.   
  62.         EditorGUILayout.Separator();  
  63.         EditorGUILayout.LabelField("生成的状态机在这个位置:"Assets/Resources/CharacterAnim/Play2Controllor_2.controller"");  
  64.   
  65.         EditorGUILayout.BeginHorizontal();  
  66.         if(GUILayout.Button("点击生成(需要一点时间)"))  
  67.         {  
  68.             generateStateMachine();  
  69.             this.ShowNotification(new GUIContent("创建完毕"));  
  70.         }  
  71.         EditorGUILayout.EndHorizontal();  
  72.     }  
  73.   
  74.     private void generateStateMachine()  
  75.     {  
  76.         //加载action文件  
  77.         Dictionary<string,ActionDetail> actionDetails=parseActionFile("Assets/Resources/Config/action.txt");  
  78.   
  79.         //创建controller  
  80.         AnimatorController controller=AnimatorController.CreateAnimatorControllerAtPath("Assets/Resources/CharacterAnim/Play2Controllor_2.controller");  
  81.         AnimatorControllerLayer layer=controller.layers[0];  
  82.         AnimatorStateMachine machine=layer.stateMachine;  
  83.   
  84.         //创建参数  
  85.         controller.AddParameter(ParamNameTransitionAction,AnimatorControllerParameterType.Trigger);  
  86.         controller.AddParameter(ParamNameActionId,AnimatorControllerParameterType.Int);  
  87.         controller.AddParameter("zhan->pa",AnimatorControllerParameterType.Trigger);  
  88.         controller.AddParameter("pa->cewo",AnimatorControllerParameterType.Trigger);  
  89.         controller.AddParameter("pa->tang",AnimatorControllerParameterType.Trigger);  
  90.         controller.AddParameter("tang->cewo",AnimatorControllerParameterType.Trigger);  
  91.         controller.AddParameter("cewo->tang",AnimatorControllerParameterType.Trigger);  
  92.         controller.AddParameter("default",AnimatorControllerParameterType.Trigger);  
  93.   
  94.         //创建默认state  
  95.         AnimatorState defaultState=machine.AddState("default",new Vector3(300,0,0));  
  96.         //defaultState.motion=  
  97.         machine.defaultState=defaultState;  
  98.         AnimatorStateTransition defaultTransition=machine.AddAnyStateTransition(defaultState);  
  99.         defaultTransition.AddCondition(AnimatorConditionMode.If,0,"default");  
  100.         defaultTransition.duration=TransitionDuring;  
  101.   
  102.         //3级状态机条件  
  103.         List<string> haveSub3=new List<string>();  
  104.         haveSub3.Add("zhan");  
  105.         haveSub3.Add("tang");  
  106.         haveSub3.Add("cewo");  
  107.         haveSub3.Add("pa");  
  108.   
  109.         //创建子状态机  
  110.         for(int k=1;k<sub2MachineNames.Length;k++)  
  111.         {  
  112.             AnimatorStateMachine sub2Machine=machine.AddStateMachine(sub2MachineNames[k],new Vector3(500,k*50,0));  
  113.             if(haveSub3.Contains(sub2MachineNames[k]))  
  114.             {  
  115.                 for(int m=0;m<sub3MachineNames.Length;m++)  
  116.                 {  
  117.                     AnimatorStateMachine sub3Machine=sub2Machine.AddStateMachine(sub3MachineNames[m],new Vector3(500,m*50,0));  
  118.                 }  
  119.             }  
  120.         }  
  121.   
  122.         //添加clip  
  123.         string[] fileNames=Directory.GetFiles(Application.dataPath+"/Art/Animation");  
  124.         for(int k=0;k<fileNames.Length;k++)  
  125.         {  
  126.             if(fileNames[k].EndsWith("fbx") || fileNames[k].EndsWith("FBX"))  
  127.             {  
  128.                 //获取fbx文件名  
  129.                 int index=fileNames[k].LastIndexOf("/");  
  130.                 string fbxName=fileNames[k].Substring(index+1,fileNames[k].Length-index-1);  
  131.   
  132.                 //读取fbx里的动作文件  
  133.                 Object[] objects=AssetDatabase.LoadAllAssetsAtPath("Assets/Art/Animation/"+fbxName);  
  134.                 for(int m=0;m<objects.Length;m++)  
  135.                 {  
  136.                     if(objects[m].GetType()==typeof(AnimationClip) && !objects[m].name.Contains("Take 001"))  
  137.                     {  
  138.                         AnimationClip clip=(AnimationClip)objects[m];  
  139.   
  140.                         if(clip.name.Equals("chushidongzuo001"))  
  141.                         {  
  142.                             defaultState.motion=clip;  
  143.                         }  
  144.                         else  
  145.                         {  
  146.                             addAnimationClip(clip,actionDetails,machine,haveSub3);  
  147.                         }  
  148.                     }  
  149.                 }  
  150.             }  
  151.         }  
  152.   
  153.   
  154.         //AnimationClip clip=AssetDatabase.LoadAssetAtPath<AnimationClip>("Assets/Art/Animation/guodudongzuo-001.FBX");  
  155.   
  156.     }  
  157.   
  158.     private static void addAnimationClip(AnimationClip clip,Dictionary<string,ActionDetail> actionDetails,AnimatorStateMachine machine,List<string> haveSub3)  
  159.     {  
  160.         if(actionDetails.ContainsKey(clip.name))  
  161.         {  
  162.             ActionDetail ad=actionDetails[clip.name];  
  163.   
  164.             foreach(ChildAnimatorStateMachine childMachine in machine.stateMachines)  
  165.             {  
  166.                 AnimatorStateMachine sub2Machine=childMachine.stateMachine;  
  167.   
  168.                 if(sub2MachineNames[ad.type]==sub2Machine.name)  
  169.                 {  
  170.                     //有3级状态机的加3级里,没有的加2级里  
  171.                     if(haveSub3.Contains(sub2Machine.name))  
  172.                     {  
  173.                         foreach(ChildAnimatorStateMachine childMachine3 in sub2Machine.stateMachines)  
  174.                         {  
  175.                             AnimatorStateMachine sub3Machine=childMachine3.stateMachine;  
  176.                             if(sub3MachineNames[ad.style]==sub3Machine.name)  
  177.                             {  
  178.                                 AnimatorState state=sub3Machine.AddState(clip.name,new Vector3(500,sub3Machine.states.Length*50,0));  
  179.                                 state.motion=clip;  
  180.                                 AnimatorStateTransition transition=machine.AddAnyStateTransition(state);  
  181.                                 transition.AddCondition(AnimatorConditionMode.If,0,ParamNameTransitionAction);  
  182.                                 transition.AddCondition(AnimatorConditionMode.Equals,ad.actionId,ParamNameActionId);  
  183.                                 transition.duration=TransitionDuring;  
  184.                             }  
  185.                         }  
  186.                     }  
  187.                     else  
  188.                     {  
  189.                         //过渡很特殊,这里写死  
  190.                         if(ad.type==(int)ActionType.guodu)  
  191.                         {  
  192.                             string stateName="";  
  193.                             float speed=1;  
  194.                             string condition="";  
  195.   
  196.                             int indexZhan=clip.name.IndexOf("zhan");  
  197.                             int indexPa=clip.name.IndexOf("pa");  
  198.                             int indexCewo=clip.name.IndexOf("cewo");  
  199.                             int indexTang=clip.name.IndexOf("tang");  
  200.                             //侧卧和躺之间有正向和反向片段  
  201.                             if(indexCewo>=0 && indexTang>=0)  
  202.                             {  
  203.                                 stateName+="cewo->tang";  
  204.                                 speed=(indexCewo<indexTang)?1:-1;  
  205.                                 condition="cewo->tang";  
  206.                                 if(!string.IsNullOrEmpty(condition))  
  207.                                 {  
  208.                                     AnimatorState state=sub2Machine.AddState(stateName,new Vector3(500,sub2Machine.states.Length*50,0));  
  209.                                     state.motion=clip;  
  210.                                     state.speed=speed;  
  211.                                     AnimatorStateTransition transition=machine.AddAnyStateTransition(state);  
  212.                                     transition.AddCondition(AnimatorConditionMode.If,0,condition);  
  213.                                     transition.duration=TransitionDuring;  
  214.                                 }  
  215.   
  216.   
  217.                                 stateName="guodu-tang->cewo";  
  218.                                 speed=(indexTang<indexCewo)?1:-1;  
  219.                                 condition="tang->cewo";  
  220.                                 if(!string.IsNullOrEmpty(condition))  
  221.                                 {  
  222.                                     AnimatorState state=sub2Machine.AddState(stateName,new Vector3(500,sub2Machine.states.Length*50,0));  
  223.                                     state.motion=clip;  
  224.                                     state.speed=speed;  
  225.                                     AnimatorStateTransition transition=machine.AddAnyStateTransition(state);  
  226.                                     transition.AddCondition(AnimatorConditionMode.If,0,condition);  
  227.                                     transition.duration=TransitionDuring;  
  228.                                 }  
  229.                             }  
  230.                             else  
  231.                             {  
  232.                                 if(indexZhan>=0 && indexPa>=0)  
  233.                                 {  
  234.                                     stateName="guodu-zhan->pa";  
  235.                                     speed=(indexZhan<indexPa)?1:-1;  
  236.                                     condition="zhan->pa";  
  237.                                 }  
  238.                                 else if(indexPa>=0 && indexCewo>=0)  
  239.                                 {  
  240.                                     stateName="guodu-pa->cewo";  
  241.                                     speed=(indexPa<indexCewo)?1:-1;  
  242.                                     condition="pa->cewo";  
  243.                                 }  
  244.                                 else if(indexPa>=0 && indexTang>=0)  
  245.                                 {  
  246.                                     stateName="guodu-pa->tang";  
  247.                                     speed=(indexPa<indexTang)?1:-1;  
  248.                                     condition="pa->tang";  
  249.                                 }  
  250.                                 if(!string.IsNullOrEmpty(condition))  
  251.                                 {  
  252.                                     AnimatorState state=sub2Machine.AddState(stateName,new Vector3(500,sub2Machine.states.Length*50,0));  
  253.                                     state.motion=clip;  
  254.                                     state.speed=speed;  
  255.                                     AnimatorStateTransition transition=machine.AddAnyStateTransition(state);  
  256.                                     transition.AddCondition(AnimatorConditionMode.If,0,condition);  
  257.                                     transition.duration=TransitionDuring;  
  258.                                 }  
  259.                             }  
  260.                         }  
  261.                         else  
  262.                         {  
  263.                             AnimatorState state=sub2Machine.AddState(clip.name,new Vector3(500,sub2Machine.states.Length*50,0));  
  264.                             state.motion=clip;  
  265.                             AnimatorStateTransition transition=machine.AddAnyStateTransition(state);  
  266.                             transition.AddCondition(AnimatorConditionMode.If,0,ParamNameTransitionAction);  
  267.                             transition.AddCondition(AnimatorConditionMode.Equals,ad.actionId,ParamNameActionId);  
  268.                             transition.duration=TransitionDuring;  
  269.                         }  
  270.                     }  
  271.                 }  
  272.             }  
  273.         }  
  274.     }  
  275.   
  276.     //解析动作文件  
  277.     private Dictionary<string,ActionDetail> parseActionFile(string actionFilePath)  
  278.     {  
  279.         Dictionary<string,ActionDetail> actionInfos=new Dictionary<string,ActionDetail>();  
  280.   
  281.         TextAsset ta=AssetDatabase.LoadAssetAtPath<TextAsset>(actionFilePath);  
  282.   
  283.         using(MemoryStream stream = new MemoryStream(ta.bytes))  
  284.         {  
  285.             using(StreamReader reader = new StreamReader(stream))  
  286.             {  
  287.                 //第一行是列名,跳过  
  288.                 int lineIdx = 0;  
  289.                 while (reader.Peek() >= 0)  
  290.                 {  
  291.                     lineIdx++;  
  292.                     string source = reader.ReadLine();  
  293.                     if (lineIdx != 1 && !string.IsNullOrEmpty(source))  
  294.                     {  
  295.                         ActionDetail ad=ActionDetail.create(source);  
  296.                         actionInfos.Add(ad.actionName,ad);  
  297.                     }  
  298.                 }  
  299.             }  
  300.         }  
  301.         Debug.Log("共需要获取"+actionInfos.Count+"个动作");  
  302.         return actionInfos;  
  303.     }  
  304.       
  305. }  
  306.   
  307. public class ActionDetail  
  308. {  
  309.     public int actionId;  
  310.     public string actionName;  
  311.     public int type;  
  312.     public int style;  
  313.   
  314.     public static ActionDetail create(string line)  
  315.     {  
  316.         string[] ss=line.Split(' ');  
  317.   
  318.         ActionDetail ad=new ActionDetail();  
  319.         ad.actionId=int.Parse(ss[0]);  
  320.         ad.actionName=ss[1];  
  321.         ad.type=int.Parse(ss[2]);  
  322.         ad.style=int.Parse(ss[3]);  
  323.   
  324.         return ad;  
  325.     }  
  326. }  

参考文章:

http://www.xuanyusong.com/archives/2811

http://7dot9.com/2015/05/11/unity3d-mecanim%E5%8A%A8%E7%94%BBanimatortransitioninfo%E5%92%8Canimatorstateinfo%E5%9C%A8%E8%A7%92%E8%89%B2%E7%A7%BB%E5%8A%A8%E5%92%8C%E5%BE%85%E6%9C%BA%E5%B9%B3%E6%BB%91%E5%88%87%E6%8D%A2%E4%B8%AD/

原文地址:https://www.cnblogs.com/nafio/p/9137372.html