## 為什么用有限狀態機?
??之前做過一款跑酷游戲,跑酷角色有很多狀態:跑、跳、二段跳、死亡等等。一開始是使用if/switch來切換狀態,但是每次角色添加一個狀態(提前沒規劃好),所有狀態處理相關的代碼就會指數級增長,那樣就會嗅出代碼的壞味道了。在這種處理狀態并且狀態數量不是特別多的情況下,自然就想到了引入狀態機。
優點:
??1. 使代碼整潔,狀態容易擴展和管理。
??2. 可復用。
??3. 還沒想到.....
缺點:
??1. 也沒想到......
## 什么是有限狀態機?
解釋不清楚,看了下百度百科。反正是一種數據結構,一個解決問題的工具。
從百度百科可以看到,有限狀態機最最最基礎的概念有兩個:狀態和轉移。
從剛才跑酷的例子來講,跑、跳、二段跳等這些就是角色的狀態。
如圖所示:
 ?
主角從跑狀態切換到跳狀態,從跳狀態切換到二段跳狀態,這里的切換就是指狀態的轉移。狀態的轉移是有條件的,比如主角從跑狀態不可以直接切換到二段跳狀態。但是可以從二段跳狀態切換到跑狀態。<br>
??另外,一個基本的狀態有:進入狀態、退出狀態、接收輸入、轉移狀態等動作。但是僅僅作為跑酷的角色的狀態管理來說,只需要轉移狀態就足夠了。有興趣的同學可以自行擴展。
## 如何實現?
??恰好之前看到過一個還算簡易的實現(簡易就是指我能看得懂- -,希望大家也是),原版是用lua實現的,我的跑酷游戲是用C#實現的,所以直接貼出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);
}
}
}
```
測試代碼(需自行修改):
```C#
// Idle, 閑置
// Run, 跑
// Jump, 一段跳
// DoubleJump, 二段跳
// Die, 掛彩
// 創建狀態
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);
```
??就這些,想要進一步擴展的話,可以給 FSMState 類添加 EnterCallback 和 ExitCallback 等委托,然后在 FSM 的 HandleEvent 方法中進行調用。當時對跑酷的項目來說夠用了,接沒繼續擴展了,我好懶- -,懶的借口是:沒有最好的設計,只有最適合的設計,233333。
此篇的內容就這些。
轉載請注明地址:涼鞋的筆記:[liangxiegame.com](http://liangxiegame.com)
## 更多內容
* QFramework 地址:[https://github.com/liangxiegame/QFramework](https://github.com/liangxiegame/QFramework)
* QQ 交流群:[623597263](http://shang.qq.com/wpa/qunwpa?idkey=706b8eef0fff3fe4be9ce27c8702ad7d8cc1bceabe3b7c0430ec9559b3a9ce66)
* **Unity 進階小班**:
* 主要訓練內容:
* 框架搭建訓練(第一年)
* 跟著案例學 Shader(第一年)
* 副業的孵化(第二年、第三年)
* 權益、授課形式等具體詳情請查看[《小班產品手冊》](https://liangxiegame.com/master/intro):https://liangxiegame.com/master/intro
* 關注公眾號:liangxiegame 獲取第一時間更新通知及更多的免費內容。

- 正文
- Unity 游戲框架搭建 2017(一)概述
- Unity 游戲框架搭建 2017(二)單例的模板
- Unity 游戲框架搭建 2017(三)MonoBehaviour 單例的模板
- Unity 游戲框架搭建 2017(四)簡易有限狀態機
- Unity 游戲框架搭建 2017(五)簡易消息機制
- Unity 游戲框架搭建 2017 (六) 關于框架的一些好文和一些思考
- Unity 游戲框架搭建 2017 (七) 減少加班利器-QApp類
- Unity 游戲框架搭建 2017 (八) 減少加班利器-QLog
- Unity 游戲框架搭建 2017 (九) 減少加班利器-QConsole
- Unity 游戲框架搭建 2017 (十) QFramework v0.0.2小結
- Unity 游戲框架搭建 2017 (十一) 簡易 AssetBundle 打包工具 (一)
- Unity 游戲框架搭建 2017 (十二) 簡易 AssetBundle 打包工具 (二)
- Unity 游戲框架搭建 2017 (十三) 無需繼承的單例的模板
- Unity 游戲框架搭建 2017 (十四) 優雅的 QSingleton (零) QuickStart
- Unity 游戲框架搭建 2017 (十四) 優雅的 QSingleton (一) Singleton 單例實現
- Unity 游戲框架搭建 2017 (十四) 優雅的 QSingleton (二) MonoSingleton單例實現
- Unity 游戲框架搭建 2017 (十四) 優雅的 QSignleton (三) 通過屬性器實現 Singleton
- Unity 游戲框架搭建 2017 (十四) 優雅的 QSingleton (四) 屬性器實現 Mono 單例
- Unity 游戲框架搭建 2017 (十四) 優雅的 QSingleton (五) 優雅地進行GameObject命名
- Unity 游戲框架搭建 2017 (十五) 優雅的 QChain (零)
- Unity 游戲框架搭建 2017 (十六) v0.0.3 架構調整
- Unity 游戲框架搭建 2017 (十七) 靜態擴展GameObject 實現鏈式編程
- Unity 游戲框架搭建 2017 (十八) 靜態擴展 + 泛型實現 transform 的鏈式編程
- Unity 游戲框架搭建 2017 (十九) 簡易對象池
- Unity 游戲框架搭建 2017 (二十) 安全的對象池
- Unity 游戲框架搭建 2017 (二十一) 使用對象池時的一些細節
- Unity 游戲框架搭建 2017 (二十二) 簡易引用計數器
- Unity 游戲框架搭建 2017 (二十三) 重構小工具 Platform
- Unity 游戲框架搭建 2017 (二十四) 小結