在前一篇文章中,我们已经将框架里几个基本的类编写完成,在本文中,将开始正式进行功能模块的开发
我们第一个开发的模块将是Fsm模块,因为作为管理整个游戏运行时生命周期的Procedure模块,是基于Fsm的
官网中关于Fsm的介绍
首先新建一个Fsm文件夹,在其中再新建4个类文件
Fsm是状态机类,IFsm是状态机的接口(可能会有人疑惑为什么还要再写一个接口?别急,这一点到后面就能明白了),FsmState是状态抽象基类,FsmManager是状态机管理器
打开IFsm,定义如下属性与方法;
/// <summary> /// 状态机接口 /// </summary> public interface IFsm { /// <summary> /// 状态机名字 /// </summary> string Name { get; } /// <summary> /// 状态机持有者类型 /// </summary> Type OwnerType { get; } /// <summary> /// 状态机是否被销毁 /// </summary> bool IsDestroyed { get; } /// <summary> /// 当前状态运行时间 /// </summary> float CurrentStateTime { get; } /// <summary> /// 状态机轮询 /// </summary> void Update(float elapseSeconds, float realElapseSeconds); /// <summary> /// 关闭并清理状态机。 /// </summary> void Shutdown(); }
打开Fsm,为其添加泛型,并实现IFsm接口
/// <summary> /// 状态机 /// </summary> /// <typeparam name="T">状态机持有者类型</typeparam> public class Fsm<T> : IFsm where T : class { /// <summary> /// 状态机名字 /// </summary> public string Name { get; private set; } /// <summary> /// 获取状态机持有者类型 /// </summary> public Type OwnerType { get { return typeof(T); } } /// <summary> /// 状态机是否被销毁 /// </summary> public bool IsDestroyed { get; private set; } /// <summary> /// 当前状态运行时间 /// </summary> public float CurrentStateTime { get; private set; } /// <summary> /// 关闭并清理状态机。 /// </summary> public void Shutdown() { } public void Update(float elapseSeconds, float realElapseSeconds) { } }
到这里先不管Fsm类,先去把FsmState类编写完善,同样为其添加泛型
/// <summary> /// 状态基类 /// </summary> /// <typeparam name="T">状态持有者类型</typeparam> public class FsmState<T> where T : class { }
在GF的Fsm模块设计里,状态机有自身的一套事件系统,主要由FsmState进行订阅与响应,因此我们需要先在FsmState类的上面定义一个委托作为响应方法的模板
/// <summary> /// 状态机事件的响应方法模板 /// </summary> public delegate void FsmEventHandler<T>(Fsm<T> fsm, object sender, object userData) where T : class;
然后在FsmState里定义一个事件码与响应方法的字典,在构造方法里初始化
/// <summary> /// 状态订阅的事件字典 /// </summary> private Dictionary<int, FsmEventHandler<T>> m_EventHandlers; public FsmState() { m_EventHandlers = new Dictionary<int, FsmEventHandler<T>>(); }
并为其添加事件订阅与响应的相关方法
/// <summary> /// 订阅状态机事件。 /// </summary> protected void SubscribeEvent(int eventId, FsmEventHandler<T> eventHandler) { if (eventHandler == null) { Debug.LogError("状态机事件响应方法为空,无法订阅状态机事件"); } if (!m_EventHandlers.ContainsKey(eventId)) { m_EventHandlers[eventId] = eventHandler; } else { m_EventHandlers[eventId] += eventHandler; } } /// <summary> /// 取消订阅状态机事件。 /// </summary> protected void UnsubscribeEvent(int eventId, FsmEventHandler<T> eventHandler) { if (eventHandler == null) { Debug.LogError("状态机事件响应方法为空,无法取消订阅状态机事件"); } if (m_EventHandlers.ContainsKey(eventId)) { m_EventHandlers[eventId] -= eventHandler; } } /// <summary> /// 响应状态机事件。 /// </summary> public void OnEvent(Fsm<T> fsm, object sender, int eventId, object userData) { FsmEventHandler<T> eventHandlers = null; if (m_EventHandlers.TryGetValue(eventId, out eventHandlers)) { if (eventHandlers != null) { eventHandlers(fsm, sender, userData); } } }
接下来,添加FsmState最主要的生命周期方法
/// <summary> /// 状态机状态初始化时调用 /// </summary> /// <param name="fsm">状态机引用</param> public virtual void OnInit(Fsm<T> fsm) { } /// <summary> /// 状态机状态进入时调用 /// </summary> /// <param name="fsm">状态机引用</param> public virtual void OnEnter(Fsm<T> fsm) { } /// <summary> /// 状态机状态轮询时调用 /// </summary> /// <param name="fsm">状态机引用</param> public virtual void OnUpdate(Fsm<T> fsm, float elapseSeconds, float realElapseSeconds) { } /// <summary> /// 状态机状态离开时调用。 /// </summary> /// <param name="fsm">状态机引用。</param> /// <param name="isShutdown">是关闭状态机时触发</param> public virtual void OnLeave(Fsm<T> fsm, bool isShutdown) { } /// <summary> /// 状态机状态销毁时调用 /// </summary> /// <param name="fsm">状态机引用。</param> public virtual void OnDestroy(Fsm<T> fsm) { m_EventHandlers.Clear(); }
然后添加FsmState切换状态的方法,当然因为切换状态是由Fsm来进行的,所以在此之前我们需要回到Fsm里
先完善一下Fsm的字段与属性,增加用来维护所有状态的字典,维护状态机全局数据的字典,代表当前状态的引用等
/// <summary> /// 状态机名字 /// </summary> public string Name { get; private set; } /// <summary> /// 获取状态机持有者类型 /// </summary> public Type OwnerType { get { return typeof(T); } } /// <summary> /// 状态机是否被销毁 /// </summary> public bool IsDestroyed { get; private set; } /// <summary> /// 当前状态运行时间 /// </summary> public float CurrentStateTime { get; private set; } /// <summary> /// 状态机里所有状态的字典 /// </summary> private Dictionary<string, FsmState<T>> m_States; /// <summary> /// 状态机里所有数据的字典 /// </summary> private Dictionary<string, object> m_Datas; /// <summary> /// 当前状态 /// </summary> public FsmState<T> CurrentState { get; private set; } /// <summary> /// 状态机持有者 /// </summary> public T Owner { get; private set; } /// <summary> /// 关闭并清理状态机。 /// </summary> public void Shutdown() { if (CurrentState != null) { CurrentState.OnLeave(this, true); CurrentState = null; CurrentStateTime = 0f; } foreach (KeyValuePair<string, FsmState<T>> state in m_States) { state.Value.OnDestroy(this); } m_States.Clear(); m_Datas.Clear(); IsDestroyed = true; } /// <summary> /// 状态机轮询 /// </summary> public void Update(float elapseSeconds, float realElapseSeconds) { if (CurrentState == null) { return; } CurrentStateTime += elapseSeconds; CurrentState.OnUpdate(this, elapseSeconds, realElapseSeconds); }
在构造方法里进行初始化
public Fsm(string name, T owner, params FsmState<T>[] states) { if (owner == null) { Debug.LogError("状态机持有者为空"); } if (states == null || states.Length < 1) { Debug.LogError("没有要添加进状态机的状态"); } Name = name; Owner = owner; m_States = new Dictionary<string, FsmState<T>>(); m_Datas = new Dictionary<string, object>(); foreach (FsmState<T> state in states) { if (state == null) { Debug.LogError("要添加进状态机的状态为空"); } string stateName = state.GetType().FullName; if (m_States.ContainsKey(stateName)) { Debug.LogError("要添加进状态机的状态已存在:" + stateName); } m_States.Add(stateName, state); state.OnInit(this); } CurrentStateTime = 0f; CurrentState = null; IsDestroyed = false; }
继续为Fsm添加获取状态的方法
/// <summary> /// 获取状态 /// </summary> public TState GetState<TState>() where TState : FsmState<T> { return GetState(typeof(TState)) as TState; } /// <summary> /// 获取状态 /// </summary> public FsmState<T> GetState(Type stateType) { if (stateType == null) { Debug.LogError("要获取的状态为空"); } if (!typeof(FsmState<T>).IsAssignableFrom(stateType)) { Debug.LogError("要获取的状态" + stateType.FullName + "没有直接或间接的实现" + typeof(FsmState<T>).FullName); } FsmState<T> state = null; if (m_States.TryGetValue(stateType.FullName, out state)) { return state; } return null; }
然后就可以开始添加切换状态的方法
/// <summary> /// 切换状态 /// </summary> public void ChangeState<TState>() where TState : FsmState<T> { ChangeState(typeof(TState)); } /// <summary> /// 切换状态 /// </summary> public void ChangeState(Type type) { if (CurrentState == null) { Debug.LogError("当前状态机状态为空,无法切换状态"); } FsmState<T> state = GetState(type); if (state == null) { Debug.Log("获取到的状态为空,无法切换:" + type.FullName); } CurrentState.OnLeave(this, false); CurrentStateTime = 0f; CurrentState = state; CurrentState.OnEnter(this); }
Fsm类还得继续完善一些相关方法,比如开始状态机的方法
/// <summary> /// 开始状态机 /// </summary> /// <typeparam name="TState">开始的状态类型</typeparam> public void Start<TState>() where TState : FsmState<T> { Start(typeof(TState)); } /// <summary> /// 开始状态机 /// </summary> /// <param name="stateType">要开始的状态类型。</param> public void Start(Type stateType) { if (CurrentState != null) { Debug.LogError("当前状态机已开始,无法再次开始"); } if (stateType == null) { Debug.LogError("要开始的状态为空,无法开始"); } FsmState<T> state = GetState(stateType); if (state == null) { Debug.Log("获取到的状态为空,无法开始"); } CurrentStateTime = 0f; CurrentState = state; CurrentState.OnEnter(this); }
顺便添加一个抛出状态机事件的方法
/// <summary> /// 抛出状态机事件 /// </summary> /// <param name="sender">事件源</param> /// <param name="eventId">事件编号</param> public void FireEvent(object sender, int eventId) { if (CurrentState == null) { Debug.Log("当前状态为空,无法抛出事件"); } CurrentState.OnEvent(this, sender, eventId, null); }以及添加状态机全局数据相关的方法(在GF里对数据类型进行了自定义的封装,本文所要搭建的简易版GF没有进行这种封装)
/// <summary> /// 是否存在状态机数据 /// </summary> public bool HasData(string name) { if (string.IsNullOrEmpty(name)) { Debug.Log("要查询的状态机数据名字为空"); } return m_Datas.ContainsKey(name); } /// <summary> /// 获取状态机数据 /// </summary> public TDate GetData<TDate>(string name) { return (TDate)GetData(name); } /// <summary> /// 获取状态机数据 /// </summary> public object GetData(string name) { if (string.IsNullOrEmpty(name)) { Debug.Log("要获取的状态机数据名字为空"); } object data = null; m_Datas.TryGetValue(name, out data); return data; } /// <summary> /// 设置状态机数据 /// </summary> public void SetData(string name, object data) { if (string.IsNullOrEmpty(name)) { Debug.Log("要设置的状态机数据名字为空"); } m_Datas[name] = data; } /// <summary> /// 移除状态机数据 /// </summary> public bool RemoveData(string name) { if (string.IsNullOrEmpty(name)) { Debug.Log("要移除的状态机数据名字为空"); } return m_Datas.Remove(name); }
至此,Fsm类的编写算是完成了,现在回到FsmState类里,添加切换状态的方法
/// <summary> /// 切换状态 /// </summary> protected void ChangeState<TState>(Fsm<T> fsm) where TState : FsmState<T> { ChangeState(fsm, typeof(TState)); } /// <summary> /// 切换状态 /// </summary> protected void ChangeState(Fsm<T> fsm, Type type) { if (fsm == null) { Debug.Log("需要切换状态的状态机为空,无法切换"); } if (type == null) { Debug.Log("需要切换到的状态为空,无法切换"); } if (!typeof(FsmState<T>).IsAssignableFrom(type)) { Debug.Log("要切换的状态没有直接或间接实现FsmState<T>,无法切换"); } fsm.ChangeState(type); }
这样FsmState类也编写完成了,最后我们还需要编写FsmManager类
打开FsmManager类,使其继承ManagerBase,并完善相关字段与方法
public class FsmManager : ManagerBase { /// <summary> /// 所有状态机的字典(在这里,状态机接口的作用就显示出来了) /// </summary> private Dictionary<string, IFsm> m_Fsms; private List<IFsm> m_TempFsms; public override int Priority { get { return 60; } } public override void Init() { m_Fsms = new Dictionary<string, IFsm>(); m_TempFsms = new List<IFsm>(); } /// <summary> /// 关闭并清理状态机管理器 /// </summary> public override void Shutdown() { foreach (KeyValuePair<string, IFsm> fsm in m_Fsms) { fsm.Value.Shutdown(); } m_Fsms.Clear(); m_TempFsms.Clear(); } /// <summary> /// 轮询状态机管理器 /// </summary> public override void Update(float elapseSeconds, float realElapseSeconds) { m_TempFsms.Clear(); if (m_Fsms.Count <= 0) { return; } foreach (KeyValuePair<string, IFsm> fsm in m_Fsms) { m_TempFsms.Add(fsm.Value); } foreach (IFsm fsm in m_TempFsms) { if (fsm.IsDestroyed) { continue; } //轮询状态机 fsm.Update(elapseSeconds, realElapseSeconds); } } }添加查询状态机的方法
/// <summary> /// 是否存在状态机 /// </summary> private bool HasFsm(string fullName) { return m_Fsms.ContainsKey(fullName); } /// <summary> /// 是否存在状态机 /// </summary> public bool HasFsm<T>() { return HasFsm(typeof(T)); } /// <summary> /// 是否存在状态机 /// </summary> public bool HasFsm(Type type) { return HasFsm(type.FullName); }
添加创建状态机的方法
/// <summary> /// 创建状态机。 /// </summary> /// <typeparam name="T">状态机持有者类型</typeparam> /// <param name="name">状态机名称</param> /// <param name="owner">状态机持有者</param> /// <param name="states">状态机状态集合</param> /// <returns>要创建的状态机</returns> public Fsm<T> CreateFsm<T>(T owner, string name = "", params FsmState<T>[] states) where T : class { if (HasFsm<T>()) { Debug.LogError("要创建的状态机已存在"); } if (name == "") { name = typeof(T).FullName; } Fsm<T> fsm = new Fsm<T>(name, owner, states); m_Fsms.Add(name, fsm); return fsm; }
最后添加销毁状态机的方法
/// <summary> /// 销毁状态机 /// </summary> public bool DestroyFsm(string name) { IFsm fsm = null; if (m_Fsms.TryGetValue(name, out fsm)) { fsm.Shutdown(); return m_Fsms.Remove(name); } return false; } public bool DestroyFsm<T>() where T : class { return DestroyFsm(typeof(T).FullName); } public bool DestroyFsm(IFsm fsm) { return DestroyFsm(fsm.Name); }OK,Fsm模块已经完成了,该模块的具体应用,将体现在下一篇所要编写Procedure模块中