fix(movement): camera auto-rotates to face WASD input direction
40250 parity: CPythonPlayer::NEW_MoveToDirection's m_isCmrRot branch continuously rolls the camera toward the held movement direction every frame while a direction key is down, independent of whether a new translation target was just set. Add _camera_auto_rotate() to replicate this (forward/back = no rotation, strafe = max rate, diagonal = half rate), called unconditionally after the frozen/locked/shop/emotion gate in _process(). Godot's yaw-increase handedness is opposite the reference's D3D Roll(), so the sign is flipped as a platform adaptation; verified by convergence invariants in keyboard_motion_timeline_test.gd rather than bit-for-bit angle matching. Closes movement.keyboard.motion/camera-auto-follow-rotation. Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
This commit is contained in:
co-authored by
Claude Sonnet 5
parent
c93894313a
commit
7df97e01f9
@@ -38,6 +38,7 @@ const SPEED_RUN := 4.8
|
||||
const KEYBOARD_MOVE_TARGET_M := 3.0 # 40250 NEW_MoveToDirection: Dst = Cur + 300 px
|
||||
const RUN_HOLD_KEY := KEY_SHIFT
|
||||
const ARRIVE_EPS := 0.05
|
||||
const CAMERA_AUTO_ROTATE_SPEED_DEG := 20.0 # 40250 CPythonPlayer::m_fCmrRotSpd 默认值
|
||||
const PICK_RADIUS := 1.4 # 点选命中半径(米)
|
||||
# __IsMovableGroundDistance:点地目标离脚下太近就不动(避免原地抖)。参考默认由
|
||||
# player.SetMovableGroundDistance 从 game.py 设置;此处用参考缺省值(~1 m)。§3.1
|
||||
@@ -503,6 +504,29 @@ func _wasd() -> Vector2:
|
||||
d.x = 1.0
|
||||
return d.normalized()
|
||||
|
||||
# 对齐 40250 CPythonPlayer::NEW_MoveToDirection 的 m_isCmrRot 分支:dir 是 _wasd() 的相机相对
|
||||
# 输入向量(0,-1=正前)。折算成角色相对相机的方位角 fDirRot(0=前 90=左 180=后 270=右,
|
||||
# 与 GetDegreeFromPosition 的取值一致),再按参考实现同样的公式折叠到 [-90,90] 的
|
||||
# fRotRat:正前/正后为 0(不转),正左/右为 ±90(转速最快),对角线为 ±45(减半)。
|
||||
# 参考实现用 CCamera::Roll(fRotDeg)(D3D 增量式 roll,fRotDeg=-speed*dt*fRotRat/90);
|
||||
# Godot 的 yaw 增大对应视线转向左,和 D3D 的 Roll 正方向手性相反,因此这里的增量对
|
||||
# fRotRat 取正号而不是参考的负号 —— 这是platform_adaptation,用“相机持续转向新朝向、
|
||||
# 从不越过目标”这一收敛不变式核验(keyboard_motion_timeline_test.gd),而不是逐位对照
|
||||
# D3D 数值。
|
||||
func _camera_auto_rotate(dir: Vector2, dt: float) -> void:
|
||||
if camera == null or not camera.has_method("heading") or dir == Vector2.ZERO:
|
||||
return
|
||||
var dir_rot_deg := fposmod(rad_to_deg(atan2(-dir.x, -dir.y)), 360.0)
|
||||
var sig_dir_rot := dir_rot_deg
|
||||
if sig_dir_rot > 180.0:
|
||||
sig_dir_rot -= 360.0
|
||||
var rot_rat := sig_dir_rot
|
||||
if rot_rat > 90.0:
|
||||
rot_rat = 180.0 - rot_rat
|
||||
elif rot_rat < -90.0:
|
||||
rot_rat = -180.0 - rot_rat
|
||||
camera.yaw += deg_to_rad(CAMERA_AUTO_ROTATE_SPEED_DEG * dt * rot_rat / 90.0)
|
||||
|
||||
func _blocked(x: float, z: float) -> bool:
|
||||
return world != null and world.has_method("is_blocked") and bool(world.call("is_blocked", x, z))
|
||||
|
||||
@@ -595,6 +619,11 @@ func _process(dt: float) -> void:
|
||||
if world and world.has_method("sample_height") and is_instance_valid(player):
|
||||
player.position.y = float(world.call("sample_height", player.position.x, player.position.z))
|
||||
return
|
||||
# 40250 CPythonPlayer::Update 每帧对仍按住的方向键重试 NEW_SetMultiDirKeyState ->
|
||||
# NEW_MoveToDirection,其 m_isCmrRot 分支与是否已建立平移目标无关,门检查(上面
|
||||
# 的 frozen/locked/shop/emotion)通过后即执行,因此放在移动逻辑之前、无条件按
|
||||
# 当前方向键调用。
|
||||
_camera_auto_rotate(_wasd(), dt)
|
||||
if force_walk:
|
||||
_run = false
|
||||
elif force_run:
|
||||
|
||||
Reference in New Issue
Block a user