feat(gamecli): implement pure Physics.Step with unit tests
This commit is contained in:
78
GameCli/Physics.cs
Normal file
78
GameCli/Physics.cs
Normal file
@@ -0,0 +1,78 @@
|
||||
namespace GameCli;
|
||||
|
||||
/// <summary>
|
||||
/// Pure physics stepper. All constants are public so training/tuning can inspect them.
|
||||
/// Explicit Euler integration at fixed dt.
|
||||
/// </summary>
|
||||
public static class Physics
|
||||
{
|
||||
public const float Dt = 0.02f; // 50 Hz
|
||||
public const float Gravity = 0.5f; // world units / s^2, +Y (downward)
|
||||
public const float MainThrust = 1.2f; // world units / s^2 along body-up
|
||||
public const float SideTorque = 4.0f; // rad / s^2
|
||||
public const float SideLateralImpulse = 0.15f; // world units / s^2 along body-x
|
||||
public const float LinearDrag = 0.10f; // per-second
|
||||
public const float AngularDrag = 0.50f; // per-second
|
||||
|
||||
public const int ActionNoop = 0;
|
||||
public const int ActionLeft = 1;
|
||||
public const int ActionMain = 2;
|
||||
public const int ActionRight = 3;
|
||||
|
||||
/// <summary>
|
||||
/// Advance the ship state by one dt given a discrete action.
|
||||
/// Gravity is always applied. Thrusters add to acceleration/torque.
|
||||
/// </summary>
|
||||
public static ShipState Step(ShipState s, int action)
|
||||
{
|
||||
// Start with gravity.
|
||||
float ax = 0f;
|
||||
float ay = Gravity;
|
||||
float torque = 0f;
|
||||
|
||||
// Body-up direction in world coords, given angle:
|
||||
// angle=0 → (0, -1) "up" on screen (Y is down)
|
||||
// angle=+π/2 → (+1, 0) right
|
||||
// angle=+π → (0, +1) down
|
||||
float sinA = MathF.Sin(s.Angle);
|
||||
float cosA = MathF.Cos(s.Angle);
|
||||
float bodyUpX = sinA;
|
||||
float bodyUpY = -cosA;
|
||||
// Body-right direction (perpendicular, rotated +90°):
|
||||
float bodyRightX = cosA;
|
||||
float bodyRightY = sinA;
|
||||
|
||||
switch (action)
|
||||
{
|
||||
case ActionMain:
|
||||
ax += bodyUpX * MainThrust;
|
||||
ay += bodyUpY * MainThrust;
|
||||
break;
|
||||
case ActionLeft:
|
||||
torque -= SideTorque;
|
||||
ax += bodyRightX * SideLateralImpulse;
|
||||
ay += bodyRightY * SideLateralImpulse;
|
||||
break;
|
||||
case ActionRight:
|
||||
torque += SideTorque;
|
||||
ax -= bodyRightX * SideLateralImpulse;
|
||||
ay -= bodyRightY * SideLateralImpulse;
|
||||
break;
|
||||
case ActionNoop:
|
||||
default:
|
||||
break;
|
||||
}
|
||||
|
||||
// Integrate velocity, apply linear drag, integrate position.
|
||||
float vx = (s.VX + ax * Dt) * (1f - LinearDrag * Dt);
|
||||
float vy = (s.VY + ay * Dt) * (1f - LinearDrag * Dt);
|
||||
float x = s.X + vx * Dt;
|
||||
float y = s.Y + vy * Dt;
|
||||
|
||||
// Angular: same pattern.
|
||||
float w = (s.AngularVelocity + torque * Dt) * (1f - AngularDrag * Dt);
|
||||
float angle = s.Angle + w * Dt;
|
||||
|
||||
return new ShipState(x, y, vx, vy, angle, w);
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user