之前一直想通过一个小案例来了解FSM,终于有空完成了。
尝试用打工人的一天来理解FSM,理解状态的切换。
代码结构:
基础实现:
namespace Assets.Scripts.FSM
{
public interface IState
{
int ID { get; }
FiniteStateMachine FSM { get; set; }
bool Enter(object data);
bool Exit();
bool Update(float dt);
bool TryTransTo(int id);
}
}
namespace Assets.Scripts.FSM
{
public abstract class BaseState : IState
{
public int ID
{
get
{
return OnStateID();
}
}
public FiniteStateMachine FSM { get; set; }
public bool Enter(object data)
{
return OnEnter(data);
}
public bool Exit()
{
return OnExit();
}
public bool TryTransTo(int id)
{
return OnTryTransTo(id);
}
public bool Update(float dt)
{
return OnUpdate(dt);
}
protected abstract int OnStateID();
protected abstract bool OnEnter(object data);
protected abstract bool OnExit();
protected abstract bool OnTryTransTo(int nextStateID);
protected abstract bool OnUpdate(float dt);
}
}
using System.Collections.Generic;
namespace Assets.Scripts.FSM
{
/// <summary>
/// 状态机管理器
/// </summary>
public class FiniteStateMachine
{
/// <summary>
/// 当前状态机所有的状态
/// </summary>
private Dictionary<int, IState> _states = new Dictionary<int, IState>();
private object _host;
private IState _currentState;
/// <summary>
/// 当前状态
/// </summary>
public IState CurrentState
{
get
{
return _currentState;
}
}
/// <summary>
/// 状态机所在对象
/// </summary>
public object Host
{
get { return _host; }
set { _host = value; }
}
public FiniteStateMachine(object host)
{
_host = host;
}
/// <summary>
/// 注册状态
/// </summary>
/// <param name="state"></param>
public void Register(IState state)
{
if (state != null)
{
_states[state.ID] = state;
state.FSM = this;
}
}
/// <summary>
/// 转移到下一个状态,结束当前状态,进入下一状态
/// </summary>
/// <param name="destId">目标状态ID</param>
/// <param name="data">转移状态传递的数据</param>
/// <returns>状态转义是否成功</returns>
public bool TransToState(int destId, object data)
{
bool canTrans = true;
if (_currentState != null)
{
canTrans = _currentState.TryTransTo(destId);
if (canTrans)
{
_currentState.Exit();
_currentState = null;
}
}
if (canTrans)
{
IState nextState;
_states.TryGetValue(destId, out nextState);
if (nextState != null)
{
_currentState = nextState;
nextState.Enter(data);
return true;
}
}
return false;
}
public void Update(float dt)
{
if (_currentState != null)
{
_currentState.Update(dt);
}
}
}
}
以上三个类构成了基础的FSM状态机的实现。
用法:
1. 定义状态
namespace Assets.Scripts.FSM
{
//定义状态
public enum StepCodeDefine
{
WakeUp, //起床
Commuter, //通勤
LunchBreak, //午休
Work, //工作
OffWork, //下班
Sleep, //睡觉
}
}
2. 再次集成,增加时间控制
namespace Assets.Scripts.FSM
{
public abstract class BaseStep : BaseState
{
//逝去时间
private float _elapseTime = 0;
//判断当前阶段是否超时,-1的话表示永远都不会超时,0:表示下一帧立即切换
private float _timeOut = -1;
public float TimeOut
{
get
{
return _timeOut;
}
set
{
_timeOut = value;
}
}
protected override bool OnEnter(object data)
{
return true;
}
protected override bool OnExit()
{
return true;
}
protected override bool OnTryTransTo(int nextStateID)
{
return true;
}
protected override bool OnUpdate(float dt)
{
if (_timeOut >= 0)
{
_elapseTime += dt;
if (_elapseTime >= _timeOut)
{
_elapseTime = 0;
OnTimeOut();
}
}
return true;
}
protected virtual void OnTimeOut() { }
public void ToNextState(StepCodeDefine dest, object data = null)
{
FSM.TransToState((int)dest, data);
}
}
}
3. 实现状态处理的细分类
using UnityEngine;
namespace Assets.Scripts.FSM
{
public class WakeUpState : BaseStep
{
protected override int OnStateID()
{
return (int)StepCodeDefine.WakeUp;
}
protected override bool OnEnter(object data)
{
TimeOut = 0;
Debug.Log("起床时间:" + (string)data);
return base.OnEnter(data);
}
protected override bool OnExit()
{
return base.OnExit();
}
protected override bool OnTryTransTo(int nextStateID)
{
return base.OnTryTransTo(nextStateID);
}
protected override bool OnUpdate(float dt)
{
return base.OnUpdate(dt);
}
protected override void OnTimeOut()
{
string commuterTime = "8:30";
ToNextState(StepCodeDefine.Commuter, commuterTime);
base.OnTimeOut();
}
}
}
using UnityEngine;
namespace Assets.Scripts.FSM
{
public class CommuterState : BaseStep
{
protected override bool OnEnter(object data)
{
TimeOut = 2;
Debug.Log("通勤出发时间:" + (string)data);
Debug.Log("公交(地铁上)玩会儿手机,看看妹儿~~~");
return base.OnEnter(data);
}
protected override bool OnExit()
{
return base.OnExit();
}
protected override int OnStateID()
{
return (int)StepCodeDefine.Commuter;
}
protected override void OnTimeOut()
{
ToNextState(StepCodeDefine.Work, "9:00");
base.OnTimeOut();
}
protected override bool OnTryTransTo(int nextStateID)
{
return base.OnTryTransTo(nextStateID);
}
protected override bool OnUpdate(float dt)
{
Debug.Log(string.Format("<color=yellow>{0}</color>", "公交(地铁上)行驶中~~~"));
return base.OnUpdate(dt);
}
}
}
using UnityEngine;
namespace Assets.Scripts.FSM
{
public class WorkState : BaseStep
{
string workTime = string.Empty;
protected override bool OnEnter(object data)
{
TimeOut = 0;
workTime = (string)data;
Debug.Log("上班了 摸鱼~~~~~~~");
return base.OnEnter(data);
}
protected override bool OnExit()
{
return base.OnExit();
}
protected override int OnStateID()
{
return (int)StepCodeDefine.Work;
}
protected override void OnTimeOut()
{
if (!string.IsNullOrEmpty(workTime))
{
ToNextState(StepCodeDefine.LunchBreak);
}
else
{
ToNextState(StepCodeDefine.OffWork);
}
base.OnTimeOut();
}
protected override bool OnTryTransTo(int nextStateID)
{
return base.OnTryTransTo(nextStateID);
}
protected override bool OnUpdate(float dt)
{
return base.OnUpdate(dt);
}
}
}
using UnityEngine;
namespace Assets.Scripts.FSM
{
public class LunchBreakState : BaseStep
{
protected override bool OnEnter(object data)
{
Debug.Log(string.Format("<color=green>{0}</color>", "午休了,逛逛B站~~~"));
TimeOut = 0;
return base.OnEnter(data);
}
protected override bool OnExit()
{
return base.OnExit();
}
protected override int OnStateID()
{
return (int)StepCodeDefine.LunchBreak;
}
protected override void OnTimeOut()
{
ToNextState(StepCodeDefine.Work);
base.OnTimeOut();
}
protected override bool OnTryTransTo(int nextStateID)
{
return base.OnTryTransTo(nextStateID);
}
protected override bool OnUpdate(float dt)
{
return base.OnUpdate(dt);
}
}
}
using UnityEngine;
namespace Assets.Scripts.FSM
{
public class OffWorkState : BaseStep
{
protected override bool OnEnter(object data)
{
TimeOut = 0;
Debug.Log(string.Format("<color=green>{0}</color>", "下班!!耶稣也留不住我!"));
Debug.Log(string.Format("<color=green>{0}</color>", "骑行,健身,玩游戏!"));
return base.OnEnter(data);
}
protected override bool OnExit()
{
return base.OnExit();
}
protected override int OnStateID()
{
return (int)StepCodeDefine.OffWork;
}
protected override void OnTimeOut()
{
ToNextState(StepCodeDefine.Sleep);
base.OnTimeOut();
}
protected override bool OnTryTransTo(int nextStateID)
{
return base.OnTryTransTo(nextStateID);
}
protected override bool OnUpdate(float dt)
{
return base.OnUpdate(dt);
}
}
}
namespace Assets.Scripts.FSM
{
public class SleepState : BaseStep
{
protected override bool OnEnter(object data)
{
UnityEngine.Debug.LogError("睡觉!完结!");
return base.OnEnter(data);
}
protected override bool OnExit()
{
return base.OnExit();
}
protected override int OnStateID()
{
return (int)StepCodeDefine.Sleep;
}
protected override void OnTimeOut()
{
base.OnTimeOut();
}
protected override bool OnTryTransTo(int nextStateID)
{
return base.OnTryTransTo(nextStateID);
}
protected override bool OnUpdate(float dt)
{
return base.OnUpdate(dt);
}
}
}
4. 使用FSM来测试
using Assets.Scripts.FSM;
using UnityEngine;
public class FsmDemo : MonoBehaviour
{
//状态机管理器
private FiniteStateMachine _fsm = null;
private void Awake()
{
//注册状态
_fsm = new FiniteStateMachine(this);
_fsm.Register(new WakeUpState());
_fsm.Register(new CommuterState());
_fsm.Register(new WorkState());
_fsm.Register(new LunchBreakState());
_fsm.Register(new OffWorkState());
_fsm.Register(new SleepState());
}
void Start()
{
Debug.LogError("FsmDemo Start");
//开始第一个状态
string time = "8:00";
_fsm.TransToState((int)StepCodeDefine.WakeUp, time);
}
void Update()
{
_fsm.Update(Time.deltaTime);
}
}
测试结果:
测试时的Demo,可以下载之后细看,看完觉得还不错回来点赞给好评,都0积分了