fix(camera): #38 render-interpolate player motion
Keep local physics authoritative at the retail 30 Hz MinQuantum, but expose a render-only position that lerps between completed physics ticks for the player mesh and chase-camera target. Network outbound continues to use the discrete physics position. Also make the visually confirmed #47 humanoid close-detail DIDDegrade path default-on, with ACDREAM_RETAIL_CLOSE_DEGRADES=0 left as a diagnostic opt-out. Verification: dotnet build AcDream.slnx -c Debug; focused #38 interpolation tests passed; visual confirmed smooth 2026-05-06. Full dotnet test AcDream.slnx -c Debug --no-build still has the known 8 AcDream.Core.Tests baseline failures. Co-authored-by: Codex <codex@openai.com>
This commit is contained in:
parent
e3d8a44c48
commit
71b1622293
5 changed files with 218 additions and 16 deletions
|
|
@ -49,6 +49,81 @@ public class PlayerMovementControllerTests
|
|||
Assert.True(result.Position.X > 96f + 2f, $"X={result.Position.X} should have moved forward");
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void Update_SubQuantumFrame_InterpolatesRenderPositionWithoutAdvancingPhysicsPosition()
|
||||
{
|
||||
var engine = MakeFlatEngine();
|
||||
var controller = new PlayerMovementController(engine);
|
||||
var start = new Vector3(96f, 96f, 50f);
|
||||
controller.SetPosition(start, 0x0001);
|
||||
controller.Yaw = 0f;
|
||||
|
||||
var firstTick = controller.Update(PhysicsBody.MinQuantum, new MovementInput(Forward: true));
|
||||
Assert.True(firstTick.Position.X > start.X, "Physics tick should advance the authoritative body position");
|
||||
Assert.Equal(start.X, firstTick.RenderPosition.X, precision: 4);
|
||||
|
||||
var halfFrame = controller.Update(PhysicsBody.MinQuantum * 0.5f, new MovementInput(Forward: true));
|
||||
|
||||
Assert.Equal(firstTick.Position.X, halfFrame.Position.X, precision: 4);
|
||||
Assert.True(halfFrame.RenderPosition.X > start.X, "Render position should move between physics ticks");
|
||||
Assert.True(halfFrame.RenderPosition.X < firstTick.Position.X,
|
||||
$"Render X={halfFrame.RenderPosition.X} should stay between {start.X} and {firstTick.Position.X}");
|
||||
|
||||
float expectedMidpoint = start.X + ((firstTick.Position.X - start.X) * 0.5f);
|
||||
Assert.Equal(expectedMidpoint, halfFrame.RenderPosition.X, precision: 3);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void SetPosition_ResnapsRenderInterpolationEndpoints()
|
||||
{
|
||||
var engine = MakeFlatEngine();
|
||||
var controller = new PlayerMovementController(engine);
|
||||
controller.SetPosition(new Vector3(96f, 96f, 50f), 0x0001);
|
||||
controller.Yaw = 0f;
|
||||
|
||||
controller.Update(PhysicsBody.MinQuantum, new MovementInput(Forward: true));
|
||||
controller.Update(PhysicsBody.MinQuantum * 0.5f, new MovementInput(Forward: true));
|
||||
|
||||
var snapped = new Vector3(120f, 80f, 50f);
|
||||
controller.SetPosition(snapped, 0x0001);
|
||||
var result = controller.Update(PhysicsBody.MinQuantum * 0.5f, new MovementInput());
|
||||
|
||||
Assert.Equal(snapped, result.Position);
|
||||
Assert.Equal(snapped, result.RenderPosition);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void Update_HugeQuantumDiscard_ResnapsRenderInterpolationEndpoints()
|
||||
{
|
||||
var engine = MakeFlatEngine();
|
||||
var controller = new PlayerMovementController(engine);
|
||||
controller.SetPosition(new Vector3(96f, 96f, 50f), 0x0001);
|
||||
controller.Yaw = 0f;
|
||||
|
||||
var moved = controller.Update(PhysicsBody.MinQuantum, new MovementInput(Forward: true));
|
||||
var stale = controller.Update(PhysicsBody.HugeQuantum + 0.1f, new MovementInput(Forward: true));
|
||||
|
||||
Assert.Equal(moved.Position.X, stale.Position.X, precision: 4);
|
||||
Assert.Equal(stale.Position, stale.RenderPosition);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void Update_LeftoverAboveMinQuantum_ClampsRenderAlphaToCurrentPhysicsPosition()
|
||||
{
|
||||
var engine = MakeFlatEngine();
|
||||
var controller = new PlayerMovementController(engine);
|
||||
controller.SetPosition(new Vector3(96f, 96f, 50f), 0x0001);
|
||||
controller.Yaw = 0f;
|
||||
|
||||
var result = controller.Update(
|
||||
PhysicsBody.MaxQuantum + PhysicsBody.MinQuantum,
|
||||
new MovementInput(Forward: true));
|
||||
|
||||
Assert.Equal(result.Position.X, result.RenderPosition.X, precision: 4);
|
||||
Assert.Equal(result.Position.Y, result.RenderPosition.Y, precision: 4);
|
||||
Assert.Equal(result.Position.Z, result.RenderPosition.Z, precision: 4);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void Update_RunForward_MoveFasterThanWalk()
|
||||
{
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue