Godot 4 状态机教程:用 GDScript 实现简单 FSM (附代码)

YGUV 发布于 21 天前 93 次阅读


在游戏开发中,我们经常会遇到角色的行为逻辑变得越来越复杂。一开始你可能只有“站立”和“行走”,用几个 if-else 就能搞定。但当你加入了“跳跃”、“攻击”、“受伤硬直”后,你的代码可能会充斥着 is_jumpingis_attacking 这样的布尔变量,最终变成难以维护的“意大利面条代码”。

为了解决这个问题,我们需要引入有限状态机 (Finite State Machine, FSM)。在这篇 Godot GDScript 学习笔记中,我将分享如何编写一个清晰、可复用的Godot 状态机框架。

Godot 状态机示意图

什么是有限状态机 (FSM)?

简单来说,状态机就是把角色的每一个动作(如:待机、跑、跳)看作一个独立的状态 (State)。 在任何时刻,角色只能处于一种状态中。当特定条件满足时(比如按下了空格键),角色会从“待机状态”切换到“跳跃状态”。

这样做的好处是:每个状态只负责自己的逻辑,互不干扰。

Godot 状态机核心代码实现

我们在 Godot 4.0 中实现状态机,通常采用“节点式”设计。我们需要两个核心脚本:一个状态基类 (Base State) 和一个状态管理器 (State Machine)

1. 定义状态基类 (State.gd)

首先,我们创建一个 State.gd,所有的具体状态(如 Idle, Run)都将继承这个类。

GDScript

class_name State
extends Node

# 保存对角色(Player)的引用,以便控制移动等
var player: CharacterBody2D

# 当进入该状态时触发(类似 _ready)
func enter() -> void:
    pass

# 当退出该状态时触发(用于清理)
func exit() -> void:
    pass

# 对应 _process
func update(_delta: float) -> void:
    pass

# 对应 _physics_process
func physics_update(_delta: float) -> void:
    pass

2. 构建状态管理器 (StateMachine.gd)

接下来是核心的大脑。创建一个 StateMachine.gd,把它挂载到 Player 节点下的一个子节点上。

GDScript

class_name StateMachine
extends Node

# 在编辑器中指定初始状态
@export var initial_state: State

# 记录当前状态
var current_state: State

# 存储所有状态节点的字典
var states: Dictionary = {}

func _ready():
    # 等待父节点(Player)就绪
    await owner.ready
    
    # 遍历所有子节点,如果它是 State 类型,就初始化它
    for child in get_children():
        if child is State:
            states[child.name.to_lower()] = child
            # 将父节点(Player)传递给状态,方便状态控制角色
            child.player = owner 
    
    # 启动初始状态
    if initial_state:
        initial_state.enter()
        current_state = initial_state

func _process(delta):
    if current_state:
        current_state.update(delta)

func _physics_process(delta):
    if current_state:
        current_state.physics_update(delta)

# 状态切换的核心函数
func transition_to(key: String):
    # 如果目标状态不存在,直接返回
    if not states.has(key.to_lower()):
        return
    
    var new_state = states[key.to_lower()]
    
    # 1. 退出旧状态
    if current_state:
        current_state.exit()
    
    # 2. 切换新状态
    current_state = new_state
    
    # 3. 进入新状态
    current_state.enter()

3. 实战:创建一个“待机”状态 (PlayerIdle.gd)

现在架构搭好了,我们来写一个具体的逻辑。创建一个继承自 State 的脚本 PlayerIdle.gd

GDScript

extends State

func enter():
    # 当进入待机状态时,播放待机动画,并将速度归零
    player.velocity = Vector2.ZERO
    player.animation_player.play("idle")

func physics_update(_delta: float):
    # 落地重力逻辑(如果有的话)在此处处理
    
    # 检测输入,如果按下移动键,切换到 Run 状态
    if Input.get_axis("ui_left", "ui_right"):
        # 注意:这里的 "Run" 必须对应节点树里的节点名
        get_parent().transition_to("Run")
        
    # 检测输入,如果按下跳跃键,切换到 Jump 状态
    if Input.is_action_just_pressed("ui_accept"):
        get_parent().transition_to("Jump")

总结

通过上面这段代码,我们在 Godot 中建立了一个清晰的 FSM 框架。以后如果想加一个“滑翔”动作,只需要新建一个 Glide 脚本继承 State,然后写上滑翔独有的逻辑,完全不用担心会把“行走”的代码搞乱。

这就是面向对象编程在游戏开发中的魅力。

如果你对这个 Godot 状态机实现有任何疑问,或者有更好的优化思路,欢迎在评论区留言交流!

更多游戏开发笔记,欢迎访问我的 [博客首页]

参考资料:Godot 官方文档 - 脚本

此作者没有提供个人介绍。
最后更新于 2025-12-29