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
@@ -3,6 +3,7 @@
|
||||
extends SceneTree
|
||||
|
||||
const PlayerController = preload("res://player_controller.gd")
|
||||
const GameCamera = preload("res://game_camera.gd")
|
||||
|
||||
var _fail := 0
|
||||
|
||||
@@ -129,9 +130,75 @@ func _init() -> void:
|
||||
"held direction resumes after emotion ends")
|
||||
pc._input(_key(KEY_W, false))
|
||||
|
||||
pc._input(_key(KEY_W, false))
|
||||
|
||||
# 40250 CPythonPlayer::NEW_MoveToDirection m_isCmrRot: holding a pure
|
||||
# forward/backward direction must not roll the camera (fRotRat == 0 at the
|
||||
# axis), while pure left/right rolls it fastest, and diagonals roll at
|
||||
# half rate. See player_controller.gd::_camera_auto_rotate.
|
||||
pc.stop()
|
||||
var cam := GameCamera.new()
|
||||
pc.camera = cam
|
||||
cam.yaw = 0.0
|
||||
pc._unhandled_input(_key(KEY_W, true))
|
||||
pc._process(0.1)
|
||||
_ck(is_zero_approx(cam.yaw), "holding pure forward does not roll the camera")
|
||||
pc._input(_key(KEY_W, false))
|
||||
|
||||
cam.yaw = 0.0
|
||||
pc._unhandled_input(_key(KEY_S, true))
|
||||
pc._process(0.1)
|
||||
_ck(is_zero_approx(cam.yaw), "holding pure backward does not roll the camera")
|
||||
pc._input(_key(KEY_S, false))
|
||||
|
||||
cam.yaw = 0.0
|
||||
pc._unhandled_input(_key(KEY_A, true))
|
||||
var yaw_after_one := 0.0
|
||||
for _i in range(5):
|
||||
pc._process(0.1)
|
||||
if _i == 0:
|
||||
yaw_after_one = cam.yaw
|
||||
_ck(yaw_after_one > 0.001, "holding left starts rolling the camera immediately")
|
||||
_ck(is_equal_approx(yaw_after_one * 5.0, cam.yaw), "camera roll rate is constant while held")
|
||||
var left_yaw := cam.yaw
|
||||
pc._input(_key(KEY_A, false))
|
||||
|
||||
cam.yaw = 0.0
|
||||
pc._unhandled_input(_key(KEY_D, true))
|
||||
pc._process(0.1)
|
||||
pc._process(0.1)
|
||||
pc._process(0.1)
|
||||
pc._process(0.1)
|
||||
pc._process(0.1)
|
||||
_ck(is_equal_approx(cam.yaw, -left_yaw),
|
||||
"holding right rolls the camera the same rate opposite left")
|
||||
pc._input(_key(KEY_D, false))
|
||||
|
||||
cam.yaw = 0.0
|
||||
pc._unhandled_input(_key(KEY_W, true))
|
||||
pc._unhandled_input(_key(KEY_A, true))
|
||||
for _i in range(5):
|
||||
pc._process(0.1)
|
||||
_ck(is_equal_approx(cam.yaw, left_yaw * 0.5),
|
||||
"a forward-left diagonal rolls the camera at half the pure-left rate")
|
||||
pc._input(_key(KEY_A, false))
|
||||
pc._input(_key(KEY_W, false))
|
||||
|
||||
# The camera-roll side effect is gated exactly like translation: no roll
|
||||
# while the actor cannot move.
|
||||
cam.yaw = 0.0
|
||||
pc.stop()
|
||||
pc.locked = true
|
||||
pc._unhandled_input(_key(KEY_A, true))
|
||||
pc._process(0.2)
|
||||
_ck(is_zero_approx(cam.yaw), "locked movement gate also suppresses the camera roll")
|
||||
pc.locked = false
|
||||
pc._input(_key(KEY_A, false))
|
||||
pc.stop()
|
||||
|
||||
player.free()
|
||||
pc.free()
|
||||
cam.free()
|
||||
if _fail == 0:
|
||||
print("PASS: keyboard_motion_timeline_test")
|
||||
quit(0)
|
||||
|
||||
Reference in New Issue
Block a user