Unity中FSM有限狀態機

什麼是FSM

FSM 即有限狀態機,它是一個狀態管理系統,表示一個對象的幾種狀態在指定條件下轉移行爲,即隨着條件的不斷改變內部狀態不斷地切換。ide

FSM用處或者使用背景

一般使用FSM去實現一些簡單的AI邏輯,對於遊戲中的每一個對象均可以在其生命週期中分出一些狀態,好比一個小兵,他可能在休息,或者巡邏,當敵人出現時,他的狀態可能切換爲追逐敵人或者攻擊敵人,當某些條件成立時,狀態機從當前狀態轉移到下一狀態,在不一樣狀態下有不一樣的任務,因此要使用有限狀態機去實現。this

FSM使用的必要性

當須要實現角色的狀態時以及狀態間的切換時,相信第一時間想到的應該是if-else,可是若是狀態的切換條件表達式過於複雜,if-else就顯得臃腫麻煩了。再加上全部條件的判斷全在一塊兒,狀態一多也容易出現Bug,擴展也很差,使用if-else就會至關臃腫,因此在這種條件下,有限狀態機的使用頗有必要,固然若是相對簡單的狀態切換,使用有限狀態機就不必了,須要根據我的需求。spa

FSM使用注意點

FSM有兩個重要的概念:狀態和轉移,轉移是切換條件,必須有一個初始狀態,並保存當前狀態,以及注意每種狀態的轉移的必要條件。code

FSM優勢

使整個狀態切換邏輯比較清晰,加強代碼的可擴展性,也便於後期維護。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,具體代碼中都有註釋,有理解不到位的地方,望請指正。

相關文章
相關標籤/搜索