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自动完成状态切换
扩展性 添加新状态需要定义新的类,代码管理灵活 新状态需要通过界面添加,逻辑复杂时可能难以维护
复杂逻辑 适合处理复杂的状态切换和行为定义 主要用于控制动画状态,逻辑层次较浅
行为与状态绑定 每个状态都可以实现独特的行为 状态通常只是动画播放,不直接控制行为
相关推荐
巴伦是只猫18 分钟前
【机器学习笔记Ⅰ】13 正则化代价函数
人工智能·笔记·机器学习
好好研究1 小时前
学习栈和队列的插入和删除操作
数据结构·学习
新中地GIS开发老师2 小时前
新发布:26考研院校和专业大纲
学习·考研·arcgis·大学生·遥感·gis开发·地理信息科学
SH11HF3 小时前
小菜狗的云计算之旅,学习了解rsync+sersync实现数据实时同步(详细操作步骤)
学习·云计算
Frank学习路上3 小时前
【IOS】XCode创建firstapp并运行(成为IOS开发者)
开发语言·学习·ios·cocoa·xcode
Chef_Chen4 小时前
从0开始学习计算机视觉--Day07--神经网络
神经网络·学习·计算机视觉
X_StarX6 小时前
【Unity笔记02】订阅事件-自动开门
笔记·学习·unity·游戏引擎·游戏开发·大学生
MingYue_SSS6 小时前
开关电源抄板学习
经验分享·笔记·嵌入式硬件·学习
巴伦是只猫7 小时前
【机器学习笔记 Ⅱ】1 神经网络
笔记·神经网络·机器学习
weixin_437398217 小时前
转Go学习笔记(2)进阶
服务器·笔记·后端·学习·架构·golang