unity fsm 有限状态机

2020-02-21  本文已影响0人  Rehma

FSM 有限状态机


概念

基本思路

pic

代码实现

以下代码在FsmBase.cs脚本 是每个状态的基类

public enum E_state {
    Idle,
    Jump,
    Walk,
    None
}
public class FsmBase {
    /// <summary>
    /// 状态的ID 用来标识每一个状态
    /// </summary>
    public E_state e_State { get; }
    //初始化
    public FsmBase (E_state _state) {
        this.e_State = _state;
    }
    
    /// <summary>
    /// 状态进入
    /// </summary>
    /// <param name="args">任意可变参数</param>
    public virtual void OnEnter (params object[] args) { }

    /// <summary>
    /// 状态保持
    /// </summary>
    /// <param name="args">任意可变参数</param>
    public virtual void OnStay (params object[] args) { }

    /// <summary>
    /// 状态退出
    /// </summary>
    /// <param name="args">任意可变参数</param>
    public virtual void OnExit (params object[] args) { }

}
/// <summary>
/// 状态模板 具体状态只需要继承此类即可
/// </summary>
/// <typeparam name="T">拥有此状态的对象</typeparam>
public class StateTemplates<T> : FsmBase where T : class {
    public T m_owner; //状态的拥有者方便后期调用
    public StateTemplates (E_state _state, T t) : base (_state) {
        this.m_owner = t;
    }
}

以下代码在FIniteStateMachine.cs脚本中 也就是👆的状态管理机

//用个字典存所有状态
public Dictionary<E_state, FsmBase> m_allState;

/// <summary>
/// 上一个状态
/// </summary>
private FsmBase m_priousState;

/// <summary>
/// 当前状态
/// </summary>
private FsmBase m_curState;

//构造函数
public FiniteStateMachine () {
    m_priousState = null;
    m_curState = null;
    m_allState = new Dictionary<E_state, FsmBase> ();
}
/// <summary>
/// 状态注册
/// </summary>
/// <param name="_fsmBase">想要被注册的状态</param>
public void RegistState (FsmBase _fsmBase) {
    //没有这个状态就添加
    if (!m_allState.ContainsKey (_fsmBase.e_State))
        m_allState.Add (_fsmBase.e_State, _fsmBase);
}
//注销状态
public void UnRegistState (FsmBase _fsmBase) {
    if (m_allState.ContainsKey (_fsmBase.e_State))
        m_allState.Remove (_fsmBase.e_State);
}
/// <summary>
/// 改变状态
/// </summary>
/// <param name="_state">需要切换的下个状态</param>
/// <param name="args">可选参数</param>
public void ChangeState (E_state _state, params object[] args) {
    //没有这个状态 或 就是当前状态 就退出
    if (!m_allState.ContainsKey (_state) || _state == m_curState.e_State) return;
    if (m_curState == null) return;
    //本次状态退出
    m_curState.OnExit (args);
    //退出后把本次状态设为前一个状态
    m_priousState = m_curState;
    //根据想要更改的状态 去字典找 并设为当前状态
    m_curState = m_allState[_state];

    m_curState.OnEnter (args);
}
public E_state GetPriousState () {
    if (m_priousState != null)
        return m_priousState.e_State;

    return E_state.None;
}

public E_state GetCurState () {
    if (m_curState != null)
        return m_curState.e_State;

    return E_state.None;
}
public void UpdateState (params object[] args) {
    if (m_curState != null)
        m_curState.OnStay (args);
}

/// <summary>
/// 开始运作并进入一个状态
/// </summary>
/// <param name="_fsmBase">想要进入的状态</param>
/// <param name="args">可选参数</param>
public void Go (FsmBase _fsmBase, params object[] args) {
    if (m_curState != null) return;
    m_curState = _fsmBase;
    m_curState.OnEnter (args);
}

接下来添加需要的状态 为了展示就定义两种状态🤭 以下代码在PlayerVariedState.cs脚本

using UnityEngine;

public class PlayerIdle : StateTemplates<Player> {

    public PlayerIdle (E_state _state, Player _p) : base (_state, _p) { }

    public override void OnEnter (params object[] args) {
    // m_owner.GetComponent<MeshFilter> ()//获取本身组时只需用m_owner对象即可
        Debug.Log ("进入待机状态");
    }
    public override void OnStay (params object[] args) {
        Debug.Log ("保持待机状态");
        //当前状态持续进行 ,
    }
    public override void OnExit (params object[] args) {
        Debug.Log ("离开待机状态");
    }
}
public class PlayerJump : StateTemplates<Player> {

    public PlayerJump (E_state _state, Player _p) : base (_state, _p) { }

    public override void OnEnter (params object[] args) {
        Debug.Log ("进入跳状态");
    }
    public override void OnStay (params object[] args) {
        Debug.Log ("保持跳状态");
    }
    public override void OnExit (params object[] args) {
        Debug.Log ("离开跳状态");
    }
}

最后进行测试😪 以下代码在Player.cs脚本

using UnityEngine;

public class Player : MonoBehaviour {

    public FiniteStateMachine fsm;

    private void Start () {
        //造个状态机
        fsm = new FiniteStateMachine ();
        //实例化状态
        var tmpIdle = new PlayerIdle (E_state.Idle, this);
        var tmpJump = new PlayerJump (E_state.Jump, this);
        //注册
        fsm.RegistState (tmpIdle);
        fsm.RegistState (tmpJump);
        //启动机器
        fsm.Go (tmpIdle);
    }

    private void Update () {
        if (Input.GetKeyDown (KeyCode.Q))
            fsm.ChangeState (E_state.Idle);
       
        if (Input.GetKeyDown (KeyCode.W))
            fsm.ChangeState (E_state.Jump);
        
        fsm.UpdateState ();
    } 
}

总结

最后:

鄙人学习笔记,仅供参考,如有不正,欢迎指正👀

【参考1】传送门
【参考2】传送门

上一篇 下一篇

猜你喜欢

热点阅读