有限状态机(Finite-state machine, FSM)—— 简称状态机
所谓有限状态机就是决定我们游戏对象的当前状态和状态间的切换,状态机最终只能指向一个结果,由这个结果指向这个状态的行为,也就是执行的函数。
一,状态模式是具体的,针对每个需求有一个状态集,并为其实现特有的迁移机制。状态机是抽象的,不是针对特定的需求,而是对各种与相关的问题的进一步抽象,那么用状态机回头去实现状态模式的时候,只需要关注问题本身,而不用去关心如何实现,也就是说你只需要绘制出状态迁移图,状态机就能帮你去实现。
二,状态模式的是命令式的,我们必须一步一步地去实现状态之间如何迁移,以及迁移过程中需要做一些额外的事情。状态机是声明式的,使用状态机,你只需要声明状态及状态的迁移路线,而不需要去提供执行层面的命令,状态机自动帮你去做。
状态机能通过全局来管理我们的游戏状态/人物状态
使我们的工程逻辑清晰,将游戏/项目各个状态的转换,交由状态机统一管理
极大的避免了当状态过多 / 转换状态过多时,每次都需要调用相应函数来完成转换的麻烦
目录
个人当前的理解:
2020.05.29:
理解可能不完全,我看别人写的有限状态机蛮庞大的 。
类通过继承状态基类来变成状态类
状态类之间可以互相连接,判断条件进行状态跳转
状态类有自己的生命周期:Start , Update , Exit
状态类需要通过状态机运行
代码:
状态基类
思路为:状态与状态之间可以互相连接,所以连接表就挂在了状态类上
连接表里记录了本状态 要连接的 <其他状态,跳转条件>
跳转条件是委托条件,算是外部全局。
/// <summary>
/// 状态基类
/// </summary>
public abstract class State_Base
{
/// <summary>
/// 转换链接字典
/// </summary>
public Dictionary<State_Base, Func<bool>> converter = new Dictionary<State_Base, Func<bool>>();
public abstract void Start();//状态委托:开始
public abstract void Update();//状态委托:刷新
public abstract void Exit();//状态委托:退出
public State_Base AddState(State_Base state_Base, Func<bool> IF_Event)//链接和新状态添加
{
if (converter.ContainsKey(state_Base))
{
converter[state_Base] = IF_Event;
}
else
{
converter.Add(state_Base, IF_Event);
}
return this;
}
public State_Base RemoveState(State_Base state_Base)//状态链接删除
{
converter.Remove(state_Base);
return this;
}
}
状态机:
思路为:状态机为动态类,它的生命周期挂在继承mono的脚本上,驱动状态机去运行状态的生命周期
状态机只会储存一个状态,通过遍历当前状态的跳转表,来判断要跳转的下一个状态
/// <summary>
/// 状态机
/// </summary>
public class StateMachine
{
public State_Base CurrentState;//当前状态
public void Start()
{
CurrentState.Start();
}
public void Update()
{
foreach (var converter in CurrentState.converter) //变量判断转换条件
{
if (converter.Value()) //转换条件成立
{
//上一个状态退出
CurrentState.Exit();
//状态切换
CurrentState = converter.Key;
//新状态启动
CurrentState.Start();
break;
}
}
CurrentState.Update();
}
public void Exit()
{
CurrentState.Exit();
}
}
结论:类似站点与火车的关系,状态是节点,状态机是火车头,
火车每到一个站点就会拿到当前站点通往下一个站点的路牌,和出发指令
当判断某个出发指令为true,则开往相应的站点。
状态实现类:
//等待状态
public class Standby : State_Base
{
public override void Start()
{
Debug.Log("等待进入 ");
}
public override void Update()
{
Debug.Log("等待刷新 ");
}
public override void Exit()
{
Debug.Log("等待离开 ");
}
}
//行走状态
public class Walk : State_Base
{
public override void Start()
{
Debug.Log("行走进入 ");
}
public override void Update()
{
Debug.Log("行走刷新 ");
}
public override void Exit()
{
Debug.Log("行走离开 ");
}
}
//奔跑状态
public class Run : State_Base
{
public override void Start()
{
Debug.Log("奔跑进入 ");
}
public override void Update()
{
Debug.Log("奔跑刷新 ");
}
public override void Exit()
{
Debug.Log("奔跑离开 ");
}
}
运行测试:
public class FiniteStateMachine : MonoBehaviour
{
//===[状态枚举类]===
[SerializeField]
public enum State_Enum
{
Standby,
Walk,
Run
}
public State_Enum state_Enum;
private StateMachine stateMachine;
void Start()
{
//===[新建状态机]===
stateMachine = new StateMachine();
//===[新建状态]===
State_Base Standby_State = new Standby();
State_Base Walk_State = new Walk();
State_Base Run_State = new Run();
//===[状态机注入初始状态]===
stateMachine.CurrentState = Standby_State;
//===[状态与状态之间的条件转换注册]===
Standby_State
.AddState(Run_State, () => state_Enum == State_Enum.Run)
.AddState(Walk_State, () => state_Enum == State_Enum.Walk)
;
Walk_State
.AddState(Standby_State, () => state_Enum == State_Enum.Standby)
.AddState(Run_State, () => state_Enum == State_Enum.Run)
;
Run_State
.AddState(Walk_State, () => state_Enum == State_Enum.Walk)
.AddState(Standby_State, () => state_Enum == State_Enum.Standby)
;
//===[状态与状态之间的条件转换删除]===
Run_State.RemoveState(Walk_State);//删除了Run->Walk的链接
//!===[状态机驱动]============================================
//===[驱动状态机的Start]===
stateMachine.Start();
}
void Update()
{
//===[驱动状态机的Update]===
stateMachine.Update();
}
private void OnApplicationQuit()
{
//===[驱动状态机的Exit]===
stateMachine.Exit();
}
}
结果:
因为删除了Run->Walk的链接所以Run没有跳回Walk
2020.05.29完