什么是FSM
FSM(Finite State Machine),無限狀態機。一個狀態機就是一個設備,具有有限的狀態數量,在任何給定的時間根據輸入和自己預先定好的狀態轉換規則,從一個狀態跳轉到另一個狀態。一個有限狀態機在同一時間只能處於某一個狀態。
- 一個簡單的例子:一個燈泡的開關就是一個狀態機。該狀態機有開狀態(亮)和關狀態(暗),當該狀態機接受關閉輸入的時候,燈泡從開狀態轉換到關狀態,也就是燈泡會滅掉。當該狀態機接受打開輸入的時候,燈泡從關狀態轉換到開狀態,也就是說燈泡會變亮。
從基類State說起
基類state可以是一個抽象函數也可以是一個純虛函數,定義了四個行為方法:
entity:表示一個具有若干個狀態的實體,比如npc在逃跑,ncp對應實體,逃跑對應狀態。
- IntoState(T entity):
- 當進入該狀態時調用的方法,只調用一次。
- 比如,要實現npc進入<逃跑>狀態時,會尖叫一聲,那么尖叫這個動作就可以在IntoState()方法里面實現
- Execute(T entity):
-
- 當進入該狀態后每一幀都會調用的方法,該狀態一般用來實現狀態的跳轉邏輯或者待在這個狀態的持續時間的計數。
- 比如,npc處於<逃跑>狀態時,逃到安全位置時,npc會跳轉到<Idle>狀態,這個邏輯可以在Execute()實現,if(沒有威脅){切換狀態到Idle;}
-
- Exit(T entity):
-
- 當從該狀態切換到另一個狀態之前,會調用該函數,只調用一次。
- 比如,想要實現npc從<逃跑>狀態切換到<Idle>狀態的時候,會做出一個擦冷汗的動作,可以在Exit()方法里面實現。
-
- OnMessage(T entity, Telegram msg):
-
- 當實體(entity)處於該狀態時,剛好有消息(Telegram)發到該entity,會調用該方法。
- 比如,想要實現npc處於<逃跑>狀態時,如果這時給npc發送一條摔倒的消息時,npc會感到絕望,並說“我要完蛋了”,可以在OnMessage()里面實現。總的來說,該方法可以實現實體(entity)處於某狀態對某條特定的消息進行特殊的處理。
-
下面貼出基類state的代碼
1 /// <summary> 2 /// 狀態類的基類 3 /// 所有的角色狀態都繼承於它 4 /// </summary> 5 public class State<T> { 6 7 /// <summary> 8 /// 進入改狀態時,所做的初始化動作 9 /// </summary> 10 /// <param name="entity"></param> 11 public virtual void IntoState(T entity) { } 12 13 /// <summary> 14 /// 處於該狀態時,要做的游戲邏輯 15 /// </summary> 16 /// <param name="entity"></param> 17 public virtual void Execute(T entity){} 18 19 /// <summary> 20 /// 退出該狀態時,做的收尾工作 21 /// </summary> 22 /// <param name="entity"></param> 23 public virtual void Exit(T entity) { } 24 25 /// <summary> 26 /// 獲取消息,並對消息進行處理 27 /// </summary> 28 /// <param name="entity"></param> 29 /// <param name="msg"></param> 30 /// <returns></returns> 31 public virtual bool OnMessage(T entity, Telegram msg) 32 { 33 return true; 34 } 35 36 }
Telegram類表示一條消息體,具體的成員和說明如下:
- int Sender:發送消息的實體(entity)的id
- int Receiver:處理消息的實體(entity)的id
- MessageTypes msg:一個enum類型的消息類型,實體(entity)根據不同的消息類型進行不同的處理
- float DispatchTime:時間戳,當時間到達該時間戳,消息才會分發出去
- object ExtraInfo:消息附帶的額外信息,這個可以根據需求自定義,也可以為null
1 public class Telegram { 2 //發送消息的實體(entity)的id 3 public int Sender; 4 //處理消息的實體(entity)的id 5 public int Receiver; 6 //一個enum類型的消息類型,實體(entity)根據不同的消息類型進行不同的處理 7 public MessageTypes Msg; 8 //時間戳,當時間到達該時間戳,消息才會分發出去 9 public float DispatchTime; 10 //消息附帶的額外信息,這個可以根據需求自定義,也可以為null 11 public object ExtraInfo; 12 13 public Telegram() { } 14 public Telegram(MessageTypes msg, int s_id, int r_id, float send_time, object info) 15 { 16 Msg = msg; 17 Sender = s_id; 18 Receiver = r_id; 19 DispatchTime = send_time; 20 ExtraInfo = info; 21 } 22 } 23 24 //Telegram的比較規則類,消息隊列里的Telegram按照時間戳進行排序 25 public class TelegramCompare : IComparer<Telegram> 26 { 27 public int Compare(Telegram a, Telegram b) 28 { 29 return a.DispatchTime.CompareTo(b.DispatchTime); 30 } 31 } 32 33 //以下是我自定義的一些消息類型 34 public enum MessageTypes 35 { 36 Msg_Shooted, // 被子彈擊中 37 Msg_HeroHurt, 38 Msg_EnemeyAnimationHurt, 39 Msg_PlayerNormalAttack,//玩家普通攻擊 40 Msg_PlayerSkillHurt, //玩家技能一傷害 41 Msg_PlayerAddHp, // 使用葯品 42 Msg_PlayerAddMp, 43 Msg_KnockBack, // 震退信息 44 // Msg_PlayerSkillHurt_2, //玩家技能二傷害 45 Msg_UseNuQi, //使用怒氣 46 Msg_EnemyDie // 敵人死亡 47 }
既然是發送消息,那負責發送消息的類是誰?答案是MessageDispatcher單例類,后面會詳解這個類的原理,先看一行發送消息的代碼。
public class MessageDisPatcher { .................................... /// <summary> /// msg: 消息類型 /// senderId: 發送者ID /// receiverId: 接受者ID /// delay: 延遲時間,例如,delay = 2 表示延遲 2 秒再發送 /// info: 該消息所附帶的額外信息,可以是任何類型 /// </summary> public void DispatchMesssage(MessageTypes msg, int senderId, int receiverId, float delay, object info) { ........................... } .................................. }
//npc給自己發一條逃跑的消息,額外信息為null MessageDisPatcher.Instance.DisPatchMessage(Run_Away, npc, npc, 0f, null);