controls: on-foot always-on mouse-look + camera-relative WASD + E/Q claim

- CameraDirector: AvatarFollow now uses always-on mouse-look (no RMB) with a
  locked/hidden cursor; Esc toggles the cursor free; RobotFollow keeps RMB-look.
- PlayerAvatar: movement is camera-relative (W = camera forward, character turns
  to face where it runs) instead of world/cardinal.
- PlayerAvatar: E claims a tread on foot, Q leaves while seated (locked cursor
  makes the OnGUI buttons unclickable).
- Verified in play: cursor locks in on-foot mode, camera-relative basis correct,
  no runtime errors.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
This commit is contained in:
megaproxy 2026-07-12 21:02:22 +01:00
parent 3082644dbd
commit 30d0b27813
2 changed files with 33 additions and 3 deletions

View file

@ -61,6 +61,7 @@ namespace Jankenbots.Prototype
float _pitch = 18f; float _pitch = 18f;
float _dist = 8f; float _dist = 8f;
float _cineAngle; float _cineAngle;
bool _cursorFree; // Esc toggles the locked cursor free (for GUI / to escape play)
PlayerAvatar _local; PlayerAvatar _local;
@ -115,13 +116,21 @@ namespace Jankenbots.Prototype
if (want != _mode) { _mode = want; Apply(_mode); } if (want != _mode) { _mode = want; Apply(_mode); }
} }
// Look / zoom input — only for the orbitable follow cams. // Cursor + look. On-foot (AvatarFollow) uses ALWAYS-ON mouse-look with a
// locked cursor (no RMB needed); other modes keep the cursor free (RobotFollow
// orbits while RMB is held). Esc toggles the cursor free to reach GUI / escape.
if (kb != null && kb.escapeKey.wasPressedThisFrame) _cursorFree = !_cursorFree;
bool avatarLook = _mode == Mode.AvatarFollow && !_cursorFree;
Cursor.lockState = avatarLook ? CursorLockMode.Locked : CursorLockMode.None;
Cursor.visible = !avatarLook;
if (_mode == Mode.AvatarFollow || _mode == Mode.RobotFollow) if (_mode == Mode.AvatarFollow || _mode == Mode.RobotFollow)
{ {
var m = Mouse.current; var m = Mouse.current;
if (m != null) if (m != null)
{ {
if (m.rightButton.isPressed) bool look = _mode == Mode.AvatarFollow ? avatarLook : m.rightButton.isPressed;
if (look)
{ {
Vector2 d = m.delta.ReadValue(); Vector2 d = m.delta.ReadValue();
_yaw += d.x * lookSensitivity; _yaw += d.x * lookSensitivity;

View file

@ -104,6 +104,15 @@ namespace Jankenbots.Prototype
else ExitSeated(); else ExitSeated();
} }
// Claim / leave a tread by KEY. On-foot mouse-look locks the cursor, so
// the OnGUI Claim/Leave buttons aren't clickable — E claims, Q leaves.
var kb = Keyboard.current;
if (_seats != null && kb != null)
{
if (!_seated && kb.eKey.wasPressedThisFrame) _seats.ClaimSeatRpc();
else if (_seated && kb.qKey.wasPressedThisFrame) _seats.ReleaseSeatRpc();
}
// On foot → drive the avatar. Seated → do nothing (control is on the tread). // On foot → drive the avatar. Seated → do nothing (control is on the tread).
if (!_seated) OwnerMove(); if (!_seated) OwnerMove();
} }
@ -169,7 +178,19 @@ namespace Jankenbots.Prototype
if (kb.sKey.isPressed || kb.downArrowKey.isPressed) v -= 1f; if (kb.sKey.isPressed || kb.downArrowKey.isPressed) v -= 1f;
} }
Vector3 input = new Vector3(h, 0f, v); // CAMERA-RELATIVE movement: W goes the way the camera (mouse) is looking,
// not world +Z. Basis is the active game camera flattened onto the ground.
Vector3 camF = Vector3.forward, camR = Vector3.right;
var cam = Camera.main;
if (cam != null)
{
camF = Vector3.ProjectOnPlane(cam.transform.forward, Vector3.up);
if (camF.sqrMagnitude < 1e-4f) camF = Vector3.ProjectOnPlane(cam.transform.up, Vector3.up); // looking straight down
camF.Normalize();
camR = Vector3.ProjectOnPlane(cam.transform.right, Vector3.up).normalized;
}
Vector3 input = camR * h + camF * v;
if (input.sqrMagnitude > 1f) input.Normalize(); if (input.sqrMagnitude > 1f) input.Normalize();
Vector3 desired = input * moveSpeed; Vector3 desired = input * moveSpeed;