好久之前写过一篇关于状态机的小例子,可以看这里http://www.cnblogs.com/mawanli/p/5966080.html,这篇博客首先感谢需要感谢当时看到凉鞋的笔记博客,
凉鞋的博客地址先分享出来http://liangxiegame.com/tag/unity_framework/
今天在这里打算在重新谈论一下这些事情,是在一个gameframework的框架里面学到新的设计方法,今天打算是贡献出来,欢迎大家指教。
首先介绍下什么是状态机,状态机说白了就是自己的状态可以通过外界条件或者自身可以转换状态的一种模式。我们需要设计状态机和状态机的状态,每个状态包含进入此状态,离开此状态的事件,状态机里面需要记录当前的状态以及状态机里的事件和状态机调换状态的方法。
先看状态机的状态设计
public abstract class FSMState { private string m_StateName; /// <summary> /// 初始化有限状态机状态基类的新实例 /// </summary> /// <param name="name">状态名称</param> public FSMState(string name) { m_StateName = name; } /// <summary> /// 状态的名字 /// </summary> public string GetStateName { get { return m_StateName; } } /// <summary> /// 有限状态机状态初始化时调用。 /// </summary> /// <param name="fsm">有限状态机引用。</param> public virtual void OnInit() { } /// <summary> /// 有限状态机状态进入时调用。 /// </summary> /// <param name="fsm">有限状态机引用。</param> public virtual void OnEnter() { } /// <summary> /// 有限状态机状态离开时调用。 /// </summary> /// <param name="fsm">有限状态机引用。</param> /// <param name="isShutdown">是否是关闭有限状态机时触发。</param> public virtual void OnLeave(bool isShutdown) { } /// <summary> /// 有限状态机状态销毁时调用。 /// </summary> /// <param name="fsm">有限状态机引用。</param> public virtual void OnDestroy() { } }
接着是状态机的设计,状态机需要继承状态的的基类,先看基类的设计
public abstract class FSMBase<T,P> { private readonly string m_Name; /// <summary> /// 初始化有限状态机基类的新实例。 /// </summary> public FSMBase(): this(null) { } /// <summary> /// 初始化有限状态机基类的新实例。 /// </summary> /// <param name="name">有限状态机名称。</param> public FSMBase(string name) { m_Name = name ?? string.Empty; } /// <summary> /// 获取有限状态机名称。 /// </summary> public string Name { get { return m_Name; } } /// <summary> /// 获取有限状态机持有者 /// </summary> public abstract T GetOwner { get; } /// <summary> /// 获取有限状态机中状态的数量。 /// </summary> public abstract int FsmStateCount { get; } /// <summary> /// 获取有限状态机是否正在运行。 /// </summary> public abstract bool IsRunning { get; } /// <summary> /// 获取有限状态机是否被销毁。 /// </summary> public abstract bool IsDestroyed { get; } /// <summary> /// 获取当前有限状态机状态名称。 /// </summary> public abstract string CurrentStateName { get; } /// <summary> /// 关闭并清理有限状态机。 /// </summary> internal abstract void Shutdown(); }
接着看状态的设计
public sealed class FSM<T,P> : FSMBase<T,P> { private readonly Dictionary<P, FSMState> m_States; //记录所有状态机的状态 private readonly Dictionary<int, FsmEventHandler<FSMEventArgs>> m_EventHandler; //记录所有状态机中的事件 private FSMState m_CurrentState; //当前状态 private T m_Owner; //状态机持有者 private bool m_IsDestoryed; //是否被销毁 public FSM(string name, T owner, List<StateTypeData<P>> states): base(name) { if (owner == null) { Console.WriteLine("状态机持有者无效"); } if (states == null || states.Count < 1) { Console.WriteLine("状态机无效"); } m_Owner = owner; m_States = new Dictionary<P, FSMState>(); m_EventHandler = new Dictionary<int, FsmEventHandler<FSMEventArgs>>(); foreach (StateTypeData<P> state in states) { if (state == null) { Console.WriteLine("状态机无效"); break; } if (m_States.ContainsKey(state.GetType)) { Console.WriteLine("状态机中已经存在此状态"); } m_States.Add(state.GetType, state.GetState); state.GetState.OnInit(); } m_CurrentState = null; m_IsDestoryed = false; } /// <summary> /// 状态机持有者 /// </summary> public override T GetOwner { get { return m_Owner; } } /// <summary> /// 状态的数量 /// </summary> public override int FsmStateCount { get { if (m_States != null) { return m_States.Count; } else { //状态机还未初始化 return 0; } } } /// <summary> /// 状态机是否在运行,在运行返回True /// </summary> public override bool IsRunning { get { return m_CurrentState != null; } } /// <summary> /// 状态机是否被销毁 /// </summary> public override bool IsDestroyed { get { return m_IsDestoryed; } } /// <summary> /// 状态名字 /// </summary> public override string CurrentStateName { get { if (m_CurrentState != null) { return m_CurrentState.GetStateName; } else { //取值无效 return null; } } } public FSMState GetState() { return m_CurrentState; } /// <summary> /// 关闭状态机 /// </summary> internal override void Shutdown() { if (m_CurrentState != null) { m_CurrentState.OnLeave(true); m_CurrentState = null; } foreach (KeyValuePair <P, FSMState> state in m_States) { state.Value.OnDestroy(); } m_States.Clear(); //m_Datas.Clear(); //状态机数据 m_IsDestoryed = true; } /// <summary> /// 开启有限状态机 /// </summary> /// <param name="stateType">起始状态类型</param> public void Start(P stateType) { if (IsRunning) { //状态机已经在运行 } FSMState state = GetState(stateType); if (state == null) { //状态机起始状态不存在 } m_CurrentState = state; m_CurrentState.OnEnter(); } /// <summary> /// 状态机是否存在此状态 /// </summary> /// <param name="stateType"></param> /// <returns></returns> public bool HasState(P stateType) { return m_States.ContainsKey(stateType); } /// <summary> /// 获取想要的状态 /// </summary> /// <param name="stateType"></param> /// <returns></returns> public FSMState GetState(P stateType) { FSMState state = null; if (m_States.TryGetValue(stateType, out state)) { return state; } return null; } /// <summary> /// 是否存在此状态 /// </summary> /// <param name="name"></param> /// <returns></returns> public bool HasData(P name) { return m_States.ContainsKey(name); } /// <summary> /// 切换当前有限状态机状态。 /// </summary> /// <param name="stateType">要切换到的有限状态机状态类型。</param> public void ChangeState(P stateType) { if (m_CurrentState == null) { //状态机已经失效 return; } FSMState state = GetState(stateType); if (state == null) { //不能切换到不存在的状态 return; } m_CurrentState.OnLeave(false); m_CurrentState = state; m_CurrentState.OnEnter(); } /// <summary> /// 订阅有限状态机事件。 /// </summary> /// <param name="eventId">事件编号。</param> /// <param name="eventHandler">有限状态机事件响应函数。</param> public void SubscribeEvent(int eventId, FsmEventHandler<FSMEventArgs> eventHandler) { if (eventHandler == null) { //响应事件为空 } if (!m_EventHandler.ContainsKey(eventId)) { m_EventHandler[eventId] = eventHandler; } else { m_EventHandler[eventId] += eventHandler; } } /// <summary> /// 取消订阅有限状态机事件。 /// </summary> /// <param name="eventId">事件编号。</param> /// <param name="eventHandler">有限状态机事件响应函数。</param> public void UnsubscribeEvent(int eventId, FsmEventHandler<FSMEventArgs> eventHandler) { if (eventHandler == null) { //事件无效 } if (m_EventHandler.ContainsKey(eventId)) { m_EventHandler[eventId] -= eventHandler; } } /// <summary> /// 响应有限状态机事件时调用。 /// </summary> /// <param name="fsm">有限状态机引用。</param> /// <param name="sender">事件源。</param> /// <param name="eventId">事件编号。</param> /// <param name="userData">用户自定义数据。</param> public void OnEvent(object sender, int eventId, FSMEventArgs userData) { FsmEventHandler<FSMEventArgs> eventHandlers = null; if (m_EventHandler.TryGetValue(eventId, out eventHandlers)) { if (eventHandlers != null) { eventHandlers(sender, userData); } } } }
状态机里的事件设计,,所有事件都要继承此类
public abstract class FSMEventArgs: EventArgs { private String m_Name; /// <summary> /// 初始化事件类 /// </summary> /// <param name="_name">事件名字</param> public FSMEventArgs(String _name) { m_Name = _name; } /// <summary> /// 得到事件的名字 /// </summary> public String GetEventName { get { return m_Name; } } }
/// <summary> /// 状态机事件 /// </summary> /// <typeparam name="P">状态机的枚举</typeparam> /// <param name="sender">发送者</param> /// <param name="userData">发送的数据</param> public delegate void FsmEventHandler<P>(object sender, P userData) where P : FSMEventArgs;
下面我自己写的三个状态的状态机的例子
首先定义了状态的枚举(方便区分状态,切换状态等都使用此枚举)
/// <summary> /// 状态机枚举 /// </summary> public enum ActioEnum { Start, Update, LateUpdate, DisViable, End }
接着是几个状态,
public class StateA : FSMState { public StateA() : base(typeof(StateA).FullName) { } public override void OnDestroy() { base.OnDestroy(); Console.WriteLine("StateA销毁"); } public override void OnEnter() { base.OnEnter(); Console.WriteLine("StateA进入"); } public override void OnInit() { base.OnInit(); Console.WriteLine("StateA初始化"); } public override void OnLeave(bool isShutdown) { base.OnLeave(isShutdown); Console.WriteLine("StateA离开"); } }
这是状态A,还有状态A,C, 大家可以根据状态A进行类比B,C,这里就不做出示例
状态机的大家可以参考下面的示例
void Start () { foreach (BianLiEnum item in Enum.GetValues(typeof(BianLiEnum))) { BianLiEnum q = (BianLiEnum)((int)item); } List<StateTypeData<ActioEnum>> fSMStateList = new List<StateTypeData<ActioEnum>>(); StateA stateA = new StateA(); StateTypeData<ActioEnum> stA = new StateTypeData<ActioEnum>(ActioEnum.Start, stateA); StateB stateB = new StateB(); StateTypeData<ActioEnum> stB = new StateTypeData<ActioEnum>(ActioEnum.Update, stateB); StateC stateC = new StateC(); StateTypeData<ActioEnum> stC = new StateTypeData<ActioEnum>(ActioEnum.LateUpdate, stateC); fSMStateList.Add(stA); fSMStateList.Add(stB); fSMStateList.Add(stC); UnityGameObject unityObj = new UnityGameObject(); FSM<UnityGameObject, ActioEnum> m_FSM = new FSM<UnityGameObject, ActioEnum>("第一个状态机", unityObj, fSMStateList); m_FSM.Start(ActioEnum.Start); Debug.Log("状态机持有者是" + m_FSM.GetOwner); m_FSM.ChangeState(ActioEnum.Update); m_FSM.Shutdown(); Debug.Log("状态机已经销毁"); }
大家会发现示例当中创建状态机很繁琐,
然后我就用反射去创建状态机,下面是我通过反射创建得到所有的子类
public static List<T> GetTypeChilds<T>(Type parentType) { List<T> lstType = new List<T>(); List<Type> typeList = new List<Type>(); List<string> typeNameList = new List<string>(); List<string> typeNameTempList = new List<string>(); Assembly assem = Assembly.GetAssembly(parentType); foreach (Type tChild in assem.GetTypes()) { if (tChild.BaseType == parentType) { typeList.Add(tChild); typeNameList.Add(tChild.FullName); typeNameTempList.Add(tChild.FullName); //lstType.Add((T)Activator.CreateInstance(tChild)); } } typeNameTempList.Sort(); int listLength = typeList.Count; int nowIndex = 0; for (int i = 0; i < listLength; i++) { nowIndex = typeNameList.IndexOf(typeNameTempList[i]); lstType.Add((T)Activator.CreateInstance(typeList[nowIndex])); } return lstType; }
这只是得到所有的子类,没有添加到状态机当中去,还需要进行完善
下次修改的时候把反射加入到状态机的创建当中去,代码就会瞬间清晰许多