Unity FSM Finite State Machine-A Day in the Life of a Worker

I had always wanted to learn about FSM through a small case, and finally I had time to complete it.

Try to use a day as a worker to understand FSM and understand the state switching.

Code structure:

Basic implementation:

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);
            }
        }
    }
}

The above three classes constitute the implementation of the basic FSM state machine.

usage:

1. Define status

namespace Assets.Scripts.FSM
{
    //定义状态
    public enum StepCodeDefine
    {
        WakeUp,       //起床
        Commuter,     //通勤
        LunchBreak,   //午休
        Work,         //工作
        OffWork,      //下班
        Sleep,        //睡觉
    }
}

2. Integrate again and add time control

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. Implement subdivision of status processing

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. Use FSM to test

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);
    }
}

Test Results:

You can download the demo during the test and take a closer look. If you think it is good after reading it, come back and give it a thumbs up, and you will get 0 points.

https://download.csdn.net/download/u013476751/86725810 

Guess you like

Origin blog.csdn.net/u013476751/article/details/127104934