namespace GameCli; /// /// Pure physics stepper. All constants are public so training/tuning can inspect them. /// Explicit Euler integration at fixed dt. /// 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; /// /// Advance the ship state by one dt given a discrete action. /// Gravity is always applied. Thrusters add to acceleration/torque. /// 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); } }