每日一学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处理之前记录的跳转条件名即可。