前言
眾所周知,Unity在開發游戲的時候經常用到狀態機,而通常情況下,我們會使用簡單的枚舉類型設置狀態,然后通過調整枚舉,來切換狀態,在簡單情況下是可以的,就像是Unity中Animator切換動畫狀態就是,直接通過動畫片段的名稱切換。而當狀態很多的時候,直接切換就顯得有些很不安全,而且友好性也不是很好,所以根據Unity中Animator這種通過條件切換狀態的架構,我寫了一個簡單的狀態機,給大家分享。
外部委托定義
/// <summary>
/// 狀態內部事件委托
/// </summary>
public delegate void StateFunctionEventHandler();
/// <summary>
/// 狀態條件判斷委托
/// </summary>
public delegate bool StateTranslateEventHandler();
狀態類
/// <summary>
/// 狀態類
/// </summary>
public class State {
/// <summary>
/// 狀態內部事件
/// </summary>
public event StateFunctionEventHandler stateFunc;
/// <summary>
/// 狀態轉換事件<目標狀態,切換條件>
/// </summary>
public Dictionary<State,StateTranslateEventHandler> stateTrs;
/// <summary>
/// 狀態名稱
/// </summary>
/// <value>The name of the state.</value>
public string stateName{get;private set;}
/// <summary>
/// 狀態運行中
/// </summary>
public bool isRun = false;
/// <summary>
/// 狀態內部事件執行間隔時間(毫秒)
/// </summary>
/// <value>The state function delta time.</value>
public int stateFunctionDeltaTime{private get; set;}
/// <summary>
/// 狀態構造
/// </summary>
/// <param name="name">Name.</param>
public State(string name)
{
stateName = name;
stateTrs = new Dictionary<State, StateTranslateEventHandler> ();
stateFunctionDeltaTime = 200;
Thread th = new Thread (Update);
th.Start ();
}
/// <summary>
/// 狀態內部事件執行
/// </summary>
public void Update()
{
while(true)
{
if(!isRun)
{
continue;
}
Thread.Sleep (stateFunctionDeltaTime);
if(stateFunc != null)
{
stateFunc();
}
}
}
/// <summary>
/// 注冊切換事件.
/// </summary>
/// <param name="target">目標狀態.</param>
/// <param name="conditional">條件事件.</param>
public void RegisterTranslate(State target, StateTranslateEventHandler conditional)
{
try
{
stateTrs.Add (target, conditional);
}
catch(Exception ex)
{
Debug.LogError(ex.ToString());
}
}
}
狀態管理類
/// <summary>
/// 狀態管理
/// </summary>
public class StateController : State {
/// <summary>
/// 狀態管理構造
/// </summary>
/// <param name="name">Name.</param>
public StateController(string name) : base(name)
{
states = new Dictionary<string, State> ();
stateparams = new Dictionary<string, object> ();
stateFunc += CheckTranslate;
isRun = true;
Thread mainTh = new Thread (Update);
mainTh.Start ();
}
/// <summary>
/// 狀態字典
/// </summary>
Dictionary<string,State> states;
/// <summary>
/// 狀態切換參數字典
/// </summary>
Dictionary<string,object> stateparams;
/// <summary>
/// 當前狀態
/// </summary>
public State currentState;
/// <summary>
/// 添加狀態
/// </summary>
/// <returns>The state.</returns>
/// <param name="statename">狀態名稱.</param>
public State AddState(string statename)
{
State temp = new State (statename);
states.Add (statename, temp);
SetDefault (temp);
return temp;
}
/// <summary>
/// 添加狀態
/// </summary>
/// <param name="s">狀態.</param>
public void AddState(State s)
{
states.Add (s.stateName, s);
SetDefault (s);
}
/// <summary>
/// 移除狀態
/// </summary>
/// <param name="statename">狀態名稱.</param>
public void RemoveState(string statename)
{
states.Remove (statename);
}
/// <summary>
/// 設置默認狀態
/// </summary>
/// <param name="s">S.</param>
private void SetDefault (State s)
{
if(states.Count == 1)
{
currentState = s;
s.isRun = true;
}
}
/// <summary>
/// 切換狀態
/// </summary>
/// <param name="name">狀態名稱.</param>
public void TranslateState(string name)
{
if(states[name] != null)
{
currentState.isRun = false;
currentState = states [name];
states [name].isRun = true;
}
}
/// <summary>
/// 獲取狀態
/// </summary>
/// <returns>The state.</returns>
/// <param name="statename">Statename.</param>
public State GetState(string statename)
{
return states [statename];
}
/// <summary>
/// 注冊參數
/// </summary>
/// <param name="paramName">參數名稱.</param>
/// <param name="value">參數值.</param>
public void RegisterParams(string paramName, int value)
{
stateparams.Add (paramName, value);
}
/// <summary>
/// 注冊參數
/// </summary>
/// <param name="paramName">參數名稱.</param>
/// <param name="value">參數值.</param>
public void RegisterParams(string paramName, float value)
{
stateparams.Add (paramName, value);
}
/// <summary>
/// 注冊參數
/// </summary>
/// <param name="paramName">參數名稱.</param>
/// <param name="value">參數值.</param>
public void RegisterParams(string paramName, string value)
{
stateparams.Add (paramName, value);
}
/// <summary>
/// 獲取int類型參數
/// </summary>
/// <returns>The int parameters.</returns>
/// <param name="paraName">Para name.</param>
public int GetIntParams(string paraName)
{
return (int)stateparams [paraName];
}
/// <summary>
/// 獲取float類型參數
/// </summary>
/// <returns>The float parameters.</returns>
/// <param name="paraName">Para name.</param>
public float GetFloatParams(string paraName)
{
return (float)stateparams [paraName];
}
/// <summary>
/// 獲取string類型參數
/// </summary>
/// <returns>The string parameters.</returns>
/// <param name="paraName">Para name.</param>
public string GetStringParams(string paraName)
{
return (string)stateparams [paraName];
}
/// <summary>
/// 設置int參數
/// </summary>
/// <param name="paraName">Para name.</param>
/// <param name="value">Value.</param>
public void SetIntParams(string paraName, int value)
{
stateparams[paraName] =value;
}
/// <summary>
/// 設置float參數
/// </summary>
/// <param name="paraName">Para name.</param>
/// <param name="value">Value.</param>
public void SetFloatParams(string paraName, float value)
{
stateparams[paraName] =value;
}
/// <summary>
/// 設置string參數
/// </summary>
/// <param name="paraName">Para name.</param>
/// <param name="value">Value.</param>
public void SetStringParams(string paraName, string value)
{
stateparams[paraName] =value;
}
/// <summary>
/// 檢查狀態切換
/// </summary>
private void CheckTranslate()
{
foreach (var item in currentState.stateTrs)
{
if(item.Value())
{
TranslateState(item.Key.stateName);
}
}
}
}
實例應用
public class GameController : MonoBehaviour {
//單例
static StateController stateController;
//外部測試
public bool test = false;
void Awake()
{
//1、實例化單例
stateController = new StateController ("MainSc");
}
void Start()
{
//2、注冊狀態Idle
State idle = stateController.AddState ("Idle");
//2、注冊狀態Run
State run = stateController.AddState ("Run");
//3、注冊參數Speed,默認值0
stateController.RegisterParams ("Speed", 0f);
//4、idle內部事件
idle.stateFunc += () => {
Debug.Log(idle.stateName);
};
//4、run內部事件
run.stateFunc += () => {
Debug.Log(run.stateName);
};
//5、切換事件Idle --> Run
idle.RegisterTranslate (run, () => {
if(stateController.GetFloatParams("Speed") > 0.5f)
{
return true;
}
return false;
});
//5、切換事件Run --> Idle
run.RegisterTranslate (idle, () => {
if(stateController.GetFloatParams("Speed") <= 0.5f)
{
return true;
}
return false;
});
}
void Update()
{
if (test)
{
//6、切換參數,調整狀態
stateController.SetFloatParams("Speed",1);
}
else
{
stateController.SetFloatParams("Speed",0);
}
}
}
結束語
這個工具類中用到了多線程的處理,其中各個狀態的線程是持續執行的,所以后期還會繼續改進,如果讀者朋友有哪些更好的建議和意見,歡迎評論。