有限狀态機的定義
表示有限個狀态以及在這些狀态之間的轉移和動作等行為的數學模型 顧名思義 就是所有的狀态都會在狀态機的集合裡,友善管理。在啟動一個FSM時,首先必須将FSM置于“起始狀态”,然後輸入一系列操作,最終,FSM會到達“結束狀态”
實作一個簡單的狀态機
///定義一個抽象的類,具體的函數在子類中實作,是狀态機的基類
public abstruct class FSMState
{
protected string name;//狀态的名字
public string Name{get=>name;}//name接口 對外界調用
protected FSMSystem fsm;
protracted Dictionary<Transaction , string>statemap=new Dictionary<Transaction , string>();//存放所有狀态的字典 Transaction是轉換條件 等一會實作 string 是名字
///添加轉換條件 首先判斷轉換條件是不是空 如果為空不執行 在判斷字典中是否有這個轉換條件 如果沒有再加入
public void AddTransaction(Transaction transaction,string name)
{
if(transaction==Transaction.NULL||name=="") return;
if(!map.ContainKey(transaction))map.add(transaction,name);
}
//删除轉換條件 思路和插入一樣
public void DestoryTransaction(Transaction transaction)
{
if (transaction == Transaction.NULL) return;
if (map.ContainsKey(transaction)) map.Remove(transaction);
}
///擷取目前狀态 如果字典中有該狀态就傳回他的名字 否則傳回空
public string GetOutState(Transaction transaction)
{
if(map.ContainsKey(transaction) return statemap[transaction];
else return"";
}
///狀态進入之前做
public virtual void DoBeforEntering(){}
///狀态離開之前做
public virtual void DoBeforLeaving(){}
///檢測狀态轉換
public abstract void Reason();
///控制該狀态的行為
public abstract void Act();
}
補充 abstract和virtual的差別(面試經常問)
共同點: 兩個都是為了讓子類對付類重新定義 覆寫父類
差別:1 在父類中virtual修飾的方法必須有實作 而abstract修飾的方法不能有實作 必須用“;”結束
2 virtual在子類中可以被重寫 abstruct必須被重寫
3 如果類中有一個函數被abstract修飾那麼類名也必須用abstract修飾
4 abstract類不能被執行個體化 必須依靠子類
言歸正傳 現在寫管理狀态的類
///使用一個List清單把狀态全部管理
public class FSMSystem
{
private List<FSMState> stateList ;
private FSMState nowState;///目前的狀态
public FSMState NowState { get => nowState; }
public FSMSystem()
{
stateList=new List<FSMState>();
}
///添加狀态
///如果沒有就跳出 如果有則判斷list是不是為空 如果為空就把第一個進入的狀态設為預設的狀态
///如果list不為空那麼就判斷list裡面有沒有這個狀态 如果沒有則加入
public void AddState(FSMState state)
{
if(state==null)return;
else if(stateList.Count==0)
{
stateList.Add(state);
nowstate=state;
return;
}
else if(!stateList.Contains(state))
stateList.Add(state);
}
///删除狀态
public void DestoryState(string stateName)
{
if (stateName == "") return;
foreach(FSMState state in stateList)
{
if(state.Name==stateName)
{
stateList.Remove(state);
break;
}
}
}
///轉換狀态
public void DoTransaction (Transaction transaction)
{
if(transaction==Transaction.NULL)
return;
string newstateName=nowState.GetOutState(transaction);
if(newstateName=="")return;
else
{
foreach(FSMState state in stateList)
{
if(state.Name==newstateName)
{
nowState.DoBeforLeaving();
nowstate=state;
nowState.DoBeforEntering();
retrun;
}
}
}
}
}
實作Transaction (狀态)
public enum Transaction
{
NULL,
MOVE,
IDEL
}
這樣架構就完整實作了 現在我來寫個小Demo來展示這個架構的用法
MoveState(移動狀态)
public class MoveState:FSMState()
{
Transform movetrans;
public MoveState(Transform target,FSMSystem f)
{
this.name="Move";
this.movetrans=target;
this.fsm=f;
}
///重寫這個狀态的要做的事情
public override void Act()
{
Debug.Log("移動了!");
}
///切換狀态
public override void Reason()
{
if (Input.GetKeyDown(KeyCode.W))
fsm.DoTransaction(Transaction.TO_IDEL);
}
}
靜止狀态
public class idelState :FSMState
{
public IdelState(FSMSystem f)
{
this.name="Idel";
this.fsm=f;
}
public override void Act()
{
Debug.Log("靜止");
}
public override void Reason()
{
if(Input.GetKeyDown(KeyCode.Space))
fsm.DoTransaction(Transaction.TO_MOVE);
}
}
玩家或敵人代碼
public class SleepControl:MonoBehaviour
{
private FSMSystem fsm;
public Transform target;
///進行初始化操作
private void MakeFSM()
{
fsm=new FSMSystem();
IdelState idel=new IdelState(fsm);
idel.AddTransaction(Transaction.MOVE,"Move");
MoveState move=new MoveState(target,fsm);
move.AddTransaction(Transaction.IDEL,"Idel");
fsm.AddState(idel);
fsm.AddState(move);
}
private void Awake()
{
MakeFSM();
}
private void Update()
{
fsm.nowState.Act();
fsm.nowState.Reason();
}
}
這樣就可以按空格和W切換不同的狀态了,如果要用自己想要在某個狀态要做的事就可以直接在Act函數中調用你想要的操作的函數接口即可