using UnityEngine; public class GimbalSystem : MonoBehaviour { [Header("References")] public MainEngine mainEngine; public RocketPhysics rocketPhysics; public Rigidbody rb; [Header("Gimbal Settings")] [Range(0f, 20f)] public float maxGimbalAngle = 10f; // degrees - maximum deflection angle public float gimbalResponseRate = 30f; // degrees per second - how fast gimbal can move [Header("Input")] public float pitchInput = 0f; // -1 to 1 (negative = pitch down, positive = pitch up) public float yawInput = 0f; // -1 to 1 (negative = yaw left, positive = yaw right) public float rollInput = 0f; // -1 to 1 (optional roll control) [Header("Current Gimbal State")] public float currentPitchAngle = 0f; // degrees public float currentYawAngle = 0f; // degrees public Vector3 appliedTorque = Vector3.zero; // N⋅m [Header("Gimbal Physics")] public float gimbalLeverArm = 5f; // meters - distance from CoM to thrust vector application point [Header("Debug")] public bool showDebugInfo = false; void FixedUpdate() { UpdateGimbalAngles(); CalculateGimbalTorque(); } /// /// Updates the current gimbal angles based on input, respecting limits and response rates /// private void UpdateGimbalAngles() { // Calculate target angles from input float targetPitchAngle = pitchInput * maxGimbalAngle; float targetYawAngle = yawInput * maxGimbalAngle; // Smoothly move toward target angles at the response rate float maxDelta = gimbalResponseRate * Time.fixedDeltaTime; currentPitchAngle = Mathf.MoveTowards(currentPitchAngle, targetPitchAngle, maxDelta); currentYawAngle = Mathf.MoveTowards(currentYawAngle, targetYawAngle, maxDelta); // Enforce gimbal limits (safety clamp) currentPitchAngle = Mathf.Clamp(currentPitchAngle, -maxGimbalAngle, maxGimbalAngle); currentYawAngle = Mathf.Clamp(currentYawAngle, -maxGimbalAngle, maxGimbalAngle); } /// /// Calculates torque based on gimbal angles and engine thrust (does not apply it) /// RocketPhysics will read and apply this torque /// private void CalculateGimbalTorque() { // Only calculate torque if engine is producing thrust if (mainEngine == null || !mainEngine.engineIgnited || mainEngine.thrust <= 0f) { appliedTorque = Vector3.zero; return; } // Convert gimbal angles to radians for calculation float pitchRad = currentPitchAngle * Mathf.Deg2Rad; float yawRad = currentYawAngle * Mathf.Deg2Rad; // Calculate perpendicular force components from gimbal deflection // For small angles: F_perp ≈ F_thrust × sin(angle) ≈ F_thrust × angle_rad // Using full sin for accuracy at larger gimbal angles float pitchForce = mainEngine.thrust * 1000f * Mathf.Sin(pitchRad); // Convert kN to N float yawForce = mainEngine.thrust * 1000f * Mathf.Sin(yawRad); // Calculate torque: τ = F × r (cross product, but simplified for perpendicular forces) // Pitch gimbal creates torque around the local right axis (X) // Yaw gimbal creates torque around the local forward axis (Z) // Torque directions: positive pitch input → nose up, positive yaw input → nose right Vector3 localTorque = new Vector3( pitchForce * gimbalLeverArm, // Pitch torque around X axis 0f, // No direct Y torque from gimbal -yawForce * gimbalLeverArm // Yaw torque around Z axis (negative for correct direction) ); // Convert local torque to world space appliedTorque = rb.rotation * localTorque; if (showDebugInfo) { Debug.Log($"[GimbalSystem] Pitch: {currentPitchAngle:F2}°, Yaw: {currentYawAngle:F2}°, Torque: {appliedTorque.magnitude:F2} N⋅m"); } } /// /// Sets the pitch input (-1 to 1) /// public void SetPitchInput(float value) { pitchInput = Mathf.Clamp(value, -1f, 1f); } /// /// Sets the yaw input (-1 to 1) /// public void SetYawInput(float value) { yawInput = Mathf.Clamp(value, -1f, 1f); } /// /// Sets the roll input (-1 to 1) - for future use with RCS or other control surfaces /// public void SetRollInput(float value) { rollInput = Mathf.Clamp(value, -1f, 1f); } /// /// Resets gimbal to neutral position /// public void ResetGimbal() { pitchInput = 0f; yawInput = 0f; rollInput = 0f; } }