feat(render): Phase A8.F — RetailChaseCamera consumes the camera-collision probe
Add ICameraCollisionProbe? CollisionProbe { get; init; } to RetailChaseCamera.
Extend Update() with optional cellId/selfEntityId params (default 0) so all
existing callers compile unchanged. After the exponential-damping block (step 5)
and before publishing Position/View (step 6), sweep _dampedEye through the
probe when CameraDiagnostics.CollideCamera is true and a probe is wired in
(step 5b). The fade computation in step 7 then naturally uses the collided eye.
Null probe and cellId=0 both short-circuit cleanly. Three new xUnit tests
cover: probe-wired+flag-on publishes collided eye, flag-off skips probe,
null probe doesn't throw. All 30 RetailChaseCameraTests pass.
Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
This commit is contained in:
parent
fcea05f808
commit
319277a27b
2 changed files with 81 additions and 1 deletions
|
|
@ -445,4 +445,66 @@ public class RetailChaseCameraTests
|
|||
cam.AdjustPitch(+10f);
|
||||
Assert.Equal(RetailChaseCamera.PitchMax, cam.Pitch);
|
||||
}
|
||||
|
||||
// ── Camera collision (A8.F) ───────────────────────────────────────
|
||||
|
||||
private sealed class FakeProbe : ICameraCollisionProbe
|
||||
{
|
||||
public int Calls;
|
||||
public Vector3 ReturnEye;
|
||||
public Vector3 SweepEye(Vector3 pivot, Vector3 desiredEye, uint cellId, uint selfEntityId)
|
||||
{
|
||||
Calls++;
|
||||
return ReturnEye;
|
||||
}
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void Update_WithProbeAndFlagOn_PublishesCollidedEye()
|
||||
{
|
||||
CameraDiagnostics.CollideCamera = true;
|
||||
var collided = new Vector3(1f, 2f, 3f);
|
||||
var probe = new FakeProbe { ReturnEye = collided };
|
||||
var cam = new RetailChaseCamera { CollisionProbe = probe };
|
||||
|
||||
cam.Update(
|
||||
playerPosition: Vector3.Zero, playerYaw: 0f, playerVelocity: Vector3.Zero,
|
||||
isOnGround: true, contactPlaneNormal: Vector3.UnitZ, dt: 1f / 60f,
|
||||
cellId: 0x100, selfEntityId: 0x5);
|
||||
|
||||
Assert.True(probe.Calls >= 1);
|
||||
Assert.Equal(collided, cam.Position);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void Update_FlagOff_DoesNotConsultProbe()
|
||||
{
|
||||
CameraDiagnostics.CollideCamera = false;
|
||||
var probe = new FakeProbe { ReturnEye = new Vector3(99f, 99f, 99f) };
|
||||
var cam = new RetailChaseCamera { CollisionProbe = probe };
|
||||
|
||||
cam.Update(
|
||||
playerPosition: Vector3.Zero, playerYaw: 0f, playerVelocity: Vector3.Zero,
|
||||
isOnGround: true, contactPlaneNormal: Vector3.UnitZ, dt: 1f / 60f,
|
||||
cellId: 0x100, selfEntityId: 0x5);
|
||||
|
||||
Assert.Equal(0, probe.Calls);
|
||||
Assert.NotEqual(new Vector3(99f, 99f, 99f), cam.Position);
|
||||
|
||||
CameraDiagnostics.CollideCamera = true; // reset
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void Update_NullProbe_DoesNotThrow()
|
||||
{
|
||||
CameraDiagnostics.CollideCamera = true;
|
||||
var cam = new RetailChaseCamera { CollisionProbe = null };
|
||||
|
||||
cam.Update(
|
||||
playerPosition: Vector3.Zero, playerYaw: 0f, playerVelocity: Vector3.Zero,
|
||||
isOnGround: true, contactPlaneNormal: Vector3.UnitZ, dt: 1f / 60f,
|
||||
cellId: 0x100, selfEntityId: 0x5);
|
||||
|
||||
Assert.NotEqual(default, cam.View);
|
||||
}
|
||||
}
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue