Unity状态机FSM

Unity状态机FSM

一:状态机介绍

有限状态机,也称为 FSM(Finite State Machine) ,这些状态是有限的、不重叠的,其在任意时刻都处于有限状态集合中的某一状态。当其获得特定输入时,将从当前状态转换到另一个状态 ,或者仍然保持在当前状态

状态机的应用领域

--- 玩家动作控制:比如一个玩家动作较多,我们可以使用状态机进行管理

--- UI界面的切换与管理

--- 怪物AI的设计

 

二:状态机设计实例

我们以案例为基础,设计一个人物有三种状态,在三种状态之间自由切换。

我先把里面的几个类描述一下

StateBase所有状态基类

StateMachine状态机类,负责管理所有的状态以及切换

PlayerCtrl玩家控制类,包含一个状态机以及自身控制逻辑

IdleStateRunState,AttackState三种状态

StateTemplate<T>泛型类,为了解决人物和怪物都存在状态机的时候,可以指定对应的所有者

 

 

 

2.1 设计状态机基类 

一个状态机里面,有很多种状态,每一种状态都有很多相似的特征,这里我们需要一个状态基类。基类里面包含所有状态的基本信息

using System.Collections;

using System.Collections.Generic;

using UnityEngine;

 

public abstract class StateBase

{

    /// <summary> 

    /// 每个状态对应不同的ID号

    /// </summary> 

    public int ID { get; private set; }

    /// <summary> 

    /// 状态机

    /// </summary> 

    public StateMachine machine;

    /// <summary> 

    /// Construtor

    /// </summary> 

    /// <param name="id">状态的id号(id号有对应的枚举)</param> 

    public StateBase(int id)

    {

        ID = id;

}

//进入状态

public virtual void OnEnter(params object[] args) { }

//状态停留

public virtual void OnStay(params object[] args) { }

//状态退出

public virtual void OnExit(params object[] args) { }

    //检查状态

 public virtual void OnCheck(params object[] args) { }

 

}

public class StateTemplate<T> : StateBase

{

    /// <summary> 

    /// 状态的拥有者

    /// </summary> 

    public T m_owner;

    /// <summary> 

    /// Constructor

    /// </summary> 

    /// <param name="id">状态的id号</param> 

    /// <param name="owner">状态的拥有者</param> 

    public StateTemplate(int id, T owner) : base(id)

    {

        m_owner = owner;

    }

}

 

 

2.2 状态机类

using System.Collections;

using System.Collections.Generic;

using UnityEngine;

 

/// 状态机

/// </summary>

public class StateMachine

{

    /// <summary>

    /// 状态缓存

    /// </summary>

    public Dictionary<int, StateBase> m_StateCache;

    /// <summary>

    /// 当前状态

    /// </summary>

    public StateBase m_CurrentState;

    /// <summary>

    /// 当前状态的前一个状态

    /// </summary>

    public StateBase m_PreviousState;

 

    #region StateMachine  Constructor

    /// <summary>

    /// Constructor

    /// </summary>

    /// <param name="beginState">开始状态</param>

    public StateMachine(StateBase beginState)

    {

        m_PreviousState = null;

        m_CurrentState = beginState;

        m_StateCache = new Dictionary<int, StateBase>();

        //注册状态

        RegisterState(beginState);

        m_CurrentState.OnEnter();

    }

    #endregion

 

    #region FSMUpdate  状态机监测状态

    /// <summary>

    /// 状态机监测状态变化

    /// </summary>

    public void FSMUpdate()

    {

       if (m_CurrentState != null)

        {

            m_CurrentState.OnStay();

            m_CurrentState.OnCheck();

        }

    }

    #endregion

 

    #region TranslateToState 状态切换

    /// <summary>

    /// 状态切换

    /// </summary>

    /// <param name="id">目标状态的id号</param>

    /// <param name="args">可变参数</param>

    public void TranslateToState(int id, params object[] args)

    {

        int key_id = id;

        if (!m_StateCache.ContainsKey(key_id))

        {

            Debug.LogError("The key is not Exist");

            return;

        }

        //当前状态退出

        m_CurrentState.OnExit();

        //保存当前状态为下一个新状态的前一个状态

        m_PreviousState = m_CurrentState;

        //当前状态更新到下一个新状态

        m_CurrentState = m_StateCache[key_id];

        //新的状态开始进入

        m_CurrentState.OnEnter(args);

    }

    #endregion

 

    #region RegisterState   注册一个新的状态到缓存中

    /// <summary>

    /// 注册一个新的状态到缓存中

    /// </summary>

