Unity学习笔记(五)什么是状态机

前言

本文为 Udemy 课程 The Ultimate Guide to Creating an RPG Game in Unity 学习笔记。

前面开发模式的问题

之前的开发中,我们的代码都集中在 Player2 这个文件下,这种开发模式存在以下缺点:

  • 代码在同一个文件中,代码量大后非常难以维护。
  • 角色的行为逻辑直接在对应的方法中维护,后续新增功能会频繁修改这些方法,容易导致大量 Bug。

例如:

一开始实现的移动代码

csharp 复制代码
private void Movement() {
    rb.velocity = new Vector2(xInput * moveSpeed, rb.velocity.y);
}

加入冲刺后

csharp 复制代码
private void Movement() {
    xInput = Input.GetAxisRaw("Horizontal");
    if (dashTime > 0) {
        rb.velocity = new Vector2(facingDir * dashSpeed, 0);
    } else {
        rb.velocity = new Vector2(xInput * moveSpeed, rb.velocity.y);
    }
}

再加入攻击后

csharp 复制代码
private void Movement() {
    xInput = Input.GetAxisRaw("Horizontal");
    if (isAttacking) {
        rb.velocity = new Vector2(0, 0);
    } else if (dashTime > 0) {
        rb.velocity = new Vector2(facingDir * dashSpeed, 0);
    } else {
        rb.velocity = new Vector2(xInput * moveSpeed, rb.velocity.y);
    }
}

每次新增功能,都需要修改 Movement() 方法,导致代码越来越臃肿,维护起来非常困难。

使用状态模式解决

为了避免上述问题,可以使用**状态模式(状态机)**来实现。


什么是状态模式(State Pattern)?

状态模式是一种面向对象的设计模式,其核心思想是:

当对象的内部状态改变时,其行为也随之改变

通过将对象的行为和状态分离,每个状态封装为独立的类,从而实现状态切换的清晰性和易扩展性。


状态模式的结构

状态模式通常包括以下几个关键部分:

1. 上下文(Context)

  • 表示当前工作的对象,包含当前状态的引用。
  • 通过调用当前状态的方法完成具体的行为。

2. 状态接口(State)

  • 定义接口或抽象类,用于表示所有可能的状态。
  • 每个状态都实现该接口。

3. 具体状态(Concrete States)

  • 实现状态接口的类,每个类表示一种特定状态并定义该状态的具体行为。

4. 状态切换

  • 状态切换通过上下文类完成,可以由上下文主动切换,也可以由状态自身决定何时切换。

状态模式的示例代码

以下是使用状态模式模拟角色行为(站立、行走、跑步)的代码示例:

1. 定义状态接口

csharp 复制代码
public interface IState {
    void EnterState(Player player);
    void UpdateState(Player player);
}

2. 定义具体状态

csharp 复制代码
public class IdleState : IState {
    public void EnterState(Player player) {
        Debug.Log("进入站立状态");
    }

    public void UpdateState(Player player) {
        if (player.Speed > 0) {
            player.SetState(new WalkState());
        }
    }
}

public class WalkState : IState {
    public void EnterState(Player player) {
        Debug.Log("进入行走状态");
    }

    public void UpdateState(Player player) {
        if (player.Speed == 0) {
            player.SetState(new IdleState());
        } else if (player.Speed > 5) {
            player.SetState(new RunState());
        }
    }
}

public class RunState : IState {
    public void EnterState(Player player) {
        Debug.Log("进入跑步状态");
    }

    public void UpdateState(Player player) {
        if (player.Speed <= 5) {
            player.SetState(new WalkState());
        }
    }
}

3. 上下文类

csharp 复制代码
public class Player {
    public float Speed { get; set; }
    private IState currentState;

    public Player() {
        currentState = new IdleState();
        currentState.EnterState(this);
    }

    public void SetState(IState newState) {
        currentState = newState;
        currentState.EnterState(this);
    }

    public void Update() {
        currentState.UpdateState(this);
    }
}

