namespace Car_simulation { public class Drivetrain { // Connected components public Engine Engine { get; private set; } public WheelSystem WheelSystem { get; private set; } private int currentGear = 1; public float[] GearRatios { get; set; } = { 3.8f, // 1st 2.5f, // 2nd 1.8f, // 3rd 1.3f, // 4th 1.0f, // 5th 0.8f, // 6th 0.65f // 7th }; public float FinalDriveRatio { get; set; } = 4.0f; public float Efficiency { get; set; } = 0.95f; public float ClutchEngagement { get; set; } = 0f; // 0 = disengaged, 1 = fully engaged // Clutch properties public float MaxClutchTorque { get; set; } = 4500f; public float ClutchStiffness { get; set; } = 50f; // Softer spring // State public float ClutchTorque { get; private set; } public float TransmittedPower { get; private set; } public float ClutchSlipRatio { get; private set; } public Drivetrain(Engine engine, WheelSystem wheelSystem) { Engine = engine; WheelSystem = wheelSystem; } public void Update(float deltaTime) { if (ClutchEngagement <= 0.01f || TotalRatio == 0) { ClutchTorque = 0; TransmittedPower = 0; ClutchSlipRatio = 1f; return; } // Calculate expected vs actual wheel speeds float expectedWheelOmega = Engine.AngularVelocity / TotalRatio; float actualWheelOmega = WheelSystem.AngularVelocity; float omegaDifference = actualWheelOmega - expectedWheelOmega; // Calculate max torque clutch can transmit float maxClutchTorque = MaxClutchTorque * ClutchEngagement; // Simple spring model: torque tries to sync speeds float desiredTorque = -omegaDifference * ClutchStiffness; // Clamp to clutch capacity desiredTorque = Math.Clamp(desiredTorque, -maxClutchTorque, maxClutchTorque); // Also limit by engine capability when accelerating if (desiredTorque > 0) { float engineTorque = Engine.GetTorqueOutput() * Engine.GetActualThrottle(); float maxEngineTorqueAtWheels = engineTorque * TotalRatio * Efficiency; desiredTorque = Math.Min(desiredTorque, maxEngineTorqueAtWheels); } ClutchTorque = desiredTorque; // Calculate energy transfer based on torque float energyTransferred = 0f; if (omegaDifference > 0.01f) // Wheels → Engine (engine braking) { // Power = torque × angular velocity (at slower side - engine) float power = ClutchTorque * (Engine.AngularVelocity); energyTransferred = power * deltaTime; // Wheels lose energy, engine gains (minus efficiency losses) float wheelEnergyLoss = Math.Abs(energyTransferred); float engineEnergyGain = wheelEnergyLoss * Efficiency; WheelSystem.TotalEnergy -= wheelEnergyLoss; Engine.FlywheelEnergy += engineEnergyGain; } else if (omegaDifference < -0.01f) // Engine → Wheels (acceleration) { // Power = torque × angular velocity (at faster side - engine) float power = -ClutchTorque * Engine.AngularVelocity; // Negative torque, positive power energyTransferred = power * deltaTime; // Engine loses energy, wheels gain float engineEnergyLoss = Math.Abs(energyTransferred); float wheelEnergyGain = engineEnergyLoss * Efficiency; Engine.FlywheelEnergy -= engineEnergyLoss; WheelSystem.TotalEnergy += wheelEnergyGain; } else { // Nearly synchronized energyTransferred = 0; } // Calculate transmitted power TransmittedPower = energyTransferred / deltaTime; // Calculate clutch slip CORRECTLY: // Slip = 0 when torque < max torque (clutch can handle it) // Slip = 1 when torque = max torque (clutch is slipping) if (maxClutchTorque > 0) { float torqueRatio = Math.Abs(ClutchTorque) / maxClutchTorque; // If we're transmitting max torque, clutch is slipping // If we're transmitting less, clutch is gripping ClutchSlipRatio = torqueRatio; // 0 = no slip, 1 = full slip } else { ClutchSlipRatio = 1f; } } // Other methods... public float GearRatio => GetCurrentGearRatio(); public float TotalRatio => GearRatio * FinalDriveRatio; private float GetCurrentGearRatio() { if (currentGear == 0) return 0f; if (currentGear == -1) return -3.5f; if (currentGear > 0 && currentGear <= GearRatios.Length) return GearRatios[currentGear - 1]; return 0f; } public float GetSpeedDifferenceRPM() { float expectedWheelOmega = Engine.AngularVelocity / TotalRatio; float actualWheelOmega = WheelSystem.AngularVelocity; return (actualWheelOmega - expectedWheelOmega) * PhysicsUtil.RAD_PER_SEC_TO_RPM; } public string GetCurrentGearName() { return currentGear switch { -1 => "R", 0 => "N", _ => currentGear.ToString() }; } public float GetClutchSlipPercent() { return ClutchSlipRatio * 100f; } public void GearUp() { if (currentGear < GearRatios.Length) currentGear++; } public void GearDown() { if (currentGear > 1) currentGear--; } } }