有限狀态機是我們遊戲程式中非常常用的一種設計模式。不止遊戲,在AI和編譯器程式方面很出名。
什麼是有限狀态機?
有限狀态機是表示有限個狀态以及在這些狀态之間的過渡和動作等行為的數學計算模型[^1]。(數學計算模型這個詞要是覺得難了解,在腦海中換成結構即可。)
有限狀态機的元素
a.狀态(status)
b.進入動作(entry action):在進入狀态時進行
c.退出動作(exit action):在退出狀态時進行
d.過渡動作(transition action)在進行特定過渡時進行
寫一個簡單版本的有限狀态機的例子
public class FSMMachine<T>
{
private string fsmName;
private bool running = false;
Dictionary<T,StateBehaviour> stateBehaviourDic = new Dictionary<T, StateBehaviour>();
public T DefaultState{get; set;} = default(T);
private T currentState;
public T CurrentState
{
get
{
return currentState;
}
set
{
ChangeStatus(value);
}
}
//構造函數中添加預設狀态
public FSMMachine(string fsmName)
{
this.fsmName = fsmName;
}
//添加狀态
public void AddStatus(T state,StateBehaviour stateBehaviour)
{
stateBehaviourDic[state] = stateBehaviour;
}
//移除狀态
public void RemoveStatus(T state)
{
if(!stateBehaviourDic.ContainsKey(state))
return;
stateBehaviourDic.Remove(state);
}
//改變狀态
public void ChangeStatus(T state)
{
if(!stateBehaviourDic.ContainsKey(state)||stateBehaviourDic[state] == null)
throw new InvalidOperationException(string.Format("[FSM {0}] : Can't call 'ChangeStatus' before the stateBehaviour of state has been settled.",fsmName));
stateBehaviourDic[currentState].OnLeave();
stateBehaviourDic[state].OnEnter();
currentState = state;
}
public void Run()
{
if(!stateBehaviourDic.ContainsKey(DefaultState)||stateBehaviourDic[DefaultState] == null)
throw new InvalidOperationException(string.Format("[FSM {0}] : Can't call 'ChangeStatus' before the stateBehaviour of state has been settled.",fsmName));
stateBehaviourDic[DefaultState].OnEnter();
currentState = DefaultState;
running = true;
}
public void Stop()
{
if(!stateBehaviourDic.ContainsKey(currentState)||stateBehaviourDic[currentState] == null)
throw new InvalidOperationException(string.Format("[FSM {0}] : Can't call 'ChangeStatus' before the stateBehaviour of state has been settled.",fsmName));
stateBehaviourDic[currentState].OnLeave();
running = false;
}
}
public class StateBehaviour
{
public virtual void OnEnter()
{
}
public virtual void OnLeave()
{
}
}
//例子:
public enum StateExample
{
LightOffOn,
LightColorSwitch
}
public class FSMExample : MonoBehaviour
{
FSMMachine<StateExample> fSMMachine;
// Use this for initialization
void Start () {
fSMMachine = new FSMMachine<StateExample>("Example");
fSMMachine.DefaultState = StateExample.LightOffOn;
fSMMachine.AddStatus(StateExample.LightOffOn, new LightingSwitchOffState());
fSMMachine.AddStatus(StateExample.LightColorSwitch, new LightingColorChangeState(Color.red));
fSMMachine.Run();
StartCoroutine(ChangeState());
}
IEnumerator ChangeState()
{
yield return new WaitForSeconds(2f);
fSMMachine.CurrentState = StateExample.LightColorSwitch;
}
}
public class LightingColorChangeState : StateBehaviour {
private Color targetColor;
private Color oriColor;
private Light light;
public LightingColorChangeState(Color TargetColor)
{
targetColor = TargetColor;
}
public override void OnEnter()
{
base.OnEnter();
light = GameObject.FindObjectOfType<Light>();
oriColor = light.color;//記錄原來的顔色
light.color = targetColor;
}
public override void OnLeave()
{
base.OnEnter();
light.color = targetColor;//恢複顔色
}
}
public class LightingSwitchOffState : StateBehaviour {
private Light light;
public override void OnEnter()
{
base.OnEnter();
light = GameObject.FindObjectOfType<Light>();
light.enabled = false;
}
public override void OnLeave()
{
base.OnEnter();
light.enabled = true;
}
}
這個簡單版本的有限狀态機,已經基本完成了。而且可以滿足很多狀況下的需求了。 但是,對有一定經驗的程式員來說,這是遠遠不夠的。因為,例子中的FSM是沒有把
轉換(Transition)這個概念給抽象出來的。 這樣,我們的轉換方向是任意的,且轉換的條件永遠是以來一個參數的不同值,這個值就是CurrentState。
狀态機除了基礎的有限狀态機,還有層次狀态機和下推狀态機。(遊戲程式設計模式這本書裡有介紹,有作者自己維護的網頁版和中國人翻譯的網頁版)
下一篇,我們會實作一個更加完整,擴充性更高的FSM。
Potro:有限狀态機(finite-state machine)(二)zhuanlan.zhihu.com
Github倉庫網址:https://github.com/PotroChen/FSM-Unity3d
參考:
有限狀态機wiki百科
遊戲程式設計模式
[^1]: 數學計算模型:這個詞聽上去好像學術,會讓人感覺很難其實并沒有。 簡單來講,數學計算模型是一種用于描述某種結構的具體組織方式的東西。隻要這種結構能根據一組輸入值,得到輸出值。是以,這個聽上去很高大上很學術的詞,本身就是一個抽象的詞用于描述一類東西的,我們不用太在意的。有限狀态機是我們遊戲程式中非常常用的一種設計模式。不止遊戲,在AI和編譯器程式方面很出名。