在前一篇文章中,我们已经将框架里几个基本的类编写完成,在本文中,将开始正式进行功能模块的开发
我们第一个开发的模块将是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模块中