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自动完成状态切换
扩展性 添加新状态需要定义新的类,代码管理灵活 新状态需要通过界面添加,逻辑复杂时可能难以维护
复杂逻辑 适合处理复杂的状态切换和行为定义 主要用于控制动画状态,逻辑层次较浅
行为与状态绑定 每个状态都可以实现独特的行为 状态通常只是动画播放,不直接控制行为
相关推荐
你好,赵志伟40 分钟前
线性代数考研笔记
笔记·线性代数·考研
胜天半月子41 分钟前
Python | 学习type()方法动态创建类
开发语言·python·学习
Vizio<2 小时前
微机——计算机中的数制
笔记·单片机·51单片机
我爱学习_zwj2 小时前
防御式CSS是一种编写CSS的方法,旨
笔记
123yhy传奇3 小时前
【学习总结|DAY027】JAVA操作数据库
java·数据库·spring boot·学习·mybatis
汇能感知7 小时前
多光谱图像的处理和分析方法有哪些?
经验分享·笔记·科技
东京老树根8 小时前
SAP SD学习笔记23 - 无偿出荷(免费交货)与继续无偿出荷(继续免费交货)
笔记·学习
xing.yu.CTF8 小时前
HTML基础到精通笔记
前端·笔记·html
垂杨有暮鸦⊙_⊙8 小时前
有限元分析学习——Anasys Workbanch第一阶段笔记(2)应力奇异及位移结果对比、初步了解单元的基本知识
笔记·学习·有限元分析