154 lines
4.9 KiB
C#
154 lines
4.9 KiB
C#
namespace Car_simulation
|
|
{
|
|
public class Engine
|
|
{
|
|
// Energy state
|
|
public float FlywheelEnergy { get; set; }
|
|
|
|
// Values
|
|
public float RPM => GetRPM();
|
|
public float AngularVelocity => GetOmega();
|
|
public float CurrentPower { get; private set; }
|
|
|
|
// Physical properties
|
|
public float MomentOfInertia { get; set; } = 0.25f;
|
|
public float IdleRPM { get; set; } = 800f;
|
|
public float RevLimit { get; set; } = 12000;
|
|
public float StallSpeed { get; set; } = 200f;
|
|
public float Throttle { get; set; } = 0f;
|
|
public bool IsRunning => RPM > StallSpeed;
|
|
private float _cutoffUntil = 0;
|
|
private bool _cutoff = false;
|
|
|
|
// Torque curve
|
|
public Dictionary<float, float> TorqueCurve { get; set; } = new()
|
|
{
|
|
{ 0f, 0f },
|
|
{ 800f, 200f },
|
|
{ 2000f, 300 },
|
|
{ 4500f, 300f },
|
|
{ 6800f, 300 },
|
|
{ 7200f, 350 },
|
|
{ 11000f, 600f },
|
|
{ 12500, 500f },
|
|
};
|
|
|
|
public Engine()
|
|
{
|
|
FlywheelEnergy = GetEnergyFromRPM(IdleRPM);
|
|
}
|
|
|
|
public void UpdateRevLimiter(float totalTime)
|
|
{
|
|
if (RPM > RevLimit)
|
|
{
|
|
_cutoffUntil = totalTime + 0.01f;
|
|
}
|
|
|
|
_cutoff = (totalTime < _cutoffUntil);
|
|
}
|
|
|
|
public float CalculateFrictionLoss(float deltaTime)
|
|
{
|
|
float frictionTorque;
|
|
|
|
// Realistic friction based on RPM
|
|
if (RPM < 500) frictionTorque = 15f;
|
|
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;
|
|
}
|
|
|
|
public float CalculateCombustionPower(float deltaTime)
|
|
{
|
|
float throttle = GetActualThrottle();
|
|
if (_cutoff) throttle = 0;
|
|
float torque = GetTorqueOutput() * throttle;
|
|
return torque * AngularVelocity * deltaTime;
|
|
}
|
|
|
|
public float GetActualThrottle()
|
|
{
|
|
// Idle control: maintain idle speed when throttle is low
|
|
if (RPM < IdleRPM && Throttle < 0.1f)
|
|
{
|
|
float idleThrottle = (IdleRPM - RPM) / 200f;
|
|
return Math.Clamp(idleThrottle, 0.1f, 0.3f);
|
|
}
|
|
|
|
return Throttle;
|
|
}
|
|
|
|
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;
|
|
}
|
|
|
|
public float GetEnergyFromRPM(float rpm)
|
|
{
|
|
float omega = rpm * PhysicsUtil.RPM_TO_RAD_PER_SEC;
|
|
return 0.5f * MomentOfInertia * omega * omega;
|
|
}
|
|
|
|
public float GetTorqueOutput()
|
|
{
|
|
if (RPM <= 400) 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 Update(float deltaTime, float totalTime)
|
|
{
|
|
UpdateRevLimiter(totalTime);
|
|
// Combustion adds energy (if throttle > 0)
|
|
float combustionEnergy = CalculateCombustionPower(deltaTime);
|
|
|
|
// Friction always removes energy
|
|
float frictionLoss = CalculateFrictionLoss(deltaTime);
|
|
|
|
// Net energy change from combustion and friction ONLY
|
|
// Note: Drivetrain energy transfer happens separately in Drivetrain.Update()
|
|
float netEnergy = combustionEnergy - frictionLoss;
|
|
CurrentPower = netEnergy / deltaTime;
|
|
|
|
FlywheelEnergy += netEnergy;
|
|
|
|
// Stall protection - keep engine running if it has throttle
|
|
float stallEnergy = GetEnergyFromRPM(StallSpeed);
|
|
if (FlywheelEnergy < stallEnergy && Throttle > 0.1f)
|
|
{
|
|
FlywheelEnergy = stallEnergy * 1.2f;
|
|
}
|
|
|
|
FlywheelEnergy = Math.Max(FlywheelEnergy, 0);
|
|
}
|
|
}
|
|
} |