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

2020-07-13  本文已影响0人  ShawnWeasley

学习来源: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处理之前记录的跳转条件名即可。

上一篇下一篇

猜你喜欢

热点阅读