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. if(clip.name.Equals("chushidongzuo001"))
  8. {
  9. defaultState.motion=clip;
  10. }
  11. else
  12. {
  13. addAnimationClip(clip,actionDetails,machine,haveSub3);
  14. }
  15. }
  16. }

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

参考文章:

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/

unity5之代码创建状态机,玩的666的更多相关文章

  1. Stateless是一个基于C#创建状态机的简单库

    Stateless是一个基于C#创建状态机的简单库 .Net轻量状态机Stateless 很多业务系统开发中,不可避免的会出现状态变化,通常采用的情形可能是使用工作流去完成,但是对于简单场景下,用工作 ...

  2. 【Android】纯代码创建页面布局(含异步加载图片)

    开发环境:macOS 10.12 + Android Studio 2.2,MinSDK Android 5.1 先看看总体效果 本示例是基于Fragment进行的,直接上代码: [界面结构] 在 F ...

  3. ios-将代码创建的视图控件放入拖拽控件的下面

    如图所示 图片是拖拽上去的imageView,橘黄色控件是在代码中创建的添加上去的,此时黄色view在imageView 上方 调用方法bringSubviewToFront:试图将imageView ...

  4. 代码创建数据库_表--SqlServer数据库

    /*1.创建数据库的时候需要设置的基本属性: 数据库名称 逻辑名称 初始大小 文件增长 路径*/ --语法: -- create database 数据库名称 -- on [primary]--创建数 ...

  5. 代码创建storyboard

    代码创建storyboard方式如下 // 加载storyboard UIStoryboard *storyboard = [UIStoryboard StoryboardWithName:@&quo ...

  6. WPF使用后台C#代码创建Grid

    笔者刚刚接触WPF,菜鸟一枚,在做一个练手程序时遇到这样一个需求,创建一个新的Grid并将其添加至一个ListView中,要求Grid及其子元素应按一定顺序给Name属性赋值,直接使用XAML创建的话 ...

  7. ios - 纯代码创建collectionView

    开始考虑好一点点时间,因为一般的都是用xib,或者storyboard来写的.这次用纯代码...废话较多请看 首先把storyboard干掉,工程里面的main干掉 由于干掉了storyboard则启 ...

  8. SharePoint 2013 中代码创建列表查阅项字段

    1.首先,打开VS创建两个List Definition,分别是Address和City,如下图: 2.City列表里修改Title为City Name,其实内部名称还是Title,注意一下: 3.给 ...

  9. swift 字符转为类,代码创建控件

    在使用类之前要先获得 命名空间 通过json来获取 字符型的类名 然后创建类对象,这时候就要用到字符转类 // 从info字典中获取到 命名空间 转为字符型 let NS = NSBundle.mai ...

随机推荐

  1. php 批量删除数据

    php 批量删除数据 :比如我们在看邮箱文件的时候,积攒了一段时间以后,看到有些文件没有用了 这时候我们就会想到把这些 没用的文件删除,这时候就用到了批量删除数据的功能,这里我是用了数据库原有的一个表 ...

  2. EasyDarwin开源流媒体服务器gettimeofday性能优化(3000万/秒次优化至8000万次/秒)

    -本文由EasyDarwin开源团队成员贡献 一.问题描述 Easydarwin中大量使用gettimeofday来获取系统时间,对系统性能造成了一定的影响.我们来做个测试: While(1) { G ...

  3. EasyDarwin手机直播是如何实现的快速显示视频的方法

    前言 经常有人提到最近比较火的映客直播.花椒直播这种,是如何做到在打开手机直播中的某一个主播房间后,立即就能显示出主播视频,非常非常快,而且延时也比较小,是怎么做到的? 其实,这并不是什么高深的技术, ...

  4. 开源流媒体服务器EasyDarwin支持epoll网络模型,大大提升流媒体服务器网络并发性能

    经过春节前后将近2个月的开发和稳定调试.测试,EasyDarwin开源流媒体服务器终于成功将底层select网络模型修改优化成epoll网络模型,将EasyDarwin流媒体服务器在网络处理的效率上提 ...

  5. (非原)SQL注入专题--整理帖 && like 语句拼sql 如何防止注入攻击。

    原地址:blog.csdn.net/lvjin110/article/details/28697695 like 语句拼sql 如何防止注入攻击?http://bbs.csdn.net/topics/ ...

  6. ecshop属性排序

    属性的排序有三个方式:sort_order, attr_price, goods_attr_id如果要修改的话,修改 includes/lib_goods.php文件的 get_goods_prope ...

  7. Xcode使用的一些小技巧,值得一看。

    有时我们需要对一个已有项目进行重构,改进设计,提高代码质量.以下几个Xcode 4中的功能,会使重构的工作变得轻松很多. 1.打开项目我的项目是Xcode3.x中编辑的,在用Xcode 4 打开时出现 ...

  8. zk使用通知移除节点

    前面:https://www.cnblogs.com/toov5/p/9899238.html 服务发生宕机 咋办? 发个事件通知,告知大家哟, 会有通知事件哦 看项目: 服务端: package c ...

  9. PYTHON 爬虫笔记三:Requests库的基本使用

    知识点一:Requests的详解及其基本使用方法 什么是requests库 Requests库是用Python编写的,基于urllib,采用Apache2 Licensed开源协议的HTTP库,相比u ...

  10. VS2010关于调用ffmpeg借口出错

    win7 下开发视频服务器,用到ffmpeg,debug版本运行正常,切换到release时,出现"0x00905a4d 处未处理的异常: 0xC0000005: 读取位置 0x00905a ...