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’)
- //创建controller
- AnimatorController controller=AnimatorController.CreateAnimatorControllerAtPath("Assets/Resources/CharacterAnim/Play2Controllor_2.controller");
- AnimatorControllerLayer layer=controller.layers[0];
- AnimatorStateMachine machine=layer.stateMachine;
这样取到了base layer的statemachine
3.创建参数,
- controller.AddParameter("zhan->pa",AnimatorControllerParameterType.Trigger);
参数类型有4种:int、float、bool、trigger(作者之前用4.2版的时候还没有trigger,只有int、float
、bool、vector)
有了参数,就有判断条件了,判断条件的类型有
- AnimatorConditionMode.If;
- AnimatorConditionMode.IfNot;
- AnimatorConditionMode.Equals;
- AnimatorConditionMode.NotEqual;
- AnimatorConditionMode.Greater;
- AnimatorConditionMode.Less;
其中if既可以来判断bool又可以判断trigger(其实trigger就是触发一下就重置了,可以理解为自动重置的bool)
4.添加内容
添加oneState
- oneState=machine.AddState(名字,位置)
添加anyState到oneState的transition
- AnimatorStateTransition defaultTransition=machine.AddAnyStateTransition(oneState);
创建二级子状态机
- AnimatorStateMachine sub2Machine=machine.AddStateMachine(名字,位置)
作者这里创建了三级子状态机,子状态机跟父状态机一样,都可以添加state、transition。有一点需要注意,
- AnimatorStateTransition transition1=machine.AddAnyStateTransition(oneState);
- AnimatorStateTransition transition2=sub2Machine.AddAnyStateTransition(twoState);
transition1和transition2不是从同一个anyState出发的,而是从各自状态机的anyState出发的(子状态
机也有个anyState,但是在controller编辑器里看不出来)
5.雨松momo的帖子里有网友问到:如果fbx里有多个clip,用AssetDatabase.LoadAssetAtPath(xxx.fbx)
如何取到呢?那么答案来了
- Object[] objects=AssetDatabase.LoadAllAssetsAtPath("Assets/Art/Animation/"+fbxName);
- for(int m=0;m<objects.Length;m++)
- {
- <span style="white-space:pre"> </span>if(objects[m].GetType()==typeof(AnimationClip) && !objects[m].name.Contains("Take 001"))
- {
- AnimationClip clip=(AnimationClip)objects[m];
- if(clip.name.Equals("chushidongzuo001"))
- {
- defaultState.motion=clip;
- }
- else
- {
- addAnimationClip(clip,actionDetails,machine,haveSub3);
- }
- }
- }
注意到了吗?AssetDatabase.LoadAllAssetsAtPath可以把fbx里的所有信息取到,clip、骨骼节点、mesh。。。,
然后过滤一下下。而AssetDatabase.LoadAssetAtPath<AnimationClip>("xxx.FBX")只能取到此fbx下第一个clip。
好了,就写到这里,下面把整个代码贴出来,供大家参考~
- using UnityEngine;
- using System.Collections.Generic;
- using UnityEditor;
- //using UnityEditorInternal;
- using UnityEditor.Animations;
- using System.IO;
- public class Play2ControllerGenerator : EditorWindow {
- public enum ActionType:int
- {
- undefine=0,
- zhan,
- tang,
- cewo,
- pa,
- guodu,
- kaichang,
- jieshu,
- wu
- }
- public enum ActionStyle:int
- {
- normal=0,
- qingchun,
- keai,
- gaoleng,
- yundong,
- chengshu,
- xinggan
- }
- private static string[] sub2MachineNames=new string[]{"undefine","zhan","tang","cewo","pa","guodu","kaichang","jieshu","wu"};
- private static string[] sub3MachineNames=new string[]{"normal","qingchun","keai","gaoleng","yundong","chengshu","xinggan"};
- private const string ParamNameTransitionAction="transitionAction";
- private const string ParamNameActionId="actionId";
- private const float TransitionDuring=0.4f;
- //
- [MenuItem("LoveLive/核心玩法2/生成状态机")]
- private static void addWindow()
- {
- Rect rect=new Rect(0,0,600,300);
- Play2ControllerGenerator window=EditorWindow.GetWindowWithRect<Play2ControllerGenerator>(rect,true,"生成核心玩法状态机(省得策划自己摆了)");
- window.minSize=new Vector2(300,100);
- window.maxSize=new Vector2(600,300);
- window.Show();
- }
- void OnGUI()
- {
- EditorGUILayout.BeginVertical();
- EditorGUILayout.LabelField("需要\"Assets/Resources/Config/action.txt\"");
- EditorGUILayout.LabelField("需要\"Assets/Art/Animation\"下的动作文件");
- EditorGUILayout.EndVertical();
- EditorGUILayout.Separator();
- EditorGUILayout.LabelField("生成的状态机在这个位置:\"Assets/Resources/CharacterAnim/Play2Controllor_2.controller\"");
- EditorGUILayout.BeginHorizontal();
- if(GUILayout.Button("点击生成(需要一点时间)"))
- {
- generateStateMachine();
- this.ShowNotification(new GUIContent("创建完毕"));
- }
- EditorGUILayout.EndHorizontal();
- }
- private void generateStateMachine()
- {
- //加载action文件
- Dictionary<string,ActionDetail> actionDetails=parseActionFile("Assets/Resources/Config/action.txt");
- //创建controller
- AnimatorController controller=AnimatorController.CreateAnimatorControllerAtPath("Assets/Resources/CharacterAnim/Play2Controllor_2.controller");
- AnimatorControllerLayer layer=controller.layers[0];
- AnimatorStateMachine machine=layer.stateMachine;
- //创建参数
- controller.AddParameter(ParamNameTransitionAction,AnimatorControllerParameterType.Trigger);
- controller.AddParameter(ParamNameActionId,AnimatorControllerParameterType.Int);
- controller.AddParameter("zhan->pa",AnimatorControllerParameterType.Trigger);
- controller.AddParameter("pa->cewo",AnimatorControllerParameterType.Trigger);
- controller.AddParameter("pa->tang",AnimatorControllerParameterType.Trigger);
- controller.AddParameter("tang->cewo",AnimatorControllerParameterType.Trigger);
- controller.AddParameter("cewo->tang",AnimatorControllerParameterType.Trigger);
- controller.AddParameter("default",AnimatorControllerParameterType.Trigger);
- //创建默认state
- AnimatorState defaultState=machine.AddState("default",new Vector3(300,0,0));
- //defaultState.motion=
- machine.defaultState=defaultState;
- AnimatorStateTransition defaultTransition=machine.AddAnyStateTransition(defaultState);
- defaultTransition.AddCondition(AnimatorConditionMode.If,0,"default");
- defaultTransition.duration=TransitionDuring;
- //3级状态机条件
- List<string> haveSub3=new List<string>();
- haveSub3.Add("zhan");
- haveSub3.Add("tang");
- haveSub3.Add("cewo");
- haveSub3.Add("pa");
- //创建子状态机
- for(int k=1;k<sub2MachineNames.Length;k++)
- {
- AnimatorStateMachine sub2Machine=machine.AddStateMachine(sub2MachineNames[k],new Vector3(500,k*50,0));
- if(haveSub3.Contains(sub2MachineNames[k]))
- {
- for(int m=0;m<sub3MachineNames.Length;m++)
- {
- AnimatorStateMachine sub3Machine=sub2Machine.AddStateMachine(sub3MachineNames[m],new Vector3(500,m*50,0));
- }
- }
- }
- //添加clip
- string[] fileNames=Directory.GetFiles(Application.dataPath+"/Art/Animation");
- for(int k=0;k<fileNames.Length;k++)
- {
- if(fileNames[k].EndsWith("fbx") || fileNames[k].EndsWith("FBX"))
- {
- //获取fbx文件名
- int index=fileNames[k].LastIndexOf("/");
- string fbxName=fileNames[k].Substring(index+1,fileNames[k].Length-index-1);
- //读取fbx里的动作文件
- Object[] objects=AssetDatabase.LoadAllAssetsAtPath("Assets/Art/Animation/"+fbxName);
- for(int m=0;m<objects.Length;m++)
- {
- if(objects[m].GetType()==typeof(AnimationClip) && !objects[m].name.Contains("Take 001"))
- {
- AnimationClip clip=(AnimationClip)objects[m];
- if(clip.name.Equals("chushidongzuo001"))
- {
- defaultState.motion=clip;
- }
- else
- {
- addAnimationClip(clip,actionDetails,machine,haveSub3);
- }
- }
- }
- }
- }
- //AnimationClip clip=AssetDatabase.LoadAssetAtPath<AnimationClip>("Assets/Art/Animation/guodudongzuo-001.FBX");
- }
- private static void addAnimationClip(AnimationClip clip,Dictionary<string,ActionDetail> actionDetails,AnimatorStateMachine machine,List<string> haveSub3)
- {
- if(actionDetails.ContainsKey(clip.name))
- {
- ActionDetail ad=actionDetails[clip.name];
- foreach(ChildAnimatorStateMachine childMachine in machine.stateMachines)
- {
- AnimatorStateMachine sub2Machine=childMachine.stateMachine;
- if(sub2MachineNames[ad.type]==sub2Machine.name)
- {
- //有3级状态机的加3级里,没有的加2级里
- if(haveSub3.Contains(sub2Machine.name))
- {
- foreach(ChildAnimatorStateMachine childMachine3 in sub2Machine.stateMachines)
- {
- AnimatorStateMachine sub3Machine=childMachine3.stateMachine;
- if(sub3MachineNames[ad.style]==sub3Machine.name)
- {
- AnimatorState state=sub3Machine.AddState(clip.name,new Vector3(500,sub3Machine.states.Length*50,0));
- state.motion=clip;
- AnimatorStateTransition transition=machine.AddAnyStateTransition(state);
- transition.AddCondition(AnimatorConditionMode.If,0,ParamNameTransitionAction);
- transition.AddCondition(AnimatorConditionMode.Equals,ad.actionId,ParamNameActionId);
- transition.duration=TransitionDuring;
- }
- }
- }
- else
- {
- //过渡很特殊,这里写死
- if(ad.type==(int)ActionType.guodu)
- {
- string stateName="";
- float speed=1;
- string condition="";
- int indexZhan=clip.name.IndexOf("zhan");
- int indexPa=clip.name.IndexOf("pa");
- int indexCewo=clip.name.IndexOf("cewo");
- int indexTang=clip.name.IndexOf("tang");
- //侧卧和躺之间有正向和反向片段
- if(indexCewo>=0 && indexTang>=0)
- {
- stateName+="cewo->tang";
- speed=(indexCewo<indexTang)?1:-1;
- condition="cewo->tang";
- if(!string.IsNullOrEmpty(condition))
- {
- AnimatorState state=sub2Machine.AddState(stateName,new Vector3(500,sub2Machine.states.Length*50,0));
- state.motion=clip;
- state.speed=speed;
- AnimatorStateTransition transition=machine.AddAnyStateTransition(state);
- transition.AddCondition(AnimatorConditionMode.If,0,condition);
- transition.duration=TransitionDuring;
- }
- stateName="guodu-tang->cewo";
- speed=(indexTang<indexCewo)?1:-1;
- condition="tang->cewo";
- if(!string.IsNullOrEmpty(condition))
- {
- AnimatorState state=sub2Machine.AddState(stateName,new Vector3(500,sub2Machine.states.Length*50,0));
- state.motion=clip;
- state.speed=speed;
- AnimatorStateTransition transition=machine.AddAnyStateTransition(state);
- transition.AddCondition(AnimatorConditionMode.If,0,condition);
- transition.duration=TransitionDuring;
- }
- }
- else
- {
- if(indexZhan>=0 && indexPa>=0)
- {
- stateName="guodu-zhan->pa";
- speed=(indexZhan<indexPa)?1:-1;
- condition="zhan->pa";
- }
- else if(indexPa>=0 && indexCewo>=0)
- {
- stateName="guodu-pa->cewo";
- speed=(indexPa<indexCewo)?1:-1;
- condition="pa->cewo";
- }
- else if(indexPa>=0 && indexTang>=0)
- {
- stateName="guodu-pa->tang";
- speed=(indexPa<indexTang)?1:-1;
- condition="pa->tang";
- }
- if(!string.IsNullOrEmpty(condition))
- {
- AnimatorState state=sub2Machine.AddState(stateName,new Vector3(500,sub2Machine.states.Length*50,0));
- state.motion=clip;
- state.speed=speed;
- AnimatorStateTransition transition=machine.AddAnyStateTransition(state);
- transition.AddCondition(AnimatorConditionMode.If,0,condition);
- transition.duration=TransitionDuring;
- }
- }
- }
- else
- {
- AnimatorState state=sub2Machine.AddState(clip.name,new Vector3(500,sub2Machine.states.Length*50,0));
- state.motion=clip;
- AnimatorStateTransition transition=machine.AddAnyStateTransition(state);
- transition.AddCondition(AnimatorConditionMode.If,0,ParamNameTransitionAction);
- transition.AddCondition(AnimatorConditionMode.Equals,ad.actionId,ParamNameActionId);
- transition.duration=TransitionDuring;
- }
- }
- }
- }
- }
- }
- //解析动作文件
- private Dictionary<string,ActionDetail> parseActionFile(string actionFilePath)
- {
- Dictionary<string,ActionDetail> actionInfos=new Dictionary<string,ActionDetail>();
- TextAsset ta=AssetDatabase.LoadAssetAtPath<TextAsset>(actionFilePath);
- using(MemoryStream stream = new MemoryStream(ta.bytes))
- {
- using(StreamReader reader = new StreamReader(stream))
- {
- //第一行是列名,跳过
- int lineIdx = 0;
- while (reader.Peek() >= 0)
- {
- lineIdx++;
- string source = reader.ReadLine();
- if (lineIdx != 1 && !string.IsNullOrEmpty(source))
- {
- ActionDetail ad=ActionDetail.create(source);
- actionInfos.Add(ad.actionName,ad);
- }
- }
- }
- }
- Debug.Log("共需要获取"+actionInfos.Count+"个动作");
- return actionInfos;
- }
- }
- public class ActionDetail
- {
- public int actionId;
- public string actionName;
- public int type;
- public int style;
- public static ActionDetail create(string line)
- {
- string[] ss=line.Split('\t');
- ActionDetail ad=new ActionDetail();
- ad.actionId=int.Parse(ss[0]);
- ad.actionName=ss[1];
- ad.type=int.Parse(ss[2]);
- ad.style=int.Parse(ss[3]);
- return ad;
- }
- }
参考文章:
http://www.xuanyusong.com/archives/2811
unity5之代码创建状态机,玩的666的更多相关文章
- Stateless是一个基于C#创建状态机的简单库
Stateless是一个基于C#创建状态机的简单库 .Net轻量状态机Stateless 很多业务系统开发中,不可避免的会出现状态变化,通常采用的情形可能是使用工作流去完成,但是对于简单场景下,用工作 ...
- 【Android】纯代码创建页面布局(含异步加载图片)
开发环境:macOS 10.12 + Android Studio 2.2,MinSDK Android 5.1 先看看总体效果 本示例是基于Fragment进行的,直接上代码: [界面结构] 在 F ...
- ios-将代码创建的视图控件放入拖拽控件的下面
如图所示 图片是拖拽上去的imageView,橘黄色控件是在代码中创建的添加上去的,此时黄色view在imageView 上方 调用方法bringSubviewToFront:试图将imageView ...
- 代码创建数据库_表--SqlServer数据库
/*1.创建数据库的时候需要设置的基本属性: 数据库名称 逻辑名称 初始大小 文件增长 路径*/ --语法: -- create database 数据库名称 -- on [primary]--创建数 ...
- 代码创建storyboard
代码创建storyboard方式如下 // 加载storyboard UIStoryboard *storyboard = [UIStoryboard StoryboardWithName:@&quo ...
- WPF使用后台C#代码创建Grid
笔者刚刚接触WPF,菜鸟一枚,在做一个练手程序时遇到这样一个需求,创建一个新的Grid并将其添加至一个ListView中,要求Grid及其子元素应按一定顺序给Name属性赋值,直接使用XAML创建的话 ...
- ios - 纯代码创建collectionView
开始考虑好一点点时间,因为一般的都是用xib,或者storyboard来写的.这次用纯代码...废话较多请看 首先把storyboard干掉,工程里面的main干掉 由于干掉了storyboard则启 ...
- SharePoint 2013 中代码创建列表查阅项字段
1.首先,打开VS创建两个List Definition,分别是Address和City,如下图: 2.City列表里修改Title为City Name,其实内部名称还是Title,注意一下: 3.给 ...
- swift 字符转为类,代码创建控件
在使用类之前要先获得 命名空间 通过json来获取 字符型的类名 然后创建类对象,这时候就要用到字符转类 // 从info字典中获取到 命名空间 转为字符型 let NS = NSBundle.mai ...
随机推荐
- SDWebImage学习
SDWebImage学习 SDWebImage版本是:'4.2.2' SDWebImage是iOS开发中常用的图片加载的库,能下载并缓存图片.这次就着重介绍SDWebImage的特色功能:下载与缓存. ...
- elk 日志分析系统Logstash+ElasticSearch+Kibana4
elk 日志分析系统 Logstash+ElasticSearch+Kibana4 logstash 管理日志和事件的工具 ElasticSearch 搜索 Kibana4 功能强大的数据显示clie ...
- WinCE下使用C#的几个小技巧
1.我们知道,在使用Windows的开发机上用C#启动一个外部程序的方法有很多,但这些方法用在使用WinCE的目标工控机上都无能为力,现在小嫚儿以打开一个IE为例,介绍如何在WinCE下使用C#来打开 ...
- GCJ Qualification Round 2016 C题
题意是给定了一个叫“jamcoin”的定义,让你生成足够数量满足条件的jamcoin. jamcoin其实就可以理解成一个二进制整数,题目要求的要么长度为16位,要么为32位,一头一尾两个位必须是1, ...
- jmeter使用笔记——流程及常用组件配置
添加线程组 线程数 :对应用户数, Ramp-Up: 多少秒启动这些线程,1秒代表1秒内启动设置的线程数,10秒代表10秒内启动线程数 循环次数: 每个线程执行线程组内的请求循环次数 调度器:可以对线 ...
- Linux系统中10个常用的ps命令总结
Linux作为Unix的衍生操作系统,Linux内建有查看当前进程的工具ps.这个工具能在命令行中使用PS 命令是什么 查看它的man手册可以看到,ps命令能够给出当前系统中进程的快照.它能捕获系统在 ...
- 网页兼容性测试(工具使用IETESTER、Firefox、360安全浏览器)
网页兼容性测试主要是针对不同的浏览器进行的测试.由于用户浏览器的不同,往往都会使我们的网页发生页面样式错乱,图片无法显示等问题.对于前端开发工程师来说,确保代码在各种主流浏览器的各个版本中都能正常显示 ...
- LA-5059(组合游戏)
题意: 有n堆石子,分别有a1,a2,...,an个,两个游戏者轮流操作,每次可以选一堆m拿走至少一个且不超过一半的石子,谁不能拿石子就算输; 思路: a1太大打印sg表找规律,然后就是异或和了; A ...
- linux命令学习笔记 : install 命令
install .作用 install命令的作用是安装或升级软件或备份数据,它的使用权限是所有用户. .格式 ()install [选项]... 来源 目的地 ()install [选项]... 来源 ...
- ORA-12547: TNS:lost contact
碰到这个ORA-12547: TNS:lost contact的问题,翻了很多资料和METALINK,总结了一下原因: 1 是由于rpm包没有安装,对于我们的生产环境,此包是安装的. admin@p1 ...