[涼鞋同學] Unity 游戲框架搭建 (四) 簡易有限狀態機

http://www.manew.com/thread-89637-1-1.html
**為什么用有限狀態機?
**
??之前做過一款跑酷游戲,跑酷角色有很多狀態:跑、跳、二段跳、死亡等等。一開始是使用if/switch來切換狀態,但是每次角色添加一個狀態(提前沒規劃好),所有狀態處理相關的代碼就會指數級增長,那樣就會嗅出代碼的壞味道了。在這種處理狀態并且狀態數量不是特別多的情況下,自然就想到了引入狀態機。
優點:
??1.使代碼整潔,狀態容易擴展和管理。
??2.可復用。
??3.還沒想到.....
缺點:
??1.也沒想到......
**什么是有限狀態機?
**
??解釋不清楚,看了下百度百科。反正是一種數據結構,一個解決問題的工具。
??從百度百科可以看到,有限狀態機最最最基礎的概念有兩個:狀態和轉移。
??從剛才跑酷的例子來講,跑、跳、二段跳等這些就是角色的狀態。
如圖所示:

-----2016-05-08---3-10-32.png

主角從跑狀態切換到跳狀態,從跳狀態切換到二段跳狀態,這里的切換就是指狀態的轉移。狀態的轉移是有條件的,比如主角從跑狀態不可以直接切換到二段跳狀態。但是可以從二段跳狀態切換到跑狀態。
??另外,一個基本的狀態有:進入狀態、退出狀態、接收輸入、轉移狀態等動作。但是僅僅作為跑酷的角色的狀態管理來說,只需要轉移狀態就足夠了。有興趣的同學可以自行擴展。
**如何實現?
**
??恰好之前看到過一個還算簡易的實現(簡易就是指我能看得懂- -,希望大家也是),原版是用lua實現的,我的跑酷游戲是用C#實現的,所以直接貼出C#代碼。

using UnityEngine;
using System.Collections;
using System.Collections.Generic;
public class FSM {
// 定義函數指針類型
public delegate void FSMTranslationCallfunc(); /// <summary>
/// 狀態類
/// </summary>
public class FSMState
{
public string name;

    public FSMState(string name)
    {
        this.name = name;
    }
    /// <summary>
    /// 存儲事件對應的條轉
    /// </summary>
    public Dictionary <string,FSMTranslation> TranslationDict = new Dictionary<string,FSMTranslation>();
}
/// <summary>
/// 跳轉類
/// </summary>
public class FSMTranslation
{
    public FSMState fromState;
    public string name;
    public FSMState toState;
    public FSMTranslationCallfunc callfunc; // 回調函數

    public FSMTranslation(FSMState fromState,string name, FSMState toState,FSMTranslationCallfunc callfunc)
    {
        this.fromState = fromState;
        this.toState   = toState;
        this.name = name;
        this.callfunc = callfunc;
    }
}
// 當前狀態
private FSMState mCurState;

Dictionary <string,FSMState> StateDict = new Dictionary<string,FSMState>();
/// <summary>
/// 添加狀態
/// </summary>
/// <param name="state">State.</param>
public void AddState(FSMState state)
{
    StateDict [state.name] = state;
}
/// <summary>
/// 添加條轉
/// </summary>
/// <param name="translation">Translation.</param>
public void AddTranslation(FSMTranslation translation)
{
    StateDict [translation.fromState.name].TranslationDict [translation.name] = translation;
}
/// <summary>
/// 啟動狀態機
/// </summary>
/// <param name="state">State.</param>
public void Start(FSMState state)
{
    mCurState = state;
}
/// <summary>
/// 處理事件
/// </summary>
/// <param name="name">Name.</param>
public void HandleEvent(string name)
{
    if (mCurState != null && mCurState.TranslationDict.ContainsKey(name)) {
        Debug.LogWarning ("fromState:" + mCurState.name);

        mCurState.TranslationDict [name].callfunc ();
        mCurState = mCurState.TranslationDict [name].toState;


        Debug.LogWarning ("toState:" + mCurState.name);
    }
}

}

測試代碼(需自行修改):

// 創建狀態
FSM.FSMState idleState = new FSM.FSMState("idle"); // Idle, 閑置
FSM.FSMState runState = new FSM.FSMState("run"); // Run, 跑
FSM.FSMState jumpState = new FSM.FSMState("jump"); // Jump, 一段跳
FSM.FSMState doubleJumpState = new FSM.FSMState("double_jump"); // DoubleJump, 二段跳
FSM.FSMState dieState = new FSM.FSMState("die"); // Die, 掛彩
// 創建跳轉
FSM.FSMTranslation touchTranslation1 = new FSM.FSMTranslation(runState,"touch_down",jumpState,Jump);
FSM.FSMTranslation touchTranslation2 = new FSM.FSMTranslation(jumpState,"touch_down",doubleJumpState,DoubleJump);

FSM.FSMTranslation landTranslation1 = new FSM.FSMTranslation(jumpState,"land",runState,Run);
FSM.FSMTranslation landTranslation2 = new FSM.FSMTranslation(doubleJumpState,"land",runState,Run);

// 添加狀態
PlayerModel.Instance ().fsm.AddState (idleState);
PlayerModel.Instance ().fsm.AddState (runState);
PlayerModel.Instance ().fsm.AddState (jumpState);
PlayerModel.Instance ().fsm.AddState (doubleJumpState);
PlayerModel.Instance ().fsm.AddState (dieState);

// 添加跳轉
PlayerModel.Instance ().fsm.AddTranslation (touchTranslation1);
PlayerModel.Instance ().fsm.AddTranslation (touchTranslation2);
PlayerModel.Instance ().fsm.AddTranslation (landTranslation1);
PlayerModel.Instance ().fsm.AddTranslation (landTranslation2);

PlayerModel.Instance ().fsm.Start (runState);[mw_shl_code=csharp,true] // 創建狀態
FSM.FSMState idleState = new FSM.FSMState("idle");
FSM.FSMState runState = new FSM.FSMState("run");
FSM.FSMState jumpState = new FSM.FSMState("jump");
FSM.FSMState doubleJumpState = new FSM.FSMState("double_jump");
FSM.FSMState dieState = new FSM.FSMState("die");
// 創建跳轉
FSM.FSMTranslation touchTranslation1 = new FSM.FSMTranslation(runState,"touch_down",jumpState,Jump);
FSM.FSMTranslation touchTranslation2 = new FSM.FSMTranslation(jumpState,"touch_down",doubleJumpState,DoubleJump);

FSM.FSMTranslation landTranslation1 = new FSM.FSMTranslation(jumpState,"land",runState,Run);
FSM.FSMTranslation landTranslation2 = new FSM.FSMTranslation(doubleJumpState,"land",runState,Run);

// 添加狀態
PlayerModel.Instance ().fsm.AddState (idleState);
PlayerModel.Instance ().fsm.AddState (runState);
PlayerModel.Instance ().fsm.AddState (jumpState);
PlayerModel.Instance ().fsm.AddState (doubleJumpState);
PlayerModel.Instance ().fsm.AddState (dieState);

// 添加跳轉
PlayerModel.Instance ().fsm.AddTranslation (touchTranslation1);
PlayerModel.Instance ().fsm.AddTranslation (touchTranslation2);
PlayerModel.Instance ().fsm.AddTranslation (landTranslation1);
PlayerModel.Instance ().fsm.AddTranslation (landTranslation2);

PlayerModel.Instance ().fsm.Start (runState);

貼上代碼地址址:https://github.com/liangxiegame/QFramework/blob/master/Script/DesignPattern/QFSM.cs
??就這些,想要進一步擴展的話,可以給FSMState類添加EnterCallback和ExitCallback等委托,然后在FSM的HandleEvent方法中進行調用。當時對跑酷的項目來說夠用了,接沒繼續擴展了,我好懶- -,懶的借口是:沒有最好的設計,只有最適合的設計,233333。

附:我的框架地址
轉載請注明地址:涼鞋的筆記

最后編輯于
?著作權歸作者所有,轉載或內容合作請聯系作者
平臺聲明:文章內容(如有圖片或視頻亦包括在內)由作者上傳并發布,文章內容僅代表作者本人觀點,簡書系信息發布平臺,僅提供信息存儲服務。

推薦閱讀更多精彩內容

  • 為什么用有限狀態機? 之前做過一款跑酷游戲,跑酷角色有很多狀態:跑、跳、二段跳、死亡等等。一開始是使用if/swi...
    涼鞋的筆記閱讀 1,163評論 0 3
  • 轉載請注明地址:涼鞋的筆記 為什么用有限狀態機? 之前做過一款跑酷游戲,跑酷角色有很多狀態:跑、跳、二段跳、死亡等...
    光明程輝閱讀 1,124評論 0 1
  • Spring Cloud為開發人員提供了快速構建分布式系統中一些常見模式的工具(例如配置管理,服務發現,斷路器,智...
    卡卡羅2017閱讀 134,981評論 19 139
  • 今天我讀了周老師在QQ上發的一對夫婦帶著一個孩子在飛機上一直打擾日本孩子被主逐出美國都新聞。 ...
    李ran閱讀 203評論 0 1
  • 你是誰,我是誰,何為現實?何為對?何為錯?你知道嗎? 【1】 街邊嘈雜的聲音轉換成了耳邊輪子滾動的聲音,不一會兒,...
    涼羽笙閱讀 1,148評論 15 13