79 lines
2.8 KiB
C#
79 lines
2.8 KiB
C#
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);
|
|
}
|
|
}
|