有限状态机的作用:
有限状态机能够很好的处理大量条件语句的逻辑,使整体的代码逻辑简洁而高效。有限状态机常用于角色控制,ai,流程控制,Ui显示控制等等,应该十分广泛。
代码设计:
设计思路:
有限状态机的核心有两点,第一是不同状态之间切换的处理,第二是状态的数据处理。
对于第一点,当状态需要发生变化时,状态机管理器需要让当前状态退出,新的状态进入。状态机本身是不关注状态切换的条件,只关注状态切换时原状态和新状态的处理。
对于第二点,需要理解状态机的本质,状态机是逻辑管理的延申,没有状态机同样的逻辑一样能够运行。所以对于状态中获取的数据或者产生的数据都应该是通过状态的持有者里获取或者储存。不要把数据存储在状态本身。
数据结构:
状态机管理器使用字典管理添加至此状态机的状态,能够快速查找指定的状态即可(O(1))。
导图:
关键代码:
FsmState.cs
状态的基类,提供了状态激活,状态退出,状态更新三个虚函数。并且通过泛型指定状态的持有者。
public class StateBase
{
public string stateName;
public StateBase(string stateName)
{
this.stateName = stateName;
}
public virtual void OnEnterState()
{
}
public virtual void OnExitState()
{
}
public virtual void OnUpdateState()
{
}
}
public class FsmState<T> : StateBase
{
public T owner;
public FsmState(string stateName, T owner) : base(stateName)
{
this.owner = owner;
}
}
FsmMgr.cs
状态机管理器,管理状态之间的相互切换。并且支持状态集
public class FsmMgr
{
private Dictionary<string, StateBase> _fsmStateDic = new Dictionary<string, StateBase>();
private Dictionary<string, List<StateEvent>> _fsmStateEventDic = new Dictionary<string, List<StateEvent>>();
private float _stateStartTime;
private float stateTime
{
get
{
return Time.time - _stateStartTime;
}
}
public FsmMgr()
{
_stateStartTime = 0;
}
private string _preStateName;
private string _curStateName;
public string curStateName
{
get
{
return _curStateName;
}
set
{
if (_preStateName != null)
{
OnExitState(_preStateName);
}
_preStateName = _curStateName;
_curStateName = value;
_stateStartTime = Time.time;
OnEnterState(_curStateName);
}
}
private void OnExitState(string stateName)
{
if (_fsmStateDic.ContainsKey(stateName))
{
_fsmStateDic[stateName].OnExitState();
}
}
private void OnEnterState(string stateName)
{
if (_fsmStateDic.ContainsKey(stateName))
{
_fsmStateDic[stateName].OnEnterState();
}
}
public void OnUpdateState()
{
if (_fsmStateDic.ContainsKey(_curStateName))
{
_fsmStateDic[_curStateName].OnUpdateState();
}
}
public void ChangeState(string stateName)
{
if (!EqualityComparer<string>.Default.Equals(curStateName, stateName))
{
curStateName = stateName;
}
}
public void AddState(StateBase fsm)
{
if (!_fsmStateDic.ContainsKey(fsm.stateName))
{
_fsmStateDic.Add(fsm.stateName, fsm);
}
}
public void SendFsmStateEvent(string fsmEventKey)
{
if (_fsmStateEventDic.ContainsKey(fsmEventKey))
{
_fsmStateEventDic.TryGetValue(fsmEventKey, out List<StateEvent> stateEvents);
foreach (StateEvent stateEvent in stateEvents)
{
if (_curStateName == stateEvent.fromState)
{
ChangeState(stateEvent.toState);
}
}
}
}
public void RegisterFsmStateEvent(string fsmEventKey, string fromState, string toState)
{
if (!_fsmStateEventDic.ContainsKey(fsmEventKey))
{
List<StateEvent> stateEvents = new List<StateEvent>();
StateEvent stateEvent = new StateEvent();
stateEvent.fromState = fromState;
stateEvent.toState = toState;
stateEvents.Add(stateEvent);
_fsmStateEventDic.Add(fsmEventKey, stateEvents);
}
else
{
_fsmStateEventDic.TryGetValue(fsmEventKey, out List<StateEvent> stateEvents);
if (stateEvents != null)
{
StateEvent stateEvent = new StateEvent();
stateEvent.fromState = fromState;
stateEvent.toState = toState;
stateEvents.Add(stateEvent);
}
}
}
}
public class StateEvent
{
public string fromState;
public string toState;
}