Unity2D - 状态机(State Machine)详解

1. 状态机概述

在角色的生成中,由于事件的不同,动作的不同,角色会处于不同的状态中。例如对战冒险游戏,面临Boss的攻击,角色会受到例如中毒,恐惧等Debuff效果,若单纯的在一个脚本中使用if等语句进行事件处理,往往会比较冗杂,程序出现bug后也无法及时排查出问题。

状态机此时就是一个很好解决上述问题的方式。例如在玩家移动过程中,此时的状态为移动状态。设定好不同状态之后。只需要相应的判定条件即可进入状态执行对应事件。

2. 初始化状态机

2.1. PlayerStateMachine

cs 复制代码
public class PlayerStateMachine : MonoBehavior
{
    public PlayerState currentState {get; privet set;} //申明一个状态
    
    public void Initialize(PlayerState _startState)
    {
        currentState = _startState; //初始化状态
        currentState.Enter(); //进入状态
    }

    public void ChangeState(PlayerState _newState)
    {
        currentState.Exit(); // 退出当前状态
        currentState = _newState;
        currentState.Enter();
    }
}

定义PlayerStateMachine类的作用类似于一个机器,将player的各个状态进行调配,例如状态机所含Initialize方法初始化状态,以及改变当前状态。

在改变当前状态时,先要执行状态中的Exit()方法,为后续动作增加了更大的可能性。

2.2. PlayerState

cs 复制代码
public class PlayerState
{
    protected PlayerStateMachine stateMachine;
    protected Player player; 
    protected string animatorName;

    public PlayerState(Player _player, PlayerStateMachine _stateMachine, string _animBoolName) //   构造函数,将上述变量传入
    {
        this.player = _player; //获取player,也可以调用player中的参数    
        this.stateMachine = _stateMachine;
        this.animatorName = _animBoolName; //动画参数的名字
    }

    public virtual void Enter()
    {
        rb = player.rb;
        player.anim.SetBool(animatorName,true);
    }

    public virtual void Update()
    {
        xInput = Input.GetAxisRaw("Horizontal"); //任意状态都可以持续调用
    }

    public virtual void Exit()
    {
        player.anim.SetBool(animatorName,false); //退出当前状态时,将动画参数设置为false
    }

}

与其说PlayerState是各个状态的模板,更不如说是各状态共有的特征。简化了代码,各状态所需要的初始化在PlayerState类中以及设定好了,在后续编写状态代码时只需简单的base.Update();即可。

在父类状态中想要一直持续调用的事件,在后续也可以传导给子类状态,例如想要一直获取角色在水平轴上的Input.GetAxisRaw。通过继承的方式,子类无论在何种状态,都在持续调用这段代码。

2.3. Player

cs 复制代码
public class Player: MonoBehaviour
{
    //声明组件以及所需要的状态
    public Rigidbody2D rb {get;private set;}
    public Animator anim {get;private set;}

    public PlayerStateMachine stateMachine {get;private set}
    public PlayerIdleState idleState {get;private set}

    private void Awake()   //初始化该player所拥有的状态与状态机,在实例化对象之前加载
    {
        stateMachine = new PlayerStateMachine();
        idleState = new PlayerIdleState(this,StateMachine,"Idle");
    }

    private void Start()
    {
        anim = GetComponentInChildren<Animator>();
        rb = GetComponent<Rigidbody2D>();
        StateMachine.Initialize(idleState); #初始化idle状态
    }

    private void Update()
    {
        StateMachine.currentState.Update(); //通过player的持续调用,持续调用当前状态的Update
    }
}

Player是应用的我们角色的脚本,实现为player声明好对应的状态机,更好的去初始化与改变当前状态。

通过Unity引擎自带的Update方法,进一步实现每个状态中我们所编写的Update方法。

3. 状态机的应用

3.1. Idle状态

cs 复制代码
public class PlayerIdleState : PlayerGroundedState
{
    public PlayerIdleState(Player _player, PlayerStateMachine _stateMachine, string _animBoolName) : base(_player, _stateMachine, _animBoolName)
    {
    }

    public override void Enter()
    {
        base.Enter();
    }

    public override void Exit()
    {
        base.Exit();
    }

    public override void Update() //持续调用
    {
        base.Update();
        if (xInput != 0)
        {
            stateMachine.ChangeState(player.moveState);
        }
    }
}
相关推荐
科技道人3 小时前
记录 默认置灰/禁用 app ‘Search Engine Selector‘ 的disable按钮
开发语言·前端·javascript
逝水无殇5 小时前
C# 异常处理详解
开发语言·后端·c#
言乐66 小时前
Python实现可运行解密游戏游戏框架
python·游戏·小程序·游戏程序·关卡设计
玖玥拾7 小时前
C# 语言进阶(十五)C# 游戏服务端 MySQL 数据库
服务器·开发语言·网络·数据库·mysql·c#
铅笔侠_小龙虾7 小时前
Rust 学习目录
开发语言·学习·rust
云泽8087 小时前
从零吃透 C++ 异常:抛出捕获、栈展开、异常重抛与编码规范详解
开发语言·c++·代码规范
灯澜忆梦7 小时前
GO---可见性规则
开发语言·golang
逝水无殇8 小时前
C# 文件的输入与输出详解
开发语言·数据库·后端·c#
这是个栗子9 小时前
前端开发中的常用工具函数(八)
开发语言·前端·javascript
凤凰院凶涛QAQ9 小时前
《Java版数据结构 & 集合类剖析》栈与队列:“push/pop 是栈的灵魂,offer/poll 是队列的骨架——四组 API,两种人生”
java·开发语言·数据结构