You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

Godot 2D动作平台游戏玩家有限状态机实现方法

Godot 2D平台玩家有限状态机完整实现方案

以下实现完全匹配GDQuest的FSM架构设计,可直接基于你已有的代码框架补全,兼容你已经写完的KinematicBody2D移动、跳跃/二段跳核心逻辑。


1. 补全核心基类代码

所有状态、状态机的通用逻辑全部抽离到基类,避免重复代码。

State虚基类

所有状态的顶层父类,定义统一的生命周期接口:

# State.gd
extends Node
class_name State

var state_machine: Node  # 持有所属状态机的引用
var host: Node           # 持有状态挂载对象(玩家)的引用

# 生命周期虚方法,子类按需重写
func _enter(msg: Dictionary = {}) -> void:
    pass  # 进入状态时触发,用于初始化、接收传参
func _exit() -> void:
    pass  # 离开状态时触发,用于重置状态、释放资源
func _physics_process(delta: float) -> void:
    pass  # 物理帧更新,放移动、碰撞检测逻辑
func _unhandled_input(event: InputEvent) -> void:
    pass  # 输入监听,放按键触发的状态跳转逻辑

StateMachine通用管理类

挂载在玩家节点下,负责状态注册、切换、生命周期事件转发:

# StateMachine.gd
extends Node
class_name StateMachine

export var initial_state: NodePath  # 编辑器里指定初始状态
var current_state: State
var states: Dictionary = {}

func _ready() -> void:
    # 注册所有子节点为可用状态
    for child in get_children():
        states[child.name] = child
        child.state_machine = self
        child.host = get_parent()
    # 启动初始状态
    current_state = get_node(initial_state)
    current_state._enter()

# 统一状态切换接口
func transition_to(target_state_name: String, msg: Dictionary = {}) -> void:
    if not states.has(target_state_name):
        push_warning("目标状态不存在:%s" % target_state_name)
        return
    if current_state:
        current_state._exit()
    current_state = states[target_state_name]
    current_state._enter(msg)

# 把引擎回调转发给当前激活状态
func _physics_process(delta: float) -> void:
    if current_state:
        current_state._physics_process(delta)
func _unhandled_input(event: InputEvent) -> void:
    if current_state:
        current_state._unhandled_input(event)

PlayerState玩家状态基类

抽离所有玩家状态共用的属性、移动逻辑:

# PlayerState.gd
extends State
class_name PlayerState

# 提前引用玩家节点下的常用组件
onready var player: KinematicBody2D = host
onready var sprite: Sprite = player.get_node("Sprite")
onready var animation_player: AnimationPlayer = player.get_node("AnimationPlayer")

# 移动参数(和你之前的配置对齐即可)
export var move_speed: float = 320.0
export var jump_force: float = -550.0
export var double_jump_force: float = -480.0
var velocity: Vector2 = Vector2.ZERO
var can_double_jump: bool = true

# 通用水平移动逻辑
func _apply_horizontal_movement(delta: float) -> void:
    var input_dir = Input.get_action_strength("move_right") - Input.get_action_strength("move_left")
    velocity.x = input_dir * move_speed
    if input_dir != 0:
        sprite.flip_h = input_dir < 0

2. 补全具体玩家状态逻辑

PlayerIdle 待机状态

# PlayerIdle.gd
extends PlayerState

func _enter(msg: Dictionary = {}) -> void:
    animation_player.play("idle")
    velocity.x = 0
    can_double_jump = true  # 落地重置二段跳标记

func _unhandled_input(event: InputEvent) -> void:
    if event.is_action_pressed("jump"):
        velocity.y = jump_force
        state_machine.transition_to("PlayerJump", {"velocity": velocity})
        return
    var input_dir = Input.get_action_strength("move_right") - Input.get_action_strength("move_left")
    if abs(input_dir) > 0.1:
        state_machine.transition_to("PlayerMove")

func _physics_process(delta: float) -> void:
    velocity.y += player.gravity_scale * ProjectSettings.get_setting("physics/2d/default_gravity") * delta
    velocity = player.move_and_slide(velocity, Vector2.UP)
    if not player.is_on_floor():
        state_machine.transition_to("PlayerFall", {"velocity": velocity})

PlayerMove 地面移动状态

# PlayerMove.gd
extends PlayerState

func _enter(msg: Dictionary = {}) -> void:
    animation_player.play("move")
    can_double_jump = true

func _unhandled_input(event: InputEvent) -> void:
    if event.is_action_pressed("jump"):
        velocity.y = jump_force
        state_machine.transition_to("PlayerJump", {"velocity": velocity})

func _physics_process(delta: float) -> void:
    _apply_horizontal_movement(delta)
    velocity.y += player.gravity_scale * ProjectSettings.get_setting("physics/2d/default_gravity") * delta
    velocity = player.move_and_slide(velocity, Vector2.UP)
    
    if not player.is_on_floor():
        state_machine.transition_to("PlayerFall", {"velocity": velocity})
    elif abs(velocity.x) < 10:
        state_machine.transition_to("PlayerIdle")

PlayerJump 跳跃/二段跳状态

# PlayerJump.gd
extends PlayerState

func _enter(msg: Dictionary = {}) -> void:
    animation_player.play("jump")
    if msg.has("velocity"):
        velocity = msg["velocity"]

func _unhandled_input(event: InputEvent) -> void:
    if event.is_action_pressed("jump") and can_double_jump:
        velocity.y = double_jump_force
        can_double_jump = false
        animation_player.play("double_jump")

func _physics_process(delta: float) -> void:
    _apply_horizontal_movement(delta)
    velocity.y += player.gravity_scale * ProjectSettings.get_setting("physics/2d/default_gravity") * delta
    velocity = player.move_and_slide(velocity, Vector2.UP)
    if velocity.y > 0:
        state_machine.transition_to("PlayerFall", {"velocity": velocity})

PlayerFall 下落状态

# PlayerFall.gd
extends PlayerState

func _enter(msg: Dictionary = {}) -> void:
    animation_player.play("fall")
    if msg.has("velocity"):
        velocity = msg["velocity"]

func _unhandled_input(event: InputEvent) -> void:
    if event.is_action_pressed("jump") and can_double_jump:
        velocity.y = double_jump_force
        can_double_jump = false
        state_machine.transition_to("PlayerJump", {"velocity": velocity})

func _physics_process(delta: float) -> void:
    _apply_horizontal_movement(delta)
    velocity.y += player.gravity_scale * ProjectSettings.get_setting("physics/2d/default_gravity") * delta
    velocity = player.move_and_slide(velocity, Vector2.UP)
    if player.is_on_floor():
        if abs(velocity.x) > 10:
            state_machine.transition_to("PlayerMove")
        else:
            state_machine.transition_to("PlayerIdle")

3. 场景配置步骤

  • 打开玩家KinematicBody2D场景,添加一个StateMachine类型子节点,命名为StateMachine
  • 在StateMachine节点下添加4个普通Node子节点,分别挂载上述4个玩家状态脚本,节点命名必须和代码里transition_to传入的字符串完全一致:PlayerIdle、PlayerMove、PlayerJump、PlayerFall
  • 选中StateMachine节点,在Inspector面板把Initial State属性指定为PlayerIdle节点路径
  • 删除玩家根节点上之前写的移动、跳跃逻辑代码,所有逻辑已经拆分到对应状态中

常见问题排查

  • 状态跳转无响应:检查状态节点命名大小写是否匹配,initial_state路径是否配置正确
  • 二段跳失效:确认Idle、Move状态进入时can_double_jump是否重置为true
  • 移动手感不对:直接调整PlayerState里的速度、跳跃力参数,和你之前的配置对齐即可

内容的提问来源于stack exchange,提问作者DeruDeru

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.08.27 05:18:20