E神

提供创建、使用和销毁有限状态机的功能,一些适用于有限状态机机制的游戏逻辑,使用此模块将是一个不错的选择。

概念

以下一段话引自我的技能系统狭义论

是对技能系统下状态机作用的一种设想和设计。

状态

任何实体任何时刻只会处在一个状态中。

引入一个极限的概念,一个抬手动作的完成过程,在这个完成区间内可以细化出无数个状态。

可以根据游戏的实际需求,去调节状态的原子化的程度。

状态重要的实现需求

要实现状态与状态之间的解耦。

每个状态要可方便的进行单独维护。

状态执行缓存栈的实现。

--例如一个可延续的状态被一个状态打断,打断结束后要继续之前的状态执行。

状态栈对于人物状态控制的重要性云分析

本人还没完整的动手操作所以加个云字

......

......

有限状态机如果抽象为数据结构的话,其实就是图

图的作用多种多样

同样的状态机可以胜任的场景也多种多样

GF就在流程中引入了游戏流程状态机的概念,在下一章会讲到

代码结构

FsmManager

用来管理状态机

 internal sealed class FsmManager : GameFrameworkModule, IFsmManager
    {
        private readonly Dictionary<TypeNamePair, FsmBase> m_Fsms;
        private readonly List<FsmBase> m_TempFsms;
    }

值得注意的是 FsmManager 的轮询方法

用了一个列表加两次foreach才完成一次轮询

为什么要这样写

因为如果在第一次foreach的时候就直接调用fsm.Update很可能导致m_Fsms的更改,从而导致迭代器的损坏

可以说是一个经典坑了

 internal override void Update(float elapseSeconds, float realElapseSeconds)
        {
            m_TempFsms.Clear();
            if (m_Fsms.Count <= 0)
            {
                return;
            }

            foreach (KeyValuePair<TypeNamePair, FsmBase> fsm in m_Fsms)
            {
                m_TempFsms.Add(fsm.Value);
            }

            foreach (FsmBase fsm in m_TempFsms)
            {
                if (fsm.IsDestroyed)
                {
                    continue;
                }

                fsm.Update(elapseSeconds, realElapseSeconds);
            }
        }

Fsm

管理一组状态的类啦

状态的承载者是T

internal sealed class Fsm<T> : FsmBase, IReference, IFsm<T> where T : class
    {
        private T m_Owner;
        private readonly Dictionary<Type, FsmState<T>> m_States;
        private Dictionary<string, Variable> m_Datas;
        private FsmState<T> m_CurrentState;
        private float m_CurrentStateTime;
        private bool m_IsDestroyed;
    }

FsmState

一个状态的基类

是T类型的状态

  public abstract class FsmState<T> where T : class
    {
        /// <summary>
        /// 初始化有限状态机状态基类的新实例。
        /// </summary>
        public FsmState()
        {
        }

        /// <summary>
        /// 有限状态机状态初始化时调用。
        /// </summary>
        /// <param name="fsm">有限状态机引用。</param>
        protected internal virtual void OnInit(IFsm<T> fsm)
        {
        }

        /// <summary>
        /// 有限状态机状态进入时调用。
        /// </summary>
        /// <param name="fsm">有限状态机引用。</param>
        protected internal virtual void OnEnter(IFsm<T> fsm)
        {
        }

        /// <summary>
        /// 有限状态机状态轮询时调用。
        /// </summary>
        /// <param name="fsm">有限状态机引用。</param>
        /// <param name="elapseSeconds">逻辑流逝时间,以秒为单位。</param>
        /// <param name="realElapseSeconds">真实流逝时间,以秒为单位。</param>
        protected internal virtual void OnUpdate(IFsm<T> fsm, float elapseSeconds, float realElapseSeconds)
        {
        }

        /// <summary>
        /// 有限状态机状态离开时调用。
        /// </summary>
        /// <param name="fsm">有限状态机引用。</param>
        /// <param name="isShutdown">是否是关闭有限状态机时触发。</param>
        protected internal virtual void OnLeave(IFsm<T> fsm, bool isShutdown)
        {
        }

        /// <summary>
        /// 有限状态机状态销毁时调用。
        /// </summary>
        /// <param name="fsm">有限状态机引用。</param>
        protected internal virtual void OnDestroy(IFsm<T> fsm)
        {
        }

        /// <summary>
        /// 切换当前有限状态机状态。
        /// </summary>
        /// <typeparam name="TState">要切换到的有限状态机状态类型。</typeparam>
        /// <param name="fsm">有限状态机引用。</param>
        protected void ChangeState<TState>(IFsm<T> fsm) where TState : FsmState<T>
        {
            Fsm<T> fsmImplement = (Fsm<T>)fsm;
            if (fsmImplement == null)
            {
                throw new GameFrameworkException("FSM is invalid.");
            }

            fsmImplement.ChangeState<TState>();
        }

        /// <summary>
        /// 切换当前有限状态机状态。
        /// </summary>
        /// <param name="fsm">有限状态机引用。</param>
        /// <param name="stateType">要切换到的有限状态机状态类型。</param>
        protected void ChangeState(IFsm<T> fsm, Type stateType)
        {
            Fsm<T> fsmImplement = (Fsm<T>)fsm;
            if (fsmImplement == null)
            {
                throw new GameFrameworkException("FSM is invalid.");
            }

            if (stateType == null)
            {
                throw new GameFrameworkException("State type is invalid.");
            }

            if (!typeof(FsmState<T>).IsAssignableFrom(stateType))
            {
                throw new GameFrameworkException(Utility.Text.Format("State type '{0}' is invalid.", stateType.FullName));
            }

            fsmImplement.ChangeState(stateType);
        }
    }

使用实践

先写两个简单的状态

public class StateOne :FsmState<TestNode> 
{
    protected override void OnEnter(IFsm<TestNode> fsm)
    {
        Debug.LogError("进入状态1");
        ChangeState(fsm,typeof(StateTwo));
    }

    protected override void OnLeave(IFsm<TestNode> fsm, bool isShutdown)
    {
        Debug.LogError("离开状态1");
    }
}


public class StateTwo : FsmState<TestNode>
{
    protected override void OnEnter(IFsm<TestNode> fsm)
    {
        Debug.LogError("进入状态2");
    }

    protected override void OnLeave(IFsm<TestNode> fsm, bool isShutdown)
    {
        Debug.LogError("离开状态2");
    }
}

写一个简单的使用脚本

public class TestNode:MonoBehaviour
{
    IFsm<TestNode> Fsm = null;


    private void Start()
    {
        Fsm = StarForce.GameEntry.Fsm.CreateFsm<TestNode>(this, new StateOne(), new StateTwo());
    }


    private void Update()
    {
        if (Input.GetKeyDown(KeyCode.C))
        {
            Fsm.Start<StateOne>();
        }
    }
}
运行结果符合预期

小结

GF的状态机基础设施全面切逻辑缜密,代码结构严谨

但以我现在的理解要用来做技能系统状态机还是得改变一下基础机制

把缓存栈概念加入Fsm