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 ...
随机推荐
- php CURL 模拟 POST 提交数据
<?php function liansuo_post($url,$data){ // 模拟提交数据函数 $curl = curl_init(); // 启动一个CURL会话 curl_seto ...
- iOS开发教程:Storyboard全解析-第一部分
本文转载至http://blog.csdn.net/chang6520/article/details/7945845 感谢原文作者分享 故事版(Storyboard)是一个能够节省你很多设计 ...
- php 中 isset 和empty 的区别
昨天终于开始学习php了,这个对于我来说听着很熟悉,但是学起来很陌生的东西,尤其是课上能听明白 但是真正写起了手生,都不知道手该往哪里放了,天哪~~~ 其中课上有讲到 isset和empty的区别,现 ...
- EasyDarwin实现RTSP播放动态认证的两种方式:Basic/Digest & Token
问题描述 目前为了能够方便开发者,我们将EasyDarwin中的RTSP认证过程直接忽略过了,如果要开启认证的方式,我们可以在代码中打开: case kRoutingRequest: { // Inv ...
- EasyCamera Android安卓移动视频监控单兵设备接入EasyDarwin开源流媒体云平台
前言 随着Android系统的不断更新和发展,现在越来越多的硬件产品选择用安卓系统作为运行环境,电视机,机顶盒.门禁.行车记录仪.车载系统.单兵设备等等,Android系统底层还是Linux,但对上层 ...
- superslider网站特效插件
网站上常用的“焦点图/幻灯片”“Tab标签切换”“图片滚动”“无缝滚动” 如何使用 1.引入jquery.js 引入superslider.js 2.编写HTML 以下是默认的HTMl结构,分别 ...
- Java类加载器( 死磕 4)
[正文]Java类加载器( CLassLoader ) 死磕 之4: 神秘的双亲委托机制 本小节目录 4.1. 每个类加载器都有一个parent父加载器 4.2. 类加载器之间的层次关系 4.3. ...
- 盒子的display属性
<body> <div style="display:inline">Box-1</div> <div style="displ ...
- UVA10518 How Many Calls? —— 矩阵快速幂
题目链接:https://vjudge.net/problem/UVA-10518 题解: 问:求斐波那契数f[n]的时候调用了多少次f[n] = f[n-1] + f[n-2],没有记忆化,一直递归 ...
- bzoj2132【圈地计划】
题面 思路: 一开始以为和为了博多一样,两边连一样的,后来发现中间连负边的话根本不会割,即割断两块收益为负,所以WA的起飞…… 正解是先黑白染色,每个点和它周围的点连边方式不同.对于黑点A,S--&g ...