feat(camera): wire RetailChaseCamera through GameWindow
GameWindow now constructs both ChaseCamera + RetailChaseCamera at player-mode entry, updates both per frame (legacy with isOnGround, retail with BodyVelocity), and routes mouse/wheel/held-key input to whichever the CameraDiagnostics flag selects. Mouse-Y goes through RetailChaseCamera.FilterMouseDelta before AdjustPitch when retail is active; legacy path is unchanged. Held-key bindings (CameraZoomIn/Out, CameraRaise/Lower; default-unbound) integrate distance/pitch at CameraDiagnostics.CameraAdjustmentSpeed per second. Default behavior: ACDREAM_RETAIL_CHASE unset -> legacy camera as before. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
This commit is contained in:
parent
ff8f434711
commit
8f30e13317
1 changed files with 74 additions and 14 deletions
|
|
@ -627,6 +627,7 @@ public sealed class GameWindow : IDisposable
|
|||
// Phase B.2: player movement mode.
|
||||
private AcDream.App.Input.PlayerMovementController? _playerController;
|
||||
private AcDream.App.Rendering.ChaseCamera? _chaseCamera;
|
||||
private AcDream.App.Rendering.RetailChaseCamera? _retailChaseCamera;
|
||||
private bool _playerMode;
|
||||
private uint _playerServerGuid;
|
||||
private uint? _playerCurrentAnimCommand;
|
||||
|
|
@ -993,7 +994,17 @@ public sealed class GameWindow : IDisposable
|
|||
// here goes ONLY through ApplyDelta — no separate
|
||||
// YawOffset write. dy still pitches the camera only.
|
||||
_mouseLook.ApplyDelta(dx, sens);
|
||||
_chaseCamera.AdjustPitch(dy * 0.003f * sens);
|
||||
if (AcDream.Core.Rendering.CameraDiagnostics.UseRetailChaseCamera && _retailChaseCamera is not null)
|
||||
{
|
||||
float nowSec = (float)(Environment.TickCount64 / 1000.0);
|
||||
var (_, filteredDy) = _retailChaseCamera.FilterMouseDelta(
|
||||
rawX: 0f, rawY: dy, weight: 0.5f, nowSec: nowSec);
|
||||
_retailChaseCamera.AdjustPitch(filteredDy * 0.003f * sens);
|
||||
}
|
||||
else
|
||||
{
|
||||
_chaseCamera.AdjustPitch(dy * 0.003f * sens);
|
||||
}
|
||||
}
|
||||
else if (_rmbHeld)
|
||||
{
|
||||
|
|
@ -1004,8 +1015,19 @@ public sealed class GameWindow : IDisposable
|
|||
// ANYTHING when in player mode — character yaw is
|
||||
// dispatcher-only (A/D keys). K.2: MMB mouse-look path
|
||||
// above takes precedence when active.
|
||||
_chaseCamera.YawOffset -= dx * 0.004f * sens;
|
||||
_chaseCamera.AdjustPitch(dy * 0.003f * sens);
|
||||
if (AcDream.Core.Rendering.CameraDiagnostics.UseRetailChaseCamera && _retailChaseCamera is not null)
|
||||
{
|
||||
float nowSec = (float)(Environment.TickCount64 / 1000.0);
|
||||
var (filteredDx, filteredDy) = _retailChaseCamera.FilterMouseDelta(
|
||||
rawX: dx, rawY: dy, weight: 0.5f, nowSec: nowSec);
|
||||
_retailChaseCamera.YawOffset -= filteredDx * 0.004f * sens;
|
||||
_retailChaseCamera.AdjustPitch(filteredDy * 0.003f * sens);
|
||||
}
|
||||
else
|
||||
{
|
||||
_chaseCamera.YawOffset -= dx * 0.004f * sens;
|
||||
_chaseCamera.AdjustPitch(dy * 0.003f * sens);
|
||||
}
|
||||
}
|
||||
// K-fix1 (2026-04-26): no default-pitch path. With
|
||||
// neither MMB nor RMB held, mouse moves the cursor
|
||||
|
|
@ -4735,6 +4757,7 @@ public sealed class GameWindow : IDisposable
|
|||
|
||||
// 4. Recenter chase camera on the new position.
|
||||
_chaseCamera?.Update(snappedPos, _playerController.Yaw);
|
||||
_retailChaseCamera?.Update(snappedPos, _playerController.Yaw, System.Numerics.Vector3.Zero, dt: 1f / 60f);
|
||||
|
||||
// 5. Return to InWorld.
|
||||
_playerController.State = AcDream.App.Input.PlayerState.InWorld;
|
||||
|
|
@ -6324,6 +6347,21 @@ public sealed class GameWindow : IDisposable
|
|||
if (_inputDispatcher is null) return;
|
||||
_playerMouseDeltaX = 0f; // defensive: ensure no leakage even if some path writes it
|
||||
|
||||
// Retail-style held-key offset integration. Only active when
|
||||
// retail chase is selected; legacy camera ignores these.
|
||||
if (AcDream.Core.Rendering.CameraDiagnostics.UseRetailChaseCamera && _retailChaseCamera is not null)
|
||||
{
|
||||
float adj = AcDream.Core.Rendering.CameraDiagnostics.CameraAdjustmentSpeed * (float)dt;
|
||||
if (_inputDispatcher.IsActionHeld(AcDream.UI.Abstractions.Input.InputAction.CameraZoomIn))
|
||||
_retailChaseCamera.AdjustDistance(-adj);
|
||||
if (_inputDispatcher.IsActionHeld(AcDream.UI.Abstractions.Input.InputAction.CameraZoomOut))
|
||||
_retailChaseCamera.AdjustDistance(+adj);
|
||||
if (_inputDispatcher.IsActionHeld(AcDream.UI.Abstractions.Input.InputAction.CameraRaise))
|
||||
_retailChaseCamera.AdjustPitch(+adj * 0.02f);
|
||||
if (_inputDispatcher.IsActionHeld(AcDream.UI.Abstractions.Input.InputAction.CameraLower))
|
||||
_retailChaseCamera.AdjustPitch(-adj * 0.02f);
|
||||
}
|
||||
|
||||
// K-fix1 (2026-04-26): retail-faithful movement semantics.
|
||||
// * Default speed = RUN. Forward / backward / strafe all run
|
||||
// by default; holding Shift (MovementWalkMode) drops to
|
||||
|
|
@ -6380,16 +6418,22 @@ public sealed class GameWindow : IDisposable
|
|||
_worldState.RelocateEntity(pe, currentLb);
|
||||
}
|
||||
|
||||
// Update chase camera. K-fix12 (2026-04-26): pass isOnGround
|
||||
// so the camera pins its Z to last-grounded while the
|
||||
// player is airborne — without this the camera follows
|
||||
// player.Z 1:1 during a jump and the player's screen
|
||||
// position never changes. With the pin: player visibly
|
||||
// rises above the camera, matching retail "you can see
|
||||
// yourself jump" feedback.
|
||||
// Update chase camera(s). The CameraController exposes whichever
|
||||
// is currently selected via CameraDiagnostics.UseRetailChaseCamera;
|
||||
// both update every frame so toggling the flag swaps instantly
|
||||
// with the new camera already warm.
|
||||
//
|
||||
// Legacy ChaseCamera: pre-K-fix12 args (isOnGround pins Z during
|
||||
// jumps as a workaround for the visual feel retail gets from
|
||||
// low-stiffness damping).
|
||||
_chaseCamera.Update(result.RenderPosition, _playerController.Yaw,
|
||||
isOnGround: result.IsOnGround,
|
||||
dt: (float)dt);
|
||||
// RetailChaseCamera: takes world velocity for slope-aligned heading;
|
||||
// jump-feedback falls out of damping naturally, no isOnGround needed.
|
||||
_retailChaseCamera!.Update(result.RenderPosition, _playerController.Yaw,
|
||||
playerVelocity: _playerController.BodyVelocity,
|
||||
dt: (float)dt);
|
||||
|
||||
// Send outbound movement messages to the live server.
|
||||
if (_liveSession is not null)
|
||||
|
|
@ -8963,6 +9007,7 @@ public sealed class GameWindow : IDisposable
|
|||
_cameraController?.ExitChaseMode();
|
||||
_playerController = null;
|
||||
_chaseCamera = null;
|
||||
_retailChaseCamera = null;
|
||||
_playerCurrentAnimCommand = null;
|
||||
}
|
||||
else
|
||||
|
|
@ -9801,6 +9846,7 @@ public sealed class GameWindow : IDisposable
|
|||
_cameraController?.ExitChaseMode();
|
||||
_playerController = null;
|
||||
_chaseCamera = null;
|
||||
_retailChaseCamera = null;
|
||||
_playerCurrentAnimCommand = null;
|
||||
_playerMouseDeltaX = 0f;
|
||||
}
|
||||
|
|
@ -9856,7 +9902,14 @@ public sealed class GameWindow : IDisposable
|
|||
&& _playerMode
|
||||
&& _chaseCamera is not null)
|
||||
{
|
||||
_cameraController.EnterChaseMode(_chaseCamera, new RetailChaseCamera { Aspect = _chaseCamera.Aspect });
|
||||
if (_retailChaseCamera is null)
|
||||
{
|
||||
_retailChaseCamera = new AcDream.App.Rendering.RetailChaseCamera
|
||||
{
|
||||
Aspect = _chaseCamera.Aspect,
|
||||
};
|
||||
}
|
||||
_cameraController.EnterChaseMode(_chaseCamera, _retailChaseCamera);
|
||||
return;
|
||||
}
|
||||
_cameraController.ToggleFly();
|
||||
|
|
@ -9957,12 +10010,16 @@ public sealed class GameWindow : IDisposable
|
|||
{
|
||||
Aspect = _window!.Size.X / (float)_window.Size.Y,
|
||||
};
|
||||
_retailChaseCamera = new AcDream.App.Rendering.RetailChaseCamera
|
||||
{
|
||||
Aspect = _window!.Size.X / (float)_window.Size.Y,
|
||||
};
|
||||
// K.1b: _playerMouseDeltaX is no longer consumed by
|
||||
// MovementInput, but we still reset it here so any stale
|
||||
// accumulated value from a previous session doesn't leak
|
||||
// into a future code path that re-enables mouse-yaw.
|
||||
_playerMouseDeltaX = 0f;
|
||||
_cameraController?.EnterChaseMode(_chaseCamera, new RetailChaseCamera { Aspect = _chaseCamera.Aspect });
|
||||
_cameraController?.EnterChaseMode(_chaseCamera, _retailChaseCamera);
|
||||
// K-fix1 (2026-04-26): latch the "we have entered chase at least
|
||||
// once" flag so the live-mode pre-login render gate stops
|
||||
// suppressing the scene. From here on, the orbit camera (if the
|
||||
|
|
@ -10106,10 +10163,13 @@ public sealed class GameWindow : IDisposable
|
|||
if (_cameraController is null) return;
|
||||
float dir = (action == AcDream.UI.Abstractions.Input.InputAction.ScrollUp) ? 1f : -1f;
|
||||
|
||||
if (_playerMode && _cameraController.IsChaseMode && _chaseCamera is not null)
|
||||
if (_playerMode && _cameraController.IsChaseMode)
|
||||
{
|
||||
// Chase mode: zoom (closer on ScrollUp).
|
||||
_chaseCamera.AdjustDistance(-dir * 0.8f);
|
||||
if (AcDream.Core.Rendering.CameraDiagnostics.UseRetailChaseCamera && _retailChaseCamera is not null)
|
||||
_retailChaseCamera.AdjustDistance(-dir * 0.8f);
|
||||
else if (_chaseCamera is not null)
|
||||
_chaseCamera.AdjustDistance(-dir * 0.8f);
|
||||
}
|
||||
else if (_cameraController.IsFlyMode)
|
||||
{
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue