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>();
}
}
}
Comments | NOTHING