FSM 即有限狀態機,它是一個狀態管理系統,表示一個對象的幾種狀態在指定條件下轉移行爲,即隨着條件的不斷改變內部狀態不斷地切換。ide
一般使用FSM去實現一些簡單的AI邏輯,對於遊戲中的每一個對象均可以在其生命週期中分出一些狀態,好比一個小兵,他可能在休息,或者巡邏,當敵人出現時,他的狀態可能切換爲追逐敵人或者攻擊敵人,當某些條件成立時,狀態機從當前狀態轉移到下一狀態,在不一樣狀態下有不一樣的任務,因此要使用有限狀態機去實現。this
當須要實現角色的狀態時以及狀態間的切換時,相信第一時間想到的應該是if-else,可是若是狀態的切換條件表達式過於複雜,if-else就顯得臃腫麻煩了。再加上全部條件的判斷全在一塊兒,狀態一多也容易出現Bug,擴展也很差,使用if-else就會至關臃腫,因此在這種條件下,有限狀態機的使用頗有必要,固然若是相對簡單的狀態切換,使用有限狀態機就不必了,須要根據我的需求。spa
FSM有兩個重要的概念:狀態和轉移,轉移是切換條件,必須有一個初始狀態,並保存當前狀態,以及注意每種狀態的轉移的必要條件。code
使整個狀態切換邏輯比較清晰,加強代碼的可擴展性,也便於後期維護。orm
下面就實現一下FSM,使用怪物AI的例子對象
1.建立狀態基類FSMstate ,此類負責處理一個狀態的週期,狀態的進入前,狀態中,離開狀態等。以及狀態切換條件的增刪。具體以下:blog
1 using System.Collections; 2 using System.Collections.Generic; 3 using UnityEngine; 4 /// <summary> 5 /// 狀態ID 6 /// </summary> 7 public enum StateID 8 { 9 NoneStateID, 10 Parol,//巡邏狀態 11 Chase,//追逐狀態 12 } 13 /// <summary> 14 /// 狀態切換條件 15 /// </summary> 16 public enum Transition 17 { 18 NoneTransition, 19 SeePlayer,//看到玩家 20 LosePlayer,//看不到玩家 21 } 22 23 /// <summary> 24 /// FSM中狀態基類(實現狀態的基本方法) 25 /// </summary> 26 public abstract class FSMstate { 27 28 protected StateID stateID;//狀態對應的ID 29 public StateID ID { get { return stateID; } } 30 protected Dictionary<Transition, StateID> Transition_StateIDDic = new Dictionary<Transition, StateID>();//存儲轉換條件和狀態的ID 31 protected FSMsystem fsmSystem;//管理狀態對象(由於要狀態更新須要經過FSMsystem去管理實現的,因此須要一個管理對象) 32 33 public FSMstate(FSMsystem fsm) 34 { 35 this.fsmSystem = fsm; 36 } 37 /// <summary> 38 ///增長轉條件 39 /// </summary> 40 /// <param name="trans"></param> 41 /// <param name="id"></param> 42 public void AddTransition(Transition trans,StateID id) 43 { 44 if (trans==Transition.NoneTransition) 45 { 46 Debug.Log("添加的轉換條件不能爲null"); 47 return; 48 } 49 if (id==StateID.NoneStateID) 50 { 51 Debug.Log("添加的狀態ID不能爲null"); 52 return; 53 } 54 if (Transition_StateIDDic.ContainsKey(trans)) 55 { 56 Debug.Log("添加轉換條件的時候," + trans + "已經存在於Transition_StateIDDic中"); 57 return; 58 } 59 Transition_StateIDDic.Add(trans,id); 60 } 61 /// <summary> 62 /// 刪除轉換條件 63 /// </summary> 64 /// <param name="trans"></param> 65 public void DeleteTransition(Transition trans) 66 { 67 if (trans == Transition.NoneTransition) 68 { 69 Debug.Log("刪除的轉換條件不能爲null"); 70 return; 71 } 72 if (!Transition_StateIDDic.ContainsKey(trans)) 73 { 74 Debug.Log("刪除轉換條件的時候," + trans + "不存在於Transition_StateIDDic中"); 75 return; 76 } 77 Transition_StateIDDic.Remove(trans); 78 } 79 /// <summary> 80 /// 根據轉換條件得到狀態ID 81 /// </summary> 82 /// <param name="trans"></param> 83 /// <returns></returns> 84 public StateID GetStateID(Transition trans) 85 { 86 if (Transition_StateIDDic.ContainsKey(trans)) 87 { 88 return Transition_StateIDDic[trans]; 89 } 90 return StateID.NoneStateID; 91 } 92 93 /// <summary> 94 ///轉換到此狀態前要執行的邏輯 95 /// </summary> 96 public virtual void DoBeforeEnterAcion() { } 97 /// <summary> 98 /// 離開此狀態前要執行的邏輯 99 /// </summary> 100 public virtual void DoAfterLevAction(){ } 101 /// <summary> 102 /// 處在本狀態時要執行的邏輯 103 /// </summary> 104 /// <param name="TargetObj"></param> 105 public abstract void CurrStateAction(GameObject TargetObj); 106 /// <summary> 107 /// 切換到下一狀態須要執行的邏輯 108 /// </summary> 109 /// <param name="TargetObj"></param> 110 public abstract void NextStateAction(GameObject TargetObj); 111 112 113 }
2. 建立狀態管理類FSMSystem的建立。用來管理全部的狀態(狀態的添加,刪除,切換,更新等)。生命週期
1 using System.Collections; 2 using System.Collections.Generic; 3 using UnityEngine; 4 /// <summary> 5 /// 狀態處理類(添加,刪除,切換,更新等管理全部狀態) 6 /// </summary> 7 public class FSMsystem { 8 9 private Dictionary<StateID, FSMstate> StateDic = new Dictionary<StateID, FSMstate>();//保存狀態ID以及ID對應的狀態 10 private StateID _CurrentStateID;//當前處於的狀態ID 11 private FSMstate _CurrentState;//當前處於的狀態 12 13 /// <summary> 14 /// 添加狀態 15 /// </summary> 16 /// <param name="state">需管理的狀態</param> 17 public void AddState(FSMstate state) { 18 if (state==null) 19 { 20 Debug.Log("添加的狀態"+state+"不能爲null"); 21 return; 22 } 23 if (_CurrentState==null) 24 { 25 _CurrentState = state; 26 _CurrentStateID = state.ID; 27 } 28 if (StateDic.ContainsKey(state.ID)) 29 { 30 Debug.Log("狀態機 "+state.ID+"已經存在,沒法添加"); 31 return; 32 } 33 StateDic.Add(state.ID,state); 34 } 35 /// <summary> 36 /// 刪除狀態 37 /// </summary> 38 /// <param name="stateID">刪除要管理狀態的ID</param> 39 public void DeleteState(StateID stateID) 40 { 41 if (stateID==StateID.NoneStateID) 42 { 43 Debug.Log("沒法刪除Null的狀態"); 44 return; 45 } 46 if (!StateDic.ContainsKey(stateID) ) 47 { 48 Debug.Log("沒法刪除不存在的狀態:" + stateID); 49 return; 50 } 51 StateDic.Remove(stateID); 52 } 53 /// <summary> 54 /// 狀態轉換(狀態的切換是根據轉換條件的變化) 55 /// </summary> 56 /// <param name="trans">轉換條件</param> 57 public void PerformTranstion(Transition trans) 58 { 59 if (trans == Transition.NoneTransition) 60 { 61 Debug.Log("沒法執行NULL的轉換條件"); 62 return; 63 } 64 StateID stateId = _CurrentState.GetStateID(trans); 65 if (stateId==StateID.NoneStateID) 66 { 67 Debug.Log("要轉換的狀態ID爲null"); 68 return; 69 } 70 if (!StateDic.ContainsKey(stateId)) 71 { 72 Debug.Log("狀態機中沒找到狀態ID "+stateId+" 沒法轉換狀態"); 73 return; 74 } 75 FSMstate state = StateDic[stateId];//根據狀態ID獲取要轉換的狀態 76 _CurrentState.DoAfterLevAction();//執行離開上一狀態邏輯 77 _CurrentState = state;//更新當前狀態 78 _CurrentStateID = stateId;//更新當前狀態ID 79 _CurrentState.DoBeforeEnterAcion();//執行進入當前狀態前要執行的邏輯 80 } 81 82 /// <summary> 83 /// 更新當前狀態行爲 84 /// </summary> 85 /// <param name="TargetObj"></param> 86 public void UpdateState(GameObject TargetObj) 87 { 88 _CurrentState.CurrStateAction(TargetObj); 89 _CurrentState.NextStateAction(TargetObj); 90 } 91 92 }
3.那咱們用怪物巡邏,追逐玩家的例子來實現狀態機。遊戲
3.1. 增長一個PartalState類,用怪物的巡邏。ci
1 using System.Collections; 2 using System.Collections.Generic; 3 using UnityEngine; 4 5 /// <summary> 6 /// 怪物巡邏狀態類 7 /// </summary> 8 public class PatrolState : FSMstate { 9 private List<Transform> path = new List<Transform>();//巡邏點 10 private int index = 0; 11 private Transform PlayerTrasform; 12 13 /// <summary> 14 /// 初始化巡邏狀態數據 15 /// </summary> 16 /// <param name="fsm"></param> 17 public PatrolState(FSMsystem fsm):base(fsm) 18 { 19 stateID = StateID.Parol; 20 //路點 21 Transform pathTransform = GameObject.Find("Path").transform; 22 Transform[] children = pathTransform.GetComponentsInChildren<Transform>(); 23 foreach (Transform child in children) 24 { 25 if (child != pathTransform) 26 { 27 path.Add(child); 28 } 29 } 30 PlayerTrasform = GameObject.Find("Player").transform; 31 } 32 33 /// <summary> 34 /// 當前狀態(巡邏狀態)執行的邏輯 35 /// </summary> 36 /// <param name="TargetObj"></param> 37 public override void CurrStateAction(GameObject TargetObj) 38 { 39 TargetObj.transform.LookAt(path[index].position); 40 TargetObj.transform.Translate(Vector3.forward * Time.deltaTime * 3); 41 if (Vector3.Distance(TargetObj.transform.position, path[index].position) < 1) 42 { 43 index++; 44 index %= path.Count; 45 } 46 } 47 /// <summary> 48 /// 切換到追逐狀態(下一狀態)執行的的邏輯 49 /// </summary> 50 /// <param name="TargetObj"></param> 51 public override void NextStateAction(GameObject TargetObj) 52 { 53 if (Vector3.Distance(PlayerTrasform.position, TargetObj.transform.position) < 3) 54 { 55 fsmSystem.PerformTranstion(Transition.SeePlayer); 56 } 57 } 58 59 60 }
3.2.增長一個ChaseState類,用怪物的追逐。
1 using System; 2 using System.Collections; 3 using System.Collections.Generic; 4 using UnityEngine; 5 6 /// <summary> 7 /// 追逐狀態類 8 /// </summary> 9 public class ChaseState : FSMstate { 10 11 private Transform PlayerTransForm;//玩家位置信息 12 13 public ChaseState(FSMsystem fsm):base(fsm) 14 { 15 stateID = StateID.Chase; 16 PlayerTransForm = GameObject.Find("Player").transform; 17 } 18 /// <summary> 19 /// 追逐狀態下執行的邏輯 20 /// </summary> 21 /// <param name="targrtObj"></param> 22 public override void CurrStateAction(GameObject targrtObj) 23 { 24 targrtObj.transform.LookAt(PlayerTransForm.transform.position); 25 targrtObj.transform.Translate(Vector3.forward*2*Time.deltaTime); 26 } 27 /// <summary> 28 /// 切換到下一狀態(巡邏)前要執行的邏輯 29 /// </summary> 30 /// <param name="targrtObj"></param> 31 public override void NextStateAction(GameObject targrtObj) 32 { 33 if (Vector3.Distance(PlayerTransForm.position,targrtObj.transform.position)>6) 34 { 35 fsmSystem.PerformTranstion(Transition.LosePlayer); 36 } 37 } 38 }
3.3 建立一個控制怪物的腳本Enemy
1 using System.Collections; 2 using System.Collections.Generic; 3 using UnityEngine; 4 /// <summary> 5 /// 怪物類 6 /// </summary> 7 public class Enemy : MonoBehaviour { 8 9 private FSMsystem fsmsystem; //在Enemy類中,實例化FSMSystem對象,添加巡邏和追逐狀態,還有之間的轉換條件 10 void Start () { 11 InitFsm();//建立狀態機 12 } 13 14 void Update () { 15 fsmsystem.UpdateState(this.gameObject);//檢查更新狀態 16 } 17 /// <summary> 18 /// 建立狀態機 19 /// 怪物有兩種狀態分別是巡邏和追逐玩家 20 /// 若是怪物初始狀態(設置爲Parol狀態)一旦SeePlayer 切換狀態被激活後,就切換到Chase狀態 21 /// 若是他在Chase狀態一旦LosePlayer狀態被激活了,它就轉變到Parol狀態 22 /// </summary> 23 void InitFsm() 24 { 25 fsmsystem = new FSMsystem(); 26 FSMstate PatrolState = new PatrolState(fsmsystem); 27 PatrolState.AddTransition(Transition.SeePlayer, StateID.Chase); 28 FSMstate ChaseState = new ChaseState(fsmsystem); 29 ChaseState.AddTransition(Transition.LosePlayer, StateID.Parol); 30 fsmsystem.AddState(PatrolState);//初始狀態 31 fsmsystem.AddState(ChaseState); 32 } 33 }
以上就用FSM有限狀態機實現了怪物AI,具體代碼中都有註釋,有理解不到位的地方,望請指正。