美文网首页
每日一学18——凉鞋的简易有限状态机

每日一学18——凉鞋的简易有限状态机

作者: ShawnWeasley | 来源:发表于2020-07-13 10:28 被阅读0次

    学习来源:https://liangxiegame.com/zhuanlan/content/detail/adb49610-b6b9-40f5-bd3d-bc3cead03343#

    FSM状态机的代码直接拷贝过来一份:

    using UnityEngine;
    using System.Collections;
    using System.Collections.Generic;
    public class FSM
    {
        // 定义函数指针类型
        public delegate void FSMTranslationCallfunc();    /// <summary>
                                                          /// 状态类
                                                          /// </summary>
        public class FSMState
        {
            public string name;
    
            public FSMState(string name)
            {
                this.name = name;
            }
            /// <summary>
            /// 存储事件对应的跳转
            /// </summary>
            public Dictionary<string, FSMTranslation> TranslationDict = new Dictionary<string, FSMTranslation>();
        }
        /// <summary>
        /// 跳转类
        /// </summary>
        public class FSMTranslation
        {
            public FSMState fromState;
            public string name;
            public FSMState toState;
            public FSMTranslationCallfunc callfunc; // 回调函数
    
            public FSMTranslation(FSMState fromState, string name, FSMState toState, FSMTranslationCallfunc callfunc)
            {
                this.fromState = fromState;
                this.toState = toState;
                this.name = name;
                this.callfunc = callfunc;
            }
        }
        // 当前状态
        private FSMState mCurState;
    
        Dictionary<string, FSMState> StateDict = new Dictionary<string, FSMState>();
        /// <summary>
        /// 添加状态
        /// </summary>
        /// <param name="state">State.</param>
        public void AddState(FSMState state)
        {
            StateDict[state.name] = state;
        }
        /// <summary>
        /// 添加条转
        /// </summary>
        /// <param name="translation">Translation.</param>
        public void AddTranslation(FSMTranslation translation)
        {
            StateDict[translation.fromState.name].TranslationDict[translation.name] = translation;
        }
        /// <summary>
        /// 启动状态机
        /// </summary>
        /// <param name="state">State.</param>
        public void Start(FSMState state)
        {
            mCurState = state;
        }
        /// <summary>
        /// 处理事件
        /// </summary>
        /// <param name="name">Name.</param>
        public void HandleEvent(string name)
        {
            if (mCurState != null && mCurState.TranslationDict.ContainsKey(name))
            {
                Debug.LogWarning("fromState:" + mCurState.name);
    
                mCurState.TranslationDict[name].callfunc();
                mCurState = mCurState.TranslationDict[name].toState;
    
    
                Debug.LogWarning("toState:" + mCurState.name);
            }
        }
    }
    

    调用方式写详细一点如下:

    using System.Collections;
    using System.Collections.Generic;
    using UnityEngine;
    
    public class NewBehaviourScript : MonoBehaviour
    {
        FSM fsm = new FSM();
        // Start is called before the first frame update
        void Start()
        {
            //新建FSM各种状态
            FSM.FSMState idleState = new FSM.FSMState("idle");
            FSM.FSMState runState = new FSM.FSMState("run");
            FSM.FSMState jumpState = new FSM.FSMState("jump");
            FSM.FSMState doubleJumpState = new FSM.FSMState("double_jump");
            FSM.FSMState dieState = new FSM.FSMState("die");
    
            //新建FSM各种状态的跳转条件
            //这里的四个参数可以这么理解:
            //1.当目前状态是runState时
            //2.接收到"touch_down"事件
            //3.即跳转到jumpState
            //4.并且回调Jump函数
            FSM.FSMTranslation touchTranslation1 = new FSM.FSMTranslation(runState, "touch_down", jumpState, Jump);
            FSM.FSMTranslation touchTranslation2 = new FSM.FSMTranslation(jumpState, "touch_down", doubleJumpState, DoubleJump);
            FSM.FSMTranslation landTranslation1 = new FSM.FSMTranslation(jumpState, "land", runState, Run);
            FSM.FSMTranslation landTranslation2 = new FSM.FSMTranslation(doubleJumpState, "land", runState, Run);
    
            //为fsm添加上面新建的状态和跳转方式
            fsm.AddState(idleState);
            fsm.AddState(runState);
            fsm.AddState(jumpState);
            fsm.AddState(doubleJumpState);
            fsm.AddState(dieState);
    
            fsm.AddTranslation(touchTranslation1);
            fsm.AddTranslation(touchTranslation2);
            fsm.AddTranslation(landTranslation1);
            fsm.AddTranslation(landTranslation2);
    
            //设定fsm初始状态
            fsm.Start(runState);
    
        }
        #region  回调
        void Run()
        {
            Debug.Log("Run");
        }
    
        void Jump()
        {
            Debug.Log("Jump");
        }
    
        void DoubleJump()
        {
            Debug.Log("DoubleJump");
        }
        #endregion
    
        // Update is called once per frame
        void Update()
        {
            //事件触发
            if (Input.GetMouseButtonDown(0))
                fsm.HandleEvent("touch_down");
            if (Input.GetMouseButtonDown(1))
                fsm.HandleEvent("land");
        }
    }
    

    运行结果:


    运行结果

    总结:
    可以看到这里实现了跑酷游戏的状态机制,即Run的时候可以Jump,Jump后未Land之前可以DoubleJump,Jump和DoubleJump之后都可以Land,Land之后立马恢复Run状态可以进行下一轮操作。
    如果没有状态机,可能需要大量的if else来实现,代码也不美观,容易出Bug。
    这里的状态机的思路是FSMState和FSMTranslation是状态和跳转关系的定义类,并且在FSMState使用一个Dictionary记录本状态可以跳转的FSMTranslation跳转关系。然后定义了当前的状态mCurState和全部的状态StateDict,在使用的时候需要先赋值当前状态Start和全部状态AddState,并且为全部状态添加跳转条件以及回调AddTranslation。最终在调用的时候直接HandleEvent处理之前记录的跳转条件名即可。

    相关文章

      网友评论

          本文标题:每日一学18——凉鞋的简易有限状态机

          本文链接:https://www.haomeiwen.com/subject/hsxicktx.html