运行流程

  1. Player 是上下文类,包含当前状态的引用 currentState
  2. IdleStateWalkStateRunState 是具体状态,实现了 IState 接口。
  3. 根据 Speed 值切换状态:
    • Speed == 0:切换到 IdleState
    • 0 < Speed <= 5:切换到 WalkState
    • Speed > 5:切换到 RunState

Unity 中的状态机

Unity Animator 中的状态机

Animator 状态机 是 Unity 提供的用于动画控制的可视化工具。其核心包括:

  1. 状态(State)

    • 每个状态代表一种动画或行为。
    • 例如:
      • Idle:站立状态。
      • Walk:行走状态。
      • Run:跑步状态。
  2. 过渡(Transition)

    • 状态之间通过过渡连接,过渡规则决定状态切换条件。
  3. 参数(Parameters)

    • 用于控制状态切换的输入值,如:
      • Float(如 speed)。
      • Bool(如 isJumping)。
      • Trigger(如 jumpTrigger)。

Animator 的使用步骤

  1. 创建 Animator Controller

    • 在 Unity 中右键 -> Create -> Animator Controller
    • 将 Animator Controller 添加到角色的 Animator 组件中。
  2. 创建动画状态

    • 双击 Animator Controller 打开 Animator 窗口。
    • 右键 -> Create State -> 添加动画状态,将动画片段拖入状态。
  3. 设置参数

    • 在 Animator 的 Parameters 面板中,添加所需的参数(Float、Bool、Trigger 等)。
  4. 添加过渡

    • 在状态之间右键 -> Make Transition,设置条件。
  5. 通过代码控制

    csharp 复制代码
    Animator animator;
    
    void Start() {
        animator = GetComponent<Animator>();
    }
    
    void Update() {
        animator.SetFloat("speed", Input.GetAxis("Vertical"));
    
        if (Input.GetKeyDown(KeyCode.Space)) {
            animator.SetBool("isJumping", true);
        }
    
        if (Input.GetMouseButtonDown(0)) {
            animator.SetTrigger("attackTrigger");
        }
    }

状态模式与Unity中的状态机的关系

Unity中的状态机(如Animator状态机)并非严格的状态模式实现,但它是状态模式的一种具体应用。以下是两者的异同点:

特性 状态模式(State Pattern) Unity Animator 状态机
状态实现 通过代码实现,每个状态是一个独立的类 通过可视化界面实现,每个状态对应一个动画片段
状态切换 显式调用代码切换状态 基于参数和条件,Animator自动完成状态切换
扩展性 添加新状态需要定义新的类,代码管理灵活 新状态需要通过界面添加,逻辑复杂时可能难以维护
复杂逻辑 适合处理复杂的状态切换和行为定义 主要用于控制动画状态,逻辑层次较浅
行为与状态绑定 每个状态都可以实现独特的行为 状态通常只是动画播放,不直接控制行为
相关推荐
数据门徒31 分钟前
《人工智能现代方法(第4版)》 第7章 逻辑智能体 学习笔记
人工智能·笔记·学习
不蒸馒头曾口气37 分钟前
申论素材学习笔记-以产业振兴激活乡村全面振兴
笔记·学习
做一道光1 小时前
电机控制——电流采样(双电阻)
笔记·单片机·嵌入式硬件·电机控制
wallace20181 小时前
笔记:SpringCloud服务间调用的方式
笔记
愚昧之山绝望之谷开悟之坡2 小时前
什么是大非农
笔记
小小的橙菜吖!2 小时前
联合体的学习
学习·算法
Chloeis Syntax2 小时前
MySQL初阶学习日记(4)--- 插入、聚合、分组查询 + 数据库约束
数据库·笔记·学习·mysql
淡海水2 小时前
【节点】[Blackbody节点]原理解析与实际应用
unity·游戏引擎·shadergraph·图形·blackbody
盐焗西兰花2 小时前
鸿蒙学习实战之路 - 应用追踪实践最佳实践
学习·华为·harmonyos
四谎真好看2 小时前
Java 黑马程序员学习笔记(进阶篇31)
java·笔记·学习·学习笔记