在游戏开发中,我们经常会遇到角色的行为逻辑变得越来越复杂。一开始你可能只有“站立”和“行走”,用几个
if-else就能搞定。但当你加入了“跳跃”、“攻击”、“受伤硬直”后,你的代码可能会充斥着is_jumping、is_attacking这样的布尔变量,最终变成难以维护的“意大利面条代码”。为了解决这个问题,我们需要引入有限状态机 (Finite State Machine, FSM)。在这篇 Godot GDScript 学习笔记中,我将分享如何编写一个清晰、可复用的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 官方文档 - 脚本
Comments NOTHING