1. 程式人生 > >學習筆記(三) 簡單的狀態模式&FSM有限狀態機框架的實現(二)

學習筆記(三) 簡單的狀態模式&FSM有限狀態機框架的實現(二)

之前釋出的那篇部落格可能說的並是不非常清楚,所以整理了一下,也參考了一些文件,於是又寫了一篇總結。

一、有限狀態機FSM的要點 1、擁有一組狀態,並且可以再這組狀態之間進行切換。 2、狀態機同時只能存在一個狀態,英雄不能能同時處於跳躍和站立。而防止這一點就是使用FSM的理由之一。(類似於動畫狀態機) 3、一連串的輸入或事件被髮送給機器。(這句話的意思就是,在使用的很多的情況下,要使用FSM狀態機) 4、每個狀態都有一系列的轉換,轉換與輸入的另一個狀態相關。當輸入進來時,如果他與當前狀態的某個轉換匹配,那麼機器則轉為所指的狀態。

舉例:在戰力狀態時,按下 下鍵轉換為俯臥狀態。在跳躍時按下 下鍵轉換為跳斬。如果輸入在當前狀態沒有定義轉換,則輸入就被忽視。也就是說,每個狀態機內管理著自己可轉換到的狀態,如果接收的按鍵是無法轉換到的,那麼則無視這個狀態。

5、就目前而言,遊戲程式設計中狀態機的實現方式,有兩種可以選擇。 (1)用列舉配合switch case語句。 (2)用多型與虛擬函式(也就是狀態模式)。

二、使用switch case設計的fsm 1、當擁有一些列的標記成員變數,而他們只能有且僅有一個為True時,定義成列舉更加合適。 (這裡比如說是你定義了一個跳躍的bool來控制跳躍,同時又定義了一個下蹲的bool來控制下蹲,那麼其實這樣會導致衝突或者說是浪費,直接定義一個列舉就好了) 2、使用switch case設計的問題:不易於拓展 在你設計好後,如果你要使用更多的功能的話,則會造成一個無法拓展的問題。往往一個小需求就會造成你需要更改很多原有程式碼,這顯然不是我們想看到的。

三、簡單描述狀態模式與狀態機 1、不要無論青紅皁白就去使用面向物件思維方式中的動態排程(也就是虛擬函式和多型)去解決問題。如果每次都這麼用,可能簡單問題會被複雜化。其實有的時候一個簡單的IF語句就夠用了。

2、描述狀態模式 “Allow an object to alter its behavior whenits internal state changes. The object will appear to change its class. 允許物件在當前內部狀態改變時,改變其行為。就像是這個物件改變了自己的類一樣。

(1)狀態模式主要解決的是當控制一個物件轉換的條件表示式過於複雜的情況,它把狀態的判斷邏輯轉移到表示不同的一系列當中,可以把複雜的邏輯判斷簡單化。

(2)狀態模式的實現要點主要有三點: (2.1)為狀態定義一個介面 (2.2)為每個狀態定義一個類 (2.3)恰當地進行狀態委託

(3)舉例:在時序電路里頭,一個系統往往由一堆狀態組成,從狀態A,通過輸入,跳轉到狀態B。這就是狀態圖。例如下面的圖片:

四、什麼是FSM有限狀態機? 上面這種方法就被稱作“有限狀態機”(FSM,Finite State Machine)。 那究竟什麼是FSM呢? ·FSM是一種資料結構,它由以下幾個部分組成: 1、內在的所有狀態(必須是有限個) //只有有限個狀態,否則也太大了 2、輸入條件 //達成某個條件之後,轉換成某個狀態 3、狀態之間起到連線性作用的轉換函式 //狀態中有切換狀態的轉換函式(傳送一條轉換訊息,由上層接收) ·為什麼要用FSM? 因為他程式設計快速簡單,易於除錯,效能高,與人類思維相似從而便於梳理,靈活且容易修改 ·FSM的描述性定義: 一個有限狀態機是一個裝置,或是一個模型,具有有限數量的狀態。它可以在任何給定時間根據輸入進行操作,使得系統從一個狀態轉換到另一個狀態,或者是使一個輸出或者一種行為的發生,一個有限狀態機在任何瞬間只能處於一種狀態。

·挖掘狀態

例如我們有這樣一個場景。玩家控制一個Hero打怪練級。這個英雄等級不夠沒啥經驗帶上典型的阿Q精神,所以基本上只有這三個動作:

1,平時的狀態是巡邏,就是漫無目的的走。。。

2,如果遇到敵人之後大量一下敵人。

3,如果敵人比自己弱小,那就打。

4,如果敵人比自己強大,那就跑。

我對FSM的基本解釋:一個智慧體,在有規則的時間間隔內詢問現在所掌握的環境資料,使得它能夠基於從遊戲環境中接受到的刺激進行必要的狀態轉換。每一個狀態可以模型化為一個分離的物件,或者存在於智慧體外部的函式。

這樣,FSM提供給了我們一個清楚靈活的結構。

·FSM一般骨架程式碼

一般FSM中需要有以下幾個類作為一種資料框架:

FSMState類:抽象類,表示基本狀態,所有狀態都應該繼承自這個類

FSMMachine類:一臺有限狀態機

FSMAIControl類:存放有限狀態機,通常就是遊戲AI的主迴圈。並且能存放環境感知資料等內容。

這裡有幾個類,需要重點解析

這個方法是在StateSystem中,負責轉換的方法 轉換是由狀態來負責,狀態觸發了條件後,將條件傳遞過來 這個方法可以通過狀態傳遞過來的條件,通過狀態身上的GetPuCutState找到對應的ID 保證這個ID不會出錯 這個好處是,就算轉換的也是轉換為狀態身上自己存的東西 (因為存是自定義存,所以可能造成每個狀態鍵和值對應不上。 比如 (攻擊)狀態 -觸發-【看到了】條件,轉換到-(技能)狀態, 而 (防禦)狀態-觸發-【看到了】條件,轉換到-(逃跑)狀態 )

總結:市面上關於狀態機的介紹很多,這裡筆者只是寫了自己瞭解的一部分,當然,更多的邏輯我還沒有整理。所以以下是我對狀態機的認知與整理。

一、關於 FSM有限狀態機是一種為了使得某事或某人在不同情況(不同狀態)下進行不同行為的解決方案。類似於Unity中自帶的動畫狀態機的模式。 優點:可以很好的解決有多重狀態的角色在不同狀態下的切換。(比如在跳躍狀態下就無法進入衝刺狀態。但是在行走狀態中就可以進入衝刺狀態。因為在跳躍狀態是沒有進入衝刺狀態的選項的) (擴充套件:每個技能的釋放都可以算作是一個狀態【技能狀態】,播放動畫的同時不可進入到其他狀態,只有達成某種條件後進入攻擊或者Idle狀態)

缺點:暫時沒看出來。

二、實現邏輯 這裡還是寫一個小型的敵人AI來舉例 首先列出必須存在的類和列舉:

enum Transition 【轉換條件】
{
LookPlayer,	//看到了player
NearPlayer,	//接近了Player
LosePlayer	//丟失了Player
}

enum StateID  【狀態ID】
{
Attack,	//攻擊
Idle,		//Idle
Chase	//追逐
}


abstract Class StateBase【抽象狀態基類】
{
//當前狀態對應的ID,構造的時候傳遞?只有Get方法,每個狀態唯一對應
StateID mStateID;  【當前狀態對應ID】

//存放當前狀態可以轉換到的狀態
Dictionary<Transition trans,StateID> map;【狀態字典】

//給字典新增轉換條件和與之對應的狀態
void AddTransition(Transition trans,StateID id); 【新增方法】

//通過狀態ID來刪除一個狀態,不用條件刪除的原因是可能某些時候我們只需要刪除掉這個狀態,而不管是哪個條件影響到他的
void RemoveTran(StateID id);【刪除方法】

//這個方法,是配合FSMSystem中切換狀態的方法使用的,在切換的時候呼叫這個方法,可以從狀態身上拿到相對應的StateID
StateID GetPuCutState(Transition trans); 【獲取當前狀態要轉換成的狀態ID】

//用於每幀監聽是否滿足切換行為
abstract void Monitor(GameObject Player); //每幀檢測是否滿足切換狀態

//這裡只負責行為
abstract void  Act(GameObject Player);  //每幀執行程式碼

 //進入後觸發一次 和 退出後觸發一次
virtual void OnEnterState(); 【進入狀態】
virtual void OnExitState();  【退出狀態】
}

//用於管理所有的狀態
Class FSMSystem  【狀態機管理系統】
{
List<StateBase> states;     【存放當前狀態機中管理的所有狀態】
StateBase CurrentState; 【當前狀態】

//新增一個狀態進入集合,同時判斷是否為第一個加入集合的,如果是,那麼就將當前狀態設定為他
void AddState();	【新增一個狀態進入】
void Remove();       【刪除一個狀態(可能會用上,先寫上)】

//轉換方法,這個方法可能比較亂,所以我詳細的寫一下
void ChangeState( Transition trans )
{
//在這裡,首先各種判斷預防錯誤我就先不說了
//首先通過當前狀態獲取StateID 
StateID nextStateID = CurrentState.GetPuCutState(trans);
//取得了ID之後,在集合中找到所對應的狀態
//foreach遍歷管理的所有集合,找到StateID相對應的狀態
//呼叫CurrentState當前狀態的退出方法
//交換CurrentState為相對應的狀態
//呼叫CurrentState的進入方法
//(這裡就簡單的交換並呼叫了一下)
}
}