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

Godot 4角色按世界坐标系移动而非自身坐标系问题求助

Godot 4 角色按世界坐标系移动而非自身坐标系的修复方案

问题说明

作为Godot 4新手,修改代码后出现Bug:角色移动时遵循世界坐标系向量,而非相机/自身朝向的局部坐标系向量。已定位到Movement代码段,尝试还原原代码、咨询AI均未解决问题。以下是当前完整代码:

extends CharacterBody3D

# Player Nodes.
@onready var neck = $Neck
@onready var head = $Neck/Head
@onready var eyes = $Neck/Head/Eyes
@onready var player_camera = $Neck/Head/Eyes/PlayerCamera

# Speed Variables.
var current_speed = 0.0
var backward_speed = 0.0
const WALK_SPEED = 2.5
const SPRINT_SPEED = 5.0
const STEALTH_SPEED = 1.2

# States.
var walking = false
var sprinting = false
var stealthing = false

# Moving Variables.
var stealth_depth = -0.07
var lerp_speed = 10.0
var gravity = 9.8

# Input Variables.
var direction = Vector3.ZERO
const MOUSE_SENSITIVITY = 0.004

# Head Bob Variables.
var current_head_bob = 0.0
const HEAD_BOB_FREQUENCY = 0.0
const HEAD_BOB_AMPLITUDE = 0.0

func _ready():
    Input.set_mouse_mode(Input.MOUSE_MODE_CAPTURED)

func _process(_delta):
    
    # Quit.
    if Input.is_action_just_pressed("quit"):
        get_tree().quit()

func _unhandled_input(event):
    
    # Camera Movement.
    if event is InputEventMouseMotion:
        head.rotate_y(-event.relative.x * MOUSE_SENSITIVITY)
        player_camera.rotate_x(-event.relative.y * MOUSE_SENSITIVITY)
        player_camera.rotation.x = clamp(player_camera.rotation.x, deg_to_rad(-90), deg_to_rad(90))

func _physics_process(delta):
    
    # Movement Input.
    var input_dir = Input.get_vector("left", "right", "forward", "backward")
    
    # Head Bob.
    current_head_bob += delta * velocity.length() * float(is_on_floor())
    player_camera.transform.origin = _headbob(current_head_bob)
    
    # Backward Walk.
    if Input.is_action_pressed("backward"):
        backward_speed = 1.0
    else:
        backward_speed = 0.0
    
    # Stealth.
    if Input.is_action_pressed("stealth"):
        current_speed = lerp(current_speed, STEALTH_SPEED - backward_speed, delta * lerp_speed)
        head.position.y = lerp(head.position.y, 1.5 + stealth_depth, delta * lerp_speed)
        var _walking = false
        var _sprinting = false
        var _stealthing = true
    else: 
        head.position.y = lerp(head.position.y, 1.5, delta * lerp_speed)
        
        # Sprint.
        if Input.is_action_pressed("sprint"):
            current_speed = lerp(current_speed, SPRINT_SPEED - backward_speed, delta * lerp_speed)
            var _walking = false
            var _sprinting = true
            var _stealthing = false
        else:
            
            # Walk.
            current_speed = lerp(current_speed, WALK_SPEED - backward_speed, delta * lerp_speed)
            var _walking = true
            var _sprinting = false
            var _stealthing = false
    
    # Gravity.
    if not is_on_floor():
        velocity.y -= gravity * delta
    
    # Movement.
    if is_on_floor():
        direction = lerp(direction, (transform.basis * Vector3(input_dir.x, 0, input_dir.y)).normalized(), delta * lerp_speed)
    if direction:
        velocity.x = direction.x * current_speed
        velocity.z = direction.z * current_speed
    else:
        velocity.x = move_toward(velocity.x, 0.0, current_speed)
        velocity.z = move_toward(velocity.z, 0.0, current_speed)
    
    move_and_slide()

func _headbob(time) -> Vector3:
    var pos = Vector3.ZERO
    pos.y = sin(time * HEAD_BOB_FREQUENCY) * HEAD_BOB_AMPLITUDE
    pos.x = cos(time * HEAD_BOB_FREQUENCY / 2) * HEAD_BOB_AMPLITUDE
    return pos

问题根源

  1. 方向计算的Basis错误:代码中移动方向使用了角色本体(CharacterBody3D)的transform.basis,但相机旋转仅作用在head节点上,角色本体未跟随相机旋转,导致transform.basis始终对应世界坐标系朝向,而非玩家实际看向的方向。
  2. 状态变量赋值无效:条件判断中创建了_walking等局部变量,未给全局状态变量赋值,导致状态判断逻辑失效。

修复步骤

  1. 替换方向计算的Basis:将Movement段中的transform.basis改为head.transform.basis,使用相机所在的head节点朝向转换输入向量。
  2. 修正状态变量赋值:移除局部变量声明,直接给全局状态变量赋值。

修复后的完整代码

extends CharacterBody3D

# Player Nodes.
@onready var neck = $Neck
@onready var head = $Neck/Head
@onready var eyes = $Neck/Head/Eyes
@onready var player_camera = $Neck/Head/Eyes/PlayerCamera

# Speed Variables.
var current_speed = 0.0
var backward_speed = 0.0
const WALK_SPEED = 2.5
const SPRINT_SPEED = 5.0
const STEALTH_SPEED = 1.2

# States.
var walking = false
var sprinting = false
var stealthing = false

# Moving Variables.
var stealth_depth = -0.07
var lerp_speed = 10.0
var gravity = 9.8

# Input Variables.
var direction = Vector3.ZERO
const MOUSE_SENSITIVITY = 0.004

# Head Bob Variables.
var current_head_bob = 0.0
const HEAD_BOB_FREQUENCY = 0.0
const HEAD_BOB_AMPLITUDE = 0.0

func _ready():
    Input.set_mouse_mode(Input.MOUSE_MODE_CAPTURED)

func _process(_delta):
    # Quit.
    if Input.is_action_just_pressed("quit"):
        get_tree().quit()

func _unhandled_input(event):
    # Camera Movement.
    if event is InputEventMouseMotion:
        head.rotate_y(-event.relative.x * MOUSE_SENSITIVITY)
        player_camera.rotate_x(-event.relative.y * MOUSE_SENSITIVITY)
        player_camera.rotation.x = clamp(player_camera.rotation.x, deg_to_rad(-90), deg_to_rad(90))

func _physics_process(delta):
    # Movement Input.
    var input_dir = Input.get_vector("left", "right", "forward", "backward")
    
    # Head Bob.
    current_head_bob += delta * velocity.length() * float(is_on_floor())
    player_camera.transform.origin = _headbob(current_head_bob)
    
    # Backward Walk.
    if Input.is_action_pressed("backward"):
        backward_speed = 1.0
    else:
        backward_speed = 0.0
    
    # Stealth.
    if Input.is_action_pressed("stealth"):
        current_speed = lerp(current_speed, STEALTH_SPEED - backward_speed, delta * lerp_speed)
        head.position.y = lerp(head.position.y, 1.5 + stealth_depth, delta * lerp_speed)
        walking = false
        sprinting = false
        stealthing = true
    else: 
        head.position.y = lerp(head.position.y, 1.5, delta * lerp_speed)
        
        # Sprint.
        if Input.is_action_pressed("sprint"):
            current_speed = lerp(current_speed, SPRINT_SPEED - backward_speed, delta * lerp_speed)
            walking = false
            sprinting = true
            stealthing = false
        else:
            # Walk.
            current_speed = lerp(current_speed, WALK_SPEED - backward_speed, delta * lerp_speed)
            walking = true
            sprinting = false
            stealthing = false
    
    # Gravity.
    if not is_on_floor():
        velocity.y -= gravity * delta
    
    # Movement.
    if is_on_floor():
        # 使用head节点的basis来转换输入方向,确保跟随相机朝向移动
        direction = lerp(direction, (head.transform.basis * Vector3(input_dir.x, 0, input_dir.y)).normalized(), delta * lerp_speed)
    if direction:
        velocity.x = direction.x * current_speed
        velocity.z = direction.z * current_speed
    else:
        velocity.x = move_toward(velocity.x, 0.0, current_speed)
        velocity.z = move_toward(velocity.z, 0.0, current_speed)
    
    move_and_slide()

func _headbob(time) -> Vector3:
    var pos = Vector3.ZERO
    pos.y = sin(time * HEAD_BOB_FREQUENCY) * HEAD_BOB_AMPLITUDE
    pos.x = cos(time * HEAD_BOB_FREQUENCY / 2) * HEAD_BOB_AMPLITUDE
    return pos

验证说明

修复后,角色移动方向会跟随head节点(即相机)的朝向,按下W键会向相机前方移动,而非世界坐标系的Z轴正方向。同时全局状态变量能正确反映当前移动状态,便于后续功能扩展。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.06.20 13:50:56