    /// <param name="aState">新状态</param>

    public void RegisterState(StateBase aState)

    {

        int id = aState.ID;

        //状态是否缓存了

        if (m_StateCache.ContainsKey(id))

        {

            Debug.LogError("The State has been added the Cache");

            return;

        }

        //缓存aState状态

        m_StateCache.Add(id, aState);

        //设置aState状态的状态机对象

        aState.machine = this;

    }

    #endregion

}

 

2.3 每种状态设计

AttackState代码如下:

using System.Collections;

using System.Collections.Generic;

using UnityEngine;

 

public class AttackState : StateTemplate<PlayerCtrl>

{

    public AttackState(int id, PlayerCtrl owner) : base(id, owner) {

 

    }

    public override void OnEnter(params object[] args) {

        base.OnEnter(args);

        Debug.Log("AttackState Enter");

        m_owner.GetComponent<MeshRenderer>().material.color = Color.red;

    }

    public override void OnStay(params object[] args) {

        base.OnExit(args);

        machine.TranslateToState(3);

    }

    public override void OnExit(params object[] args) {

        base.OnExit(args);

        Debug.Log("Attack Exit");

    }

    public override void OnCheck(params object[] args)

    {

        base.OnCheck(args);

    }

}

 

 

IdleState状态代码如下:

using UnityEngine;

using System.Collections;

public class IdleState : StateTemplate<PlayerCtrl>

{

    public IdleState(int id, PlayerCtrl owner) : base(id, owner)

    { }

    public override void OnEnter(params object[] args)

    {

        base.OnEnter(args);

        Debug.Log("IdleState Enter");

        m_owner.GetComponent<MeshRenderer>().material.color = Color.blue;

 

    }

    public override void OnStay(params object[] args)

    {

        base.OnStay(args);

    }

    public override void OnExit(params object[] args)

    {

        base.OnExit(args);

        Debug.Log("IdleState OnExit");

    }

    public override void OnCheck(params object[] args)

    {

 

    }

}

 

RunState代码如下:

using System.Collections;

using System.Collections.Generic;

using UnityEngine;

 

public class RunState : StateTemplate<PlayerCtrl>

{

    public RunState(int id, PlayerCtrl owner) : base(id, owner)

    { }

    public override void OnEnter(params object[] args)

    {

        base.OnEnter(args);

        Debug.Log("RunState Enter");

        m_owner.GetComponent<MeshRenderer>().material.color = Color.green;

    }

    public override void OnStay(params object[] args)

    {

        base.OnStay(args);

    }

    public override void OnExit(params object[] args)

    {

        base.OnExit(args);

        Debug.Log("RunState OnExit");

    }

    public override void OnCheck(params object[] args)

    {

 

    }

}

2.4 玩家控制类实现

using System.Collections;

using System.Collections.Generic;

using UnityEngine;

 

public class PlayerCtrl : MonoBehaviour

{

    StateMachine m_stateMachine;

    // Start is called before the first frame update

    void Start()

    {

        //初始化状态机

        m_stateMachine = new StateMachine(new IdleState(1,this));

        //注册人物的所有状态

        InitState();

    }

    void InitState() {

        m_stateMachine.RegisterState(new RunState(2,this));

        m_stateMachine.RegisterState(new AttackState(3, this));

    }

    void LateUpdate()

{

        m_stateMachine.FSMUpdate();

    }

    // Update is called once per frame

    void Update()

    {

        if (Input.GetKeyDown(KeyCode.A))

        {

            m_stateMachine.TranslateToState(2);

        }

        if (Input.GetKeyDown(KeyCode.B))

        {

            m_stateMachine.TranslateToState(3);

        }

        if (Input.GetKeyDown(KeyCode.C))

        {

            m_stateMachine.TranslateToState(1);

        }

    }

}

将该脚本挂载在立方体身上进行测试,按键盘上的ABC可以实现红绿蓝三种状态之间切换。

2.5 总结变化

(1)当把颜色变化,切换为Animator变化。其实本质上是一样的,在进入的时候播放对应的动画即可。在OnCheck的时候检查动画是否播放完毕,进行对应的状态切换。记住:在状态内部也是可以调用TranslateToState的,因为本身每个状态里面包含了对应的状态机

(2)如果是满足每个状态2s时间,进行切换怎么办?其实也是类似的,在OnCheck函数内部不停检测时间即可,根据自己具体的逻辑实现即可。

(3)不管状态机写法怎么变化,本质思想是一样的。

(4)将状态的ID从int类型修改为枚举更容易表达对应的含义。

 

  • 2
    点赞
  • 7
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值