通用框架(六)Fsm状态机

在项目当中,状态机是经常用到的组件。接下来就整理实现状态机的思路;

在以前自己写过几个不同的状态机,但是最终使用时会发现几个问题,与其他模块不易解耦合,而且复用起来很麻烦。

写此状态机就解决两大问题,复用性以及解耦合。

流程图

和以往的组件一样都是通过一个入口GameEntry来调用Fsm组件对外提供Create(),Destory()接口。

我们先定义FsmBase基类,在基类上我这里定义了三个属性,Id,Owner拥有着,以及当前的状态。其中拥有者是泛型T,因为我们不确定他的拥有者是谁,所以谁Create谁把自己的类型传进来。

FsmState基类和FsmBase的思路是一样的,都是规范接口,FsmState规范了业务逻辑接口,思路是状态机Enter、Update、Leave这三个接口,以及当前对应的状态机。

接下来我们写Fsm主要逻辑,还是Fsm自己提供注册接口,在初始化时把状态传进来,完成初始化。主要对外接口ChangeState()传入一个新的状态。

using System.Collections;
using System.Collections.Generic;
using UnityEngine;

namespace JIANING
{
    /// <summary>
    /// 状态机
    /// </summary>
    /// <typeparam name="T"></typeparam>
    public class Fsm<T> : FsmBase where T : class
    {

        /// <summary>
        /// 当前状态
        /// </summary>
        private FsmState<T> m_CurrState;


        /// <summary>
        /// 状态字典
        /// </summary>
        private Dictionary<byte, FsmState<T>> m_StateDic;

        /// <summary>
        /// 参数字典
        /// </summary>
        private Dictionary<string, VariableBase> m_ParamDic;

        /// <summary>
        /// 构造函数
        /// </summary>
        /// <param name="fsmId">状态机编号</param>
        /// <param name="Owner">拥有者</param>
        /// <param name="states">状态数组</param>
        public Fsm(int fsmId, T Owner, FsmState<T>[] states) : base(fsmId)
        {
            m_StateDic = new Dictionary<byte, FsmState<T>>();
            m_ParamDic = new Dictionary<string, VariableBase>();
            //把状态加入字典
            int len = states.Length;
            for (int i = 0; i < len; i++)
            {
                FsmState<T> state = states[i];
                state.CurrFsm = this;
                m_StateDic[(byte)i] = state;
            }

            //设置默认状态
            CurrStateType = 0;

            m_CurrState = m_StateDic[CurrStateType];
            m_CurrState.OnEnter();
        }

        /// <summary>
        /// 获取状态
        /// </summary>
        /// <param name="stateType"></param>
        /// <returns></returns>
        public FsmState<T> GetState(byte stateType)
        {
            FsmState<T> state = null;
            m_StateDic.TryGetValue(stateType, out state);

            return state;
        }

        public void OnUpdate()
        {
            if (m_CurrState != null)
            {
                m_CurrState.OnUpdate();
            }
        }

        /// <summary>
        /// 切换状态
        /// </summary>
        /// <param name="newState"></param>
        public void ChangeState(byte newState)
        {
            if (newState == CurrStateType)
            {
                Debug.LogError("Fsm状态重复");
                return;
            }
            if (m_CurrState != null)
            {
                m_CurrState.OnLeave();
            }

            CurrStateType = newState;

            m_CurrState = m_StateDic[CurrStateType];

            //进入新状态
            m_CurrState.OnEnter();

        }

        /// <summary>
        /// 设置参数值
        /// </summary>
        /// <typeparam name="TData">泛型的类型</typeparam>
        /// <param name="key"></param>
        /// <param name="value"></param>
        public void SetDate<TData>(string key, TData value)
        {
            VariableBase itemBase = null;
            if (m_ParamDic.TryGetValue(key, out itemBase))
            {
                Variable<TData> item = itemBase as Variable<TData>;
                item.Value = value;
                m_ParamDic[key] = item;

            }
            else
            {
                //参数原来不存在
                Variable<TData> item = new Variable<TData>();
                item.Value = value;
                m_ParamDic[key] = item;
            }
        }

        /// <summary>
        /// 获取参数值
        /// </summary>
        /// <typeparam name="TData"></typeparam>
        /// <param name="key"></param>
        /// <returns></returns>
        public TData GetData<TData>(string key)
        {
            VariableBase itemBase = null;
            if (m_ParamDic.TryGetValue(key, out itemBase))
            {
                Variable<TData> item = itemBase as Variable<TData>;
                return item.Value;
            }
            else
            {
                return default(TData);
            }
        }

        /// <summary>
        /// 关闭状态机
        /// </summary>
        public override void Shutdown()
        {
            if (m_CurrState != null)
            {
                m_CurrState.OnLeave();
            }
            foreach (KeyValuePair<byte, FsmState<T>> state in m_StateDic)
            {
                state.Value.OnDestory();
            }
            m_StateDic.Clear();
            m_ParamDic.Clear();
        }
    }
}

到这里内部逻辑我们写完了,接下来写外部调用接口。

using System.Collections;
using System.Collections.Generic;
using System;
using UnityEngine;
namespace JIANING
{
    /// <summary>
    /// 状态机管理器
    /// </summary>
    public class FsmManager : ManagerBase,IDisposable
    {

        /// <summary>
        /// 状态机字典
        /// </summary>
        private Dictionary<int, FsmBase> m_FsmDic;

        public FsmManager()
        {
            m_FsmDic = new Dictionary<int, FsmBase>();
        }


        /// <summary>
        /// 创建状态机
        /// </summary>
        /// <typeparam name="T">拥有者的类型</typeparam>
        /// <param name="fsmId">状态机编号</param>
        /// <param name="owner">拥有者</param>
        /// <param name="states">状态数组</param>
        /// <returns></returns>
        public Fsm<T> Create<T>(int fsmId, T owner, FsmState<T>[] states) where T : class
        {
            Fsm<T> fsm = new Fsm<T>(fsmId, owner, states);

            m_FsmDic[fsmId] = fsm;

            return fsm;
        }

        /// <summary>
        /// 销毁状态机
        /// </summary>
        /// <param name="fsmId"></param>
        public void DestoryFsm(int fsmId)
        {
            FsmBase fsm = null;
            if (m_FsmDic.TryGetValue(fsmId, out fsm))
            {
                fsm.Shutdown();
                m_FsmDic.Remove(fsmId);
            }
         
        }

        public void Dispose()
        {
            var enumerator = m_FsmDic.GetEnumerator();
            while (enumerator.MoveNext())
            {
                enumerator.Current.Value.Shutdown();
            }

            m_FsmDic.Clear();
        }
    }
}

FsmManager和我们上面的思路是一样的,提供出去创建和销毁接口,并且不继承mono,需要时Create,销毁时Destory,解决了我们上面的两大问题解耦合以及复用性。

到这里Fsm状态机就写完了。具体代码在GitHub上。

留下评论

邮箱地址不会被公开。 必填项已用*标注