本人新手,隨便寫寫而已。html
本文經過一個實例實現了在Unity下的有限狀態機(參考了wiki上的教程)。設計模式
有限狀態機是一個設備具備有限數量的狀態,他能夠在任什麼時候間根據輸入進行操做,使得一個狀態進入另個一個狀態。一個狀態機在任何瞬間只能處於一種狀態。app
具體能夠參考 狀態設計模式。ide
本例是這樣一種狀態裝換。post
遊戲人物NPC在空閒時處於巡邏狀態,當看見Player在視野內的時候,轉入轉入追逐Player狀態;一旦和Player距離拉大,便返回巡邏狀態。url
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); }
隨後根據這個基類派生出巡邏類,和追逐類。spa
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(); } }