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

Godot 4.6.2中Koopa从壳状态恢复后速度异常问题

Koopa从壳状态恢复后无法移动的问题分析

问题描述

使用Godot 4.6.2重制原版《超级马里奥兄弟》时,踩击Koopa使其进入壳状态后,10秒后恢复正常状态,但Koopa无法移动,速度始终为0。

Koopa节点树

Koopa
├╴AnimatedSprite2D
├╴CollisionShape2D
├╴SmushedArea
│ └╴SmushedCollision
├╴KillerArea
│ └╴KillerCollision
└╴ShelledTimer

核心代码

extends CharacterBody2D

# editables
@export var max_speed := 1600.0
@export_enum('Alive', 'Shelled', 'Dead') var state := 'Alive'

@onready var prev_state := state

@onready var direction := max_speed

# inner nodes
@onready var sprite := $AnimatedSprite2D
@onready var collider := $CollisionShape2D

@onready var smushed := $SmushedArea
@onready var killer := $KillerArea

@onready var shelled_timer := $ShelledTimer

# player
@onready var player := $'..'/Player
@onready var smusher := $'..'/Player/SmusherArea
@onready var killed := $'..'/Player/KilledArea

@onready var camera := $'..'/Camera2D

func _physics_process(delta: float) -> void:
    if not is_on_floor():
        velocity += get_gravity() * delta
        
    if is_on_wall():
        direction *= -1
        
    velocity.x = direction * delta
    
    move_and_slide()

func _process(delta: float) -> void:
    if velocity.x != 0:
        if abs(velocity.x) != velocity.x:
            sprite.flip_h = true
        if abs(velocity.x) == velocity.x:
            sprite.flip_h = false
            
    print(velocity.x)
    
    if state != prev_state:
        if state == 'Alive':
            killer.set_deferred('monitoring', true)
            smushed.set_deferred('monitoring', true)
            
            velocity.x = direction * delta
            
            sprite.play('walk')
            
        elif state == 'Shell':
            direction = 0
        
            killer.set_deferred('monitoring', false)
            smushed.set_deferred('monitoring', false)
            
            player.velocity.y *= -0.7
            velocity.y = -100
            velocity.x = 0
            
            sprite.play('shell')
            
            shelled_timer.start()
            
    if state == 'Shell' and shelled_timer.time_left < 3:
        sprite.play("wiggling")
        
    prev_state = state

func _on_smushed_area_entered(area: Area2D) -> void:
    if area == smusher and\
    (abs(player.velocity.y) == player.velocity.y) and player.velocity.y != 0:
        state = 'Shell'

func _on_killer_area_entered(area: Area2D) -> void:
    if area == killed and player.is_alive:
        camera.follow_player = false
        player.is_alive = false
        
        await get_tree().create_timer(0.5).timeout
        
        player.collider.set_deferred('disabled', true)
        player.killed.set_deferred('monitoring', false)
        player.smusher.set_deferred('monitorable', false)
        
        player.velocity.y = player.jump_height
        
        await get_tree().create_timer(2.0).timeout
        
        player.queue_free()

func _on_shelled_timer_timeout() -> void:
    state = 'Alive'

问题原因

核心问题是进入壳状态时将direction设为0,但恢复Alive状态时未重置direction的数值:

  1. 当Koopa被踩击进入Shell状态时,代码中执行了direction = 0,直接将控制移动的方向速度值清零。
  2. 10秒后计时器触发_on_shelled_timer_timeout()将状态设回Alive,此时_process函数的Alive分支仅尝试设置velocity.x = direction * delta,但direction仍为0,导致velocity.x始终为0,Koopa自然无法移动。

修复方案

方案1:恢复Alive状态时重置direction为初始速度

修改_process函数中state == 'Alive'的分支,添加direction重置逻辑:

if state == 'Alive':
    killer.set_deferred('monitoring', true)
    smushed.set_deferred('monitoring', true)
    
    # 重置direction为初始max_speed
    direction = max_speed
    velocity.x = direction
    
    sprite.play('walk')

方案2:保存进入壳前的方向,恢复时复用

  1. 先添加变量保存原始方向:
# 在变量定义区域添加
var original_direction := max_speed
  1. 修改Shell状态分支,保存当前方向:
elif state == 'Shell':
    original_direction = direction  # 保存进入壳前的移动方向
    direction = 0

    killer.set_deferred('monitoring', false)
    smushed.set_deferred('monitoring', false)
    
    player.velocity.y *= -0.7
    velocity.y = -100
    velocity.x = 0
    
    sprite.play('shell')
    
    shelled_timer.start()
  1. 修改Alive状态分支,恢复之前的方向:
if state == 'Alive':
    killer.set_deferred('monitoring', true)
    smushed.set_deferred('monitoring', true)
    
    direction = original_direction  # 恢复进入壳前的方向
    velocity.x = direction
    
    sprite.play('walk')

额外优化提示

_physics_process中的velocity.x = direction * delta存在逻辑错误:CharacterBody2D的velocity是每秒移动的像素数,不需要乘以delta(delta是帧间隔时间),否则移动速度会随帧率波动。正确写法应为:

velocity.x = direction

内容的提问来源于stack exchange,提问作者Mohammed H.

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.06.01 15:43:14