基于Unity有限状态机框架
这个框架是Unity wiki上的框架。网址:http://wiki.unity3d.com/index.php/Finite_State_Machine
这就相当于是“模板”吧,自己写的代码,写啥都是一个风格,还是得多读书,多看报啊...
有限状态机用来做状态转移,比如代码中的例子,NPC有两个状态跟着主角走(状态A),或者沿着自己的路线走(状态B)。有两个转换,其实就是两条边,A->B,B->A.
框架里面Transition表示边,StateID表示状态,FSMState是一个状态的抽象类,最主要的是Reason方法,就是判断是不是要进行转换,像例子里B状态里写的就是如果npc前面15m有主角,就进行转换,转换的时候又要进行一些个操作。Act方法就是当前状态要做的一些事情。
具体也没用过这个框架,也不知道理解的对不对。。项目里,我都是硬写乱搞的,网上搜了搜说,有限状态机有很多缺点,我觉得简单的只有几个状态的话,也挺方便的,以后可以尝试一下。
框架:
- using System;
- using System.Collections;
- using System.Collections.Generic;
- using UnityEngine;
- /**
- A Finite State Machine System based on Chapter 3.1 of Game Programming Gems 1 by Eric Dybsand
- Written by Roberto Cezar Bianchini, July 2010
- How to use:
- 1. Place the labels for the transitions and the states of the Finite State System
- in the corresponding enums.
- 2. Write new class(es) inheriting from FSMState and fill each one with pairs (transition-state).
- These pairs represent the state S2 the FSMSystem should be if while being on state S1, a
- transition T is fired and state S1 has a transition from it to S2. Remember this is a Deterministic FSM.
- You can't have one transition leading to two different states.
- Method Reason is used to determine which transition should be fired.
- You can write the code to fire transitions in another place, and leave this method empty if you
- feel it's more appropriate to your project.
- Method Act has the code to perform the actions the NPC is supposed do if it's on this state.
- You can write the code for the actions in another place, and leave this method empty if you
- feel it's more appropriate to your project.
- 3. Create an instance of FSMSystem class and add the states to it.
- 4. Call Reason and Act (or whichever methods you have for firing transitions and making the NPCs
- behave in your game) from your Update or FixedUpdate methods.
- Asynchronous transitions from Unity Engine, like OnTriggerEnter, SendMessage, can also be used,
- just call the Method PerformTransition from your FSMSystem instance with the correct Transition
- when the event occurs.
- THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED,
- INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE
- AND NON-INFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM,
- DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
- OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
- */
- /// <summary>
- /// Place the labels for the Transitions in this enum.
- /// Don't change the first label, NullTransition as FSMSystem class uses it.
- /// </summary>
- public enum Transition
- {
- NullTransition = , // Use this transition to represent a non-existing transition in your system
- }
- /// <summary>
- /// Place the labels for the States in this enum.
- /// Don't change the first label, NullTransition as FSMSystem class uses it.
- /// </summary>
- public enum StateID
- {
- NullStateID = , // Use this ID to represent a non-existing State in your system
- }
- /// <summary>
- /// This class represents the States in the Finite State System.
- /// Each state has a Dictionary with pairs (transition-state) showing
- /// which state the FSM should be if a transition is fired while this state
- /// is the current state.
- /// Method Reason is used to determine which transition should be fired .
- /// Method Act has the code to perform the actions the NPC is supposed do if it's on this state.
- /// </summary>
- public abstract class FSMState
- {
- protected Dictionary<Transition, StateID> map = new Dictionary<Transition, StateID>();
- protected StateID stateID;
- public StateID ID { get { return stateID; } }
- public void AddTransition(Transition trans, StateID id)
- {
- // Check if anyone of the args is invalid
- if (trans == Transition.NullTransition)
- {
- Debug.LogError("FSMState ERROR: NullTransition is not allowed for a real transition");
- return;
- }
- if (id == StateID.NullStateID)
- {
- Debug.LogError("FSMState ERROR: NullStateID is not allowed for a real ID");
- return;
- }
- // Since this is a Deterministic FSM,
- // check if the current transition was already inside the map
- if (map.ContainsKey(trans))
- {
- Debug.LogError("FSMState ERROR: State " + stateID.ToString() + " already has transition " + trans.ToString() +
- "Impossible to assign to another state");
- return;
- }
- map.Add(trans, id);
- }
- /// <summary>
- /// This method deletes a pair transition-state from this state's map.
- /// If the transition was not inside the state's map, an ERROR message is printed.
- /// </summary>
- public void DeleteTransition(Transition trans)
- {
- // Check for NullTransition
- if (trans == Transition.NullTransition)
- {
- Debug.LogError("FSMState ERROR: NullTransition is not allowed");
- return;
- }
- // Check if the pair is inside the map before deleting
- if (map.ContainsKey(trans))
- {
- map.Remove(trans);
- return;
- }
- Debug.LogError("FSMState ERROR: Transition " + trans.ToString() + " passed to " + stateID.ToString() +
- " was not on the state's transition list");
- }
- /// <summary>
- /// This method returns the new state the FSM should be if
- /// this state receives a transition and
- /// </summary>
- public StateID GetOutputState(Transition trans)
- {
- // Check if the map has this transition
- if (map.ContainsKey(trans))
- {
- return map[trans];
- }
- return StateID.NullStateID;
- }
- /// <summary>
- /// This method is used to set up the State condition before entering it.
- /// It is called automatically by the FSMSystem class before assigning it
- /// to the current state.
- /// </summary>
- public virtual void DoBeforeEntering() { }
- /// <summary>
- /// This method is used to make anything necessary, as reseting variables
- /// before the FSMSystem changes to another one. It is called automatically
- /// by the FSMSystem before changing to a new state.
- /// </summary>
- public virtual void DoBeforeLeaving() { }
- /// <summary>
- /// This method decides if the state should transition to another on its list
- /// NPC is a reference to the object that is controlled by this class
- /// </summary>
- public abstract void Reason(GameObject player, GameObject npc);
- /// <summary>
- /// This method controls the behavior of the NPC in the game World.
- /// Every action, movement or communication the NPC does should be placed here
- /// NPC is a reference to the object that is controlled by this class
- /// </summary>
- public abstract void Act(GameObject player, GameObject npc);
- } // class FSMState
- /// <summary>
- /// FSMSystem class represents the Finite State Machine class.
- /// It has a List with the States the NPC has and methods to add,
- /// delete a state, and to change the current state the Machine is on.
- /// </summary>
- public class FSMSystem
- {
- private List<FSMState> states;
- // The only way one can change the state of the FSM is by performing a transition
- // Don't change the CurrentState directly
- private StateID currentStateID;
- public StateID CurrentStateID { get { return currentStateID; } }
- private FSMState currentState;
- public FSMState CurrentState { get { return currentState; } }
- public FSMSystem()
- {
- states = new List<FSMState>();
- }
- /// <summary>
- /// This method places new states inside the FSM,
- /// or prints an ERROR message if the state was already inside the List.
- /// First state added is also the initial state.
- /// </summary>
- public void AddState(FSMState s)
- {
- // Check for Null reference before deleting
- if (s == null)
- {
- Debug.LogError("FSM ERROR: Null reference is not allowed");
- }
- // First State inserted is also the Initial state,
- // the state the machine is in when the simulation begins
- if (states.Count == )
- {
- states.Add(s);
- currentState = s;
- currentStateID = s.ID;
- return;
- }
- // Add the state to the List if it's not inside it
- foreach (FSMState state in states)
- {
- if (state.ID == s.ID)
- {
- Debug.LogError("FSM ERROR: Impossible to add state " + s.ID.ToString() +
- " because state has already been added");
- return;
- }
- }
- states.Add(s);
- }
- /// <summary>
- /// This method delete a state from the FSM List if it exists,
- /// or prints an ERROR message if the state was not on the List.
- /// </summary>
- public void DeleteState(StateID id)
- {
- // Check for NullState before deleting
- if (id == StateID.NullStateID)
- {
- Debug.LogError("FSM ERROR: NullStateID is not allowed for a real state");
- return;
- }
- // Search the List and delete the state if it's inside it
- foreach (FSMState state in states)
- {
- if (state.ID == id)
- {
- states.Remove(state);
- return;
- }
- }
- Debug.LogError("FSM ERROR: Impossible to delete state " + id.ToString() +
- ". It was not on the list of states");
- }
- /// <summary>
- /// This method tries to change the state the FSM is in based on
- /// the current state and the transition passed. If current state
- /// doesn't have a target state for the transition passed,
- /// an ERROR message is printed.
- /// </summary>
- public void PerformTransition(Transition trans)
- {
- // Check for NullTransition before changing the current state
- if (trans == Transition.NullTransition)
- {
- Debug.LogError("FSM ERROR: NullTransition is not allowed for a real transition");
- return;
- }
- // Check if the currentState has the transition passed as argument
- StateID id = currentState.GetOutputState(trans);
- if (id == StateID.NullStateID)
- {
- Debug.LogError("FSM ERROR: State " + currentStateID.ToString() + " does not have a target state " +
- " for transition " + trans.ToString());
- return;
- }
- // Update the currentStateID and currentState
- currentStateID = id;
- foreach (FSMState state in states)
- {
- if (state.ID == currentStateID)
- {
- // Do the post processing of the state before setting the new one
- currentState.DoBeforeLeaving();
- currentState = state;
- // Reset the state to its desired condition before it can reason or act
- currentState.DoBeforeEntering();
- break;
- }
- }
- } // PerformTransition()
- } //class FSMSystem
例子:
- using System;
- using System.Collections.Generic;
- using System.Text;
- using UnityEngine;
- [RequireComponent(typeof(Rigidbody))]
- public class NPCControl : MonoBehaviour
- {
- public GameObject player;
- public Transform[] path;
- private FSMSystem fsm;
- public void SetTransition(Transition t) { fsm.PerformTransition(t); }
- public void Start()
- {
- MakeFSM();
- }
- public void FixedUpdate()
- {
- fsm.CurrentState.Reason(player, gameObject);
- fsm.CurrentState.Act(player, gameObject);
- }
- // The NPC has two states: FollowPath and ChasePlayer
- // If it's on the first state and SawPlayer transition is fired, it changes to ChasePlayer
- // If it's on ChasePlayerState and LostPlayer transition is fired, it returns to FollowPath
- private void MakeFSM()
- {
- FollowPathState follow = new FollowPathState(path);
- follow.AddTransition(Transition.SawPlayer, StateID.ChasingPlayer);
- ChasePlayerState chase = new ChasePlayerState();
- chase.AddTransition(Transition.LostPlayer, StateID.FollowingPath);
- fsm = new FSMSystem();
- fsm.AddState(follow);
- fsm.AddState(chase);
- }
- }
- public class FollowPathState : FSMState
- {
- private int currentWayPoint;
- private Transform[] waypoints;
- public FollowPathState(Transform[] wp)
- {
- waypoints = wp;
- currentWayPoint = ;
- stateID = StateID.FollowingPath;
- }
- public override void Reason(GameObject player, GameObject npc)
- {
- // If the Player passes less than 15 meters away in front of the NPC
- RaycastHit hit;
- if (Physics.Raycast(npc.transform.position, npc.transform.forward, out hit, 15F))
- {
- if (hit.transform.gameObject.tag == "Player")
- npc.GetComponent<NPCControl>().SetTransition(Transition.SawPlayer);
- }
- }
- public override void Act(GameObject player, GameObject npc)
- {
- // Follow the path of waypoints
- // Find the direction of the current way point
- Vector3 vel = npc.rigidbody.velocity;
- Vector3 moveDir = waypoints[currentWayPoint].position - npc.transform.position;
- if (moveDir.magnitude < )
- {
- currentWayPoint++;
- if (currentWayPoint >= waypoints.Length)
- {
- currentWayPoint = ;
- }
- }
- else
- {
- vel = moveDir.normalized * ;
- // Rotate towards the waypoint
- npc.transform.rotation = Quaternion.Slerp(npc.transform.rotation,
- Quaternion.LookRotation(moveDir),
- * Time.deltaTime);
- npc.transform.eulerAngles = new Vector3(, npc.transform.eulerAngles.y, );
- }
- // Apply the Velocity
- npc.rigidbody.velocity = vel;
- }
- } // FollowPathState
- public class ChasePlayerState : FSMState
- {
- public ChasePlayerState()
- {
- stateID = StateID.ChasingPlayer;
- }
- public override void Reason(GameObject player, GameObject npc)
- {
- // If the player has gone 30 meters away from the NPC, fire LostPlayer transition
- if (Vector3.Distance(npc.transform.position, player.transform.position) >= )
- npc.GetComponent<NPCControl>().SetTransition(Transition.LostPlayer);
- }
- public override void Act(GameObject player, GameObject npc)
- {
- // Follow the path of waypoints
- // Find the direction of the player
- Vector3 vel = npc.rigidbody.velocity;
- Vector3 moveDir = player.transform.position - npc.transform.position;
- // Rotate towards the waypoint
- npc.transform.rotation = Quaternion.Slerp(npc.transform.rotation,
- Quaternion.LookRotation(moveDir),
- * Time.deltaTime);
- npc.transform.eulerAngles = new Vector3(, npc.transform.eulerAngles.y, );
- vel = moveDir.normalized * ;
- // Apply the new Velocity
- npc.rigidbody.velocity = vel;
- }
- } // ChasePlayerState
基于Unity有限状态机框架的更多相关文章
- MVC4 基于 Unity Ioc 框架的 ControllerFactory
首先引入Untiy框架. 可以在NuGet程序包 管理器中直接安装. 新建 继承DefaultControllerFactory 的UnityControllerFactory: 重写虚方法GetC ...
- 关于基于python2.7的unity自动化测试框架GAutomator测试环境的搭建(源码网盘下载地址:https://pan.baidu.com/s/1c2TXwtU)
关于基于python 2.7的unity自动化测试框架GAutomator测试环境的搭建 百度云盘链接(思维图学习资料):https://pan.baidu.com/s/1dFWExMD 准备工作(具 ...
- Unity 游戏框架搭建 (十) QFramework v0.0.2小结
从框架搭建系列的第一篇文章开始到现在有四个多月时间了,这段时间对自己来说有很多的收获,好多小伙伴和前辈不管是在评论区还是私下里给出的建议非常有参考性,在此先谢过各位. 说到是一篇小节,先列出框架的概要 ...
- 基于Qt有限状态机的一种实现方式和完善的人工智能方法
基于Qt有限状态机的一种实现方式和完善的人工智能方法 人工智能在今年是一个非常火的方向,当然了.不不过今年,它一直火了非常多年,有关人工智能的一些算法层出不穷.人工智能在非常多领域都有应用,就拿我熟悉 ...
- Unity 游戏框架搭建 2018 (一) 架构、框架与 QFramework 简介
约定 还记得上版本的第二十四篇的约定嘛?现在出来履行啦~ 为什么要重制? 之前写的专栏都是按照心情写的,在最初的时候笔者什么都不懂,而且文章的发布是按照很随性的一个顺序.结果就是说,大家都看完了,都还 ...
- Unity 游戏框架搭建 (十六) v0.0.1 架构调整
背景: 前段时间用Xamarin.OSX开发一些工具,遇到了两个问题. QFramework的大部分的类耦合了Unity的API,这样导致不能在其他CLR平台使用QFramework. QFramew ...
- Unity StrangeIoC框架
Unity StrangeIoC框架 http://blog.csdn.net/y1196645376/article/details/52746251
- 基于Java Netty框架构建高性能的部标808协议的GPS服务器
使用Java语言开发一个高质量和高性能的jt808 协议的GPS通信服务器,并不是一件简单容易的事情,开发出来一段程序和能够承受数十万台车载接入是两码事,除去开发部标808协议的固有复杂性和几个月长周 ...
- 基于Typecho CMS框架开发大中型应用
基于Typecho CMS框架开发大中型应用 大中型应用暂且定义为:大于等于3个数据表的应用!汗吧! Typecho原本是一款博客系统,其框架体系有别于市面上一般意义MVC框架,主体代码以自创的Wid ...
随机推荐
- js_面向对象
面向对象的语言有一个标志,即拥有类的概念,抽象实例对象的公共属性与方法,基于类可以创建任意多个实例对象,一般具有封装.继承.多态的特性!但JS中对象与纯面向对象语言中的对象是不同的,ECMA标准定义J ...
- IIS性能提升
1. 调整IIS 7应用程序池队列长度 由原来的默认1000改为65535. IIS Manager > ApplicationPools > Advanced Settings Queu ...
- jquery样式篇
1.jquery: 1.1简介 jquery是一个轻量级的javascript库.版本号分1.x版本和2.x版本,2.x版本不再支持IE6 7 8,而更好的支 持移动端开发. 每一个版本分为开发版和压 ...
- OpenLayers.2.10.Beginners.Guide---第一章
从网上下载openlayers2,解压取得img theme 文件夹和openlayes.js文件.放在同一文件夹下用phpstorm打开. 创建index.html-------------每一行都 ...
- Sqlserver 2008清除数据库日志
USE [master] GO ALTER DATABASE DBNAME SET RECOVERY SIMPLE WITH NO_WAIT GO ALTER DATABASE DBNAME SET ...
- 解决java.lang.NoClassDefFoundError: org/objectweb/asm/util/TraceClassVisitor
方案一: <dependency> <groupId>asm</groupId> <artifactId>asm-all</artifactId& ...
- JPA快速入门(自用)
该案例为JPA的实体映射案例,将该项目解压导入到myeclipse中即可使用,然后直接使用hibernate即可! 文件地址:files.cnblogs.com/mrwangblog/JPA-Hibe ...
- MongoDB安装并随windows开机自启
MongoDB的官方下载站是http://www.mongodb.org/downloads,可以去上面下载最新的程序下来.在下载页面可以看到,对操作系统支持很全面,OS X.Linux.Window ...
- ethhdr、ether_header、iphdr、tcphdr、udphdr 结构介绍
转自:http://blog.csdn.net/petershina/article/details/8573853 ************************eth的结构*********** ...
- Android学习资源整理
官方文档:https://developer.android.com/guide/index.html (万万没想到居然有中文) 网友整理的学习笔记,挺不错的 http://www.runoob.co ...