简易版GameFramework游戏框架搭建教程(三)Fsm

在前一篇文章中,我们已经将框架里几个基本的类编写完成,在本文中,将开始正式进行功能模块的开发

我们第一个开发的模块将是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模块中

猜你喜欢

转载自blog.csdn.net/qq_32821435/article/details/80470631