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
问题根源
- 方向计算的Basis错误:代码中移动方向使用了角色本体(
CharacterBody3D)的transform.basis,但相机旋转仅作用在head节点上,角色本体未跟随相机旋转,导致transform.basis始终对应世界坐标系朝向,而非玩家实际看向的方向。 - 状态变量赋值无效:条件判断中创建了
_walking等局部变量,未给全局状态变量赋值,导致状态判断逻辑失效。
修复步骤
- 替换方向计算的Basis:将Movement段中的
transform.basis改为head.transform.basis,使用相机所在的head节点朝向转换输入向量。 - 修正状态变量赋值:移除局部变量声明,直接给全局状态变量赋值。
修复后的完整代码
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
相关产品推荐
相关产品推荐

