Unity的状态机设计
Unity的状态机设计
本人新手,随便写写而已。
本文通过一个实例实现了在Unity下的有限状态机(参考了wiki上的教程)。
有限状态机是一个设备具有有限数量的状态,他可以在任何时间根据输入进行操作,使得一个状态进入另个一个状态。一个状态机在任何瞬间只能处于一种状态。
具体可以参考 状态设计模式。
本例是这样一种状态装换。
游戏人物NPC在空闲时处于巡逻状态,当看见Player在视野内的时候,转入转入追逐Player状态;一旦和Player距离拉大,便返回巡逻状态。
using UnityEngine; using System.Collections; using System; using System.Collections.Generic; //无论是NPC还是Player都有很多状态,在状态切换的时候为了使得程序显得清晰明了,采用了状态机制 //首先实际状态都是继承自某个状态抽象类的,这个抽象类定义了进入,退出一个状态虚方法 //定义了检测环境是否发生状态改变,以及在该状态下执行的动作的抽象方法 // 该实例主要涉及到一个NPC在指定的位置进行巡逻,当看到Playeer的时候切换状态进入追逐Player状态 public enum Translate //如果进入一个新的状态,需要一些触发,比如NPC看到了Player,由巡逻状态进入跟踪状态 { NullTrans, SeePlayer, LosePlayer } public enum StateID //每个状态都应该有一个ID,作为识别改状态的标志 { NullState, Chaseingplayer, FollowPath } public abstract class FMS_State //定一个抽象类 { private StateID id; //定一个状态ID作为变量来标识 public StateID ID { set { id = value; } get { return id; } } private Dictionary<Translate, StateID> map = new Dictionary<Translate, StateID>(); //在某一状态下,事件引起了触发进入另一个状态 // 于是我们定义了一个字典,存储的便是触发的类型,以及对应要进入的状态 public void addDictionary(Translate tr, StateID id1) //向字典里添加 { if (tr == Translate.NullTrans) { Debug.LogError("Null Trans is not allower to adding into"); return; } if (ID == StateID.NullState) { Debug.LogError("Null State id not ~~~"); return; } if (map.ContainsKey(tr)) //NPC 任何时候都只能出于一种状态,所以一旦定义了一个触发的枚举类型,对应的只能是接下来的一种状态 { Debug.LogError(id1.ToString() + "is already added to"); return; } map.Add(tr, id1); } public void deleateDictionary(Translate tr) //删除字典里存储的一个状态 { if (tr == Translate.NullTrans) { Debug.LogError("TransNull is not allowed to delate"); return; } if (map.ContainsKey(tr)) { map.Remove(tr); return; } Debug.LogError(tr.ToString() + "are not exist"); } public StateID GetOutState(Translate tr) //由状态的触发枚举类型返回一个对应的状态类型 { if (map.ContainsKey(tr)) { // Debug.Log("Translate " + tr + "State" + map[tr]); return map[tr]; } return StateID.NullState; } public virtual void DoBeforeEnter() { } //虚方法 public virtual void DoBeforeMoveing() { } public abstract void Reason(GameObject player, GameObject NPC); // 抽象方法 public abstract void Act(GameObject player, GameObject NPC); }
随后根据这个基类派生出巡逻类,和追逐类。
using UnityEngine; using System.Collections; using System; using System.Collections.Generic; public class Follow_State:FMS_State //派生出一个巡逻的类 { private GameObject[] waypoints; //在这些位置巡逻 private int currentWayPoint; public Follow_State(GameObject[] ob) { waypoints= ob; currentWayPoint = 0; ID = StateID.FollowPath; //当前状态的ID } public override void Reason(GameObject player, GameObject NPC) //与环境交互,来判断是否需要状态切换 { RaycastHit hit; if(Physics.Raycast(NPC.transform.position,NPC.transform.forward,out hit ,15)) { if(hit.transform.tag=="Player") { FMS_Machine_Manage.GetInstance.changeState(Translate.SeePlayer); } } } public override void Act(GameObject player, GameObject NPC) //在该状态下改做点什么呢 { Vector3 vel = NPC.rigidbody.velocity; Vector3 dir =waypoints[currentWayPoint].transform.position- NPC.transform.position; if(dir.magnitude<1) { currentWayPoint++; // Debug.Log("currentwappoint " + currentWayPoint); if(currentWayPoint>=waypoints.Length) { currentWayPoint = 0; } } else { vel = dir.normalized*10; NPC.transform.rotation = Quaternion.Lerp(NPC.transform.rotation, Quaternion.LookRotation(dir), Time.deltaTime * 5); NPC.transform.eulerAngles = new Vector3(0, NPC.transform.localEulerAngles.y, 0); } NPC.rigidbody.velocity = vel; } public override void DoBeforeMoveing() { } public override void DoBeforeEnter() { } }
using UnityEngine; using System.Collections; using System; using System.Collections.Generic; public class Chaseing_State : FMS_State { //派生出了一个追逐类 public Chaseing_State() { ID = StateID.Chaseingplayer; //定义 状态ID } public override void Reason(GameObject player, GameObject NPC) { if(Vector3.Distance(player.transform.position,NPC.transform.position)>=5) { FMS_Machine_Manage.GetInstance.changeState(Translate.LosePlayer); } } public override void Act(GameObject player, GameObject NPC) { Vector3 vel = NPC.rigidbody.velocity; Vector3 moveDir = player.transform.position - NPC.transform.position; NPC.transform.rotation = Quaternion.Lerp(NPC.transform.rotation, Quaternion.LookRotation(moveDir), Time.deltaTime * 5); NPC.transform.eulerAngles = new Vector3(0, NPC.transform.eulerAngles.y, 0); vel = moveDir * 3; NPC.rigidbody.velocity = vel; } }
通常对状态的管理,我们会建立一个stateMachine来管理。
using UnityEngine; using System.Collections; using System; using System.Collections.Generic; //状态管理类,对状态进行管理 public class FMS_Machine_Manage:MonoBehaviour { private List<FMS_State> states;//存储所有状态的的List private FMS_State currentState; //当前状态 private StateID currentStateID;//当前状态ID public FMS_State CurrentState { set { currentState = value; } get { return currentState; } } public StateID CurrentStateID { set { currentStateID = value; } get { return currentStateID; } } public GameObject player; public GameObject[] path; public GameObject NPC; private static FMS_Machine_Manage instance; public static FMS_Machine_Manage GetInstance { get { if (instance == null) { // instance = new FMS_Machine_Manage(); GameObject n = new GameObject(); n.name = "FMS_Machine_Manage"; instance = n.AddComponent<FMS_Machine_Manage>() as FMS_Machine_Manage; } return instance; } } public void UpdateFunction() { CurrentState.Reason(player, NPC); CurrentState.Act(player, NPC); } public void Revert() { } void Awake() { states = new List<FMS_State>(); //初始化 NPC = GameObject.FindGameObjectWithTag("NPC"); player = GameObject.FindGameObjectWithTag("Player"); path = GameObject.FindGameObjectsWithTag("path"); } public void MakeFMSMachine() { Follow_State follow = new Follow_State(path); follow.addDictionary(Translate.SeePlayer, StateID.Chaseingplayer); Chaseing_State chase = new Chaseing_State(); chase.addDictionary(Translate.LosePlayer, StateID.FollowPath); GetInstance. AddFmsState(follow); GetInstance. AddFmsState(chase); } public void AddFmsState(FMS_State s)//注册所有状态 { if (s == null) { Debug.LogError(" Null reference is not allowed"); } if (states.Count == 0) { states.Add(s); //设置默认状态(important); currentState = s; currentStateID = s.ID; return; } foreach (FMS_State state in states) { if (state == s) { Debug.LogError(s.ID.ToString() + "has already been added"); return; } } states.Add(s); } public void delateFmsState(StateID id) { if (id == StateID.NullState) { Debug.LogError("NullStateID is not allowed for a real state"); return; } foreach (FMS_State state in states) { if (state.ID == id) { states.Remove(state); return; } } } public void changeState(Translate tr) //更改状态 { if (tr == Translate.NullTrans) { Debug.LogError("NullTransition is not allowed for a real transition"); return; } //if (currentState.GetOutState(tr) == StateID.NullState) //{ // Debug.Log("translate" + " " + tr + " " + currentState.GetOutState(tr)+" "+CurrentStateID); // Debug.LogError("1234"); // return; //} StateID id = CurrentState.GetOutState(tr); //当前状态会进入的新的状态 currentStateID = id; // Debug.Log("Prives" + Prives.ID); foreach (FMS_State state in states) //通过注册的所有状态,进行搜索来获取要进入的状态实例 { if (currentStateID == state.ID) { CurrentState.DoBeforeMoveing(); //退出状态前,留下点什么,比如挥舞下手臂 currentState = state; CurrentState.DoBeforeEnter(); //进入状态 break; } } } }
using UnityEngine; using System.Collections; using System; using System.Collections.Generic; public class NPC_Control : MonoBehaviour { public void Start() { FMS_Machine_Manage.GetInstance.MakeFMSMachine(); } public void FixedUpdate() { FMS_Machine_Manage.GetInstance.UpdateFunction(); } }