Files
Car-Simulation/Car simulation/Engine.cs
2025-12-18 01:04:21 +01:00

143 lines
4.5 KiB
C#

namespace Car_simulation
{
public class Engine
{
// Energy state
public float FlywheelEnergy { get; set; } // Joules
// Values
public float RPM => GetRPM();
public float AngularVelocity => GetOmega();
public float CurrentPower { get; private set; }
// Physical properties
public float MomentOfInertia { get; set; } = 0.25f; // kg·m²
public float IdleRPM { get; set; } = 800f;
public float StallSpeed { get; set; } = 200f;
public float Throttle { get; set; } = 0f;
public bool IsRunning => RPM > StallSpeed;
// Torque characteristics
public Dictionary<float, float> TorqueCurve { get; set; } = new()
{
// RPM - Torque Nm
{ 0f, 0f },
{ 800f, 150f }, // Idle
{ 2000f, 200f }, // Peak torque
{ 4500f, 250f },
{ 7200f, 250f },
{ 9200f, 250f },
{ 10000f, 200f },
{ 11000f, 0f }
};
public Engine()
{
// Start with idle energy
FlywheelEnergy = GetEnergyFromRPM(IdleRPM);
}
// Calculations
public float CalculateFrictionEnergy(float deltaTime)
{
// Real friction torque data for 2.0L engine (Nm)
float frictionTorque;
if (RPM < 500) frictionTorque = 15f; // Static/breakaway
else if (RPM < 1000) frictionTorque = 14f;
else if (RPM < 2000) frictionTorque = 16f;
else if (RPM < 3000) frictionTorque = 18f;
else if (RPM < 4000) frictionTorque = 21f;
else if (RPM < 5000) frictionTorque = 25f;
else if (RPM < 6000) frictionTorque = 30f;
else if (RPM < 7000) frictionTorque = 36f;
else frictionTorque = 44f;
float frictionPower = frictionTorque * AngularVelocity;
return frictionPower * deltaTime;
}
private float CalculateCombustionEnergy(float deltaTime)
{
float torque = GetTorqueOutput() * GetActualThrottle();
return torque * AngularVelocity * deltaTime;
}
private float CalculateLoadEnergy(float deltaTime, float loadTorque)
{
return loadTorque * AngularVelocity * deltaTime;
}
// Get
public float GetActualThrottle()
{
float idleThrottle = Math.Max((IdleRPM - RPM) / 10, 0);
return Math.Clamp(Throttle + idleThrottle, 0, 1);
}
public float GetOmega()
{
if (FlywheelEnergy <= 0) return 0;
return MathF.Sqrt(2f * FlywheelEnergy / MomentOfInertia);
}
public float GetRPM()
{
return GetOmega() * PhysicsUtil.RAD_PER_SEC_TO_RPM;
}
// Set
public float GetEnergyFromRPM(float rpm)
{
float omega = rpm * PhysicsUtil.RPM_TO_RAD_PER_SEC;
return 0.5f * MomentOfInertia * omega * omega;
}
// torque curve
public float GetTorqueOutput()
{
if (RPM <= 0) return 0;
var points = TorqueCurve.OrderBy(p => p.Key).ToList();
if (RPM <= points.First().Key) return points.First().Value;
if (RPM >= points.Last().Key) return points.Last().Value;
for (int i = 0; i < points.Count - 1; i++)
{
if (RPM >= points[i].Key && RPM <= points[i + 1].Key)
{
float t = (RPM - points[i].Key) / (points[i + 1].Key - points[i].Key);
return PhysicsUtil.Lerp(points[i].Value, points[i + 1].Value, t);
}
}
return 0f;
}
public void ApplyTorque(float torque, float deltaTime)
{
if (torque == 0) return;
float work = torque * AngularVelocity * deltaTime;
FlywheelEnergy += work;
FlywheelEnergy = Math.Max(FlywheelEnergy, 0);
}
public void Update(float deltaTime, float loadTorque)
{
float combustionEnergy = CalculateCombustionEnergy(deltaTime);
float frictionEnergy = CalculateFrictionEnergy(deltaTime);
float loadEnergy = CalculateLoadEnergy(deltaTime, loadTorque);
float netEnergy = combustionEnergy - frictionEnergy - loadEnergy;
CurrentPower = netEnergy / deltaTime;
FlywheelEnergy += netEnergy;
FlywheelEnergy = Math.Max(FlywheelEnergy, 0);
}
}
}