163 lines
5.2 KiB
C#
163 lines
5.2 KiB
C#
using Car_simulation.Core.Physics;
|
|
|
|
namespace Car_simulation.Core.Components
|
|
{
|
|
public class Engine : ICarComponent
|
|
{
|
|
// Energy state
|
|
public float FlywheelEnergy { get; set; }
|
|
|
|
// Physical properties
|
|
public float MomentOfInertia { get; set; } = 0.25f;
|
|
public float IdleRPM { get; set; } = 800f;
|
|
public float RevLimit { get; set; } = 7000;
|
|
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
|
|
private TorqueCurve _torqueCurve;
|
|
|
|
public Engine()
|
|
{
|
|
FlywheelEnergy = GetEnergyFromRPM(IdleRPM);
|
|
_torqueCurve = new TorqueCurve();
|
|
InitializeDefaultCurve();
|
|
}
|
|
|
|
private void InitializeDefaultCurve()
|
|
{
|
|
_torqueCurve.AddPoint(0f, 0f);
|
|
_torqueCurve.AddPoint(800f, 95f);
|
|
_torqueCurve.AddPoint(1500f, 160f);
|
|
_torqueCurve.AddPoint(2500f, 200f);
|
|
_torqueCurve.AddPoint(4000f, 235f);
|
|
_torqueCurve.AddPoint(5000f, 230f);
|
|
_torqueCurve.AddPoint(6000f, 210f);
|
|
_torqueCurve.AddPoint(6800f, 185f);
|
|
_torqueCurve.AddPoint(7200f, 170f);
|
|
}
|
|
|
|
public float RPM => GetRPM();
|
|
public float AngularVelocity => GetOmega();
|
|
public float CurrentPower { get; private set; }
|
|
|
|
public void Update(float deltaTime)
|
|
{
|
|
// Engine updates are now handled through Car with totalTime parameter
|
|
}
|
|
|
|
public void UpdateWithTime(float deltaTime, float totalTime)
|
|
{
|
|
UpdateRevLimiter(totalTime);
|
|
|
|
float combustionEnergy = CalculateCombustionPower(deltaTime);
|
|
float frictionLoss = CalculateFrictionLoss(deltaTime);
|
|
float netEnergy = combustionEnergy - frictionLoss;
|
|
|
|
CurrentPower = netEnergy / deltaTime;
|
|
FlywheelEnergy += netEnergy;
|
|
|
|
// Stall protection
|
|
float stallEnergy = GetEnergyFromRPM(StallSpeed);
|
|
if (FlywheelEnergy < stallEnergy && Throttle > 0.1f)
|
|
{
|
|
FlywheelEnergy = stallEnergy * 1.2f;
|
|
}
|
|
|
|
FlywheelEnergy = Math.Max(FlywheelEnergy, 0);
|
|
}
|
|
|
|
private void UpdateRevLimiter(float totalTime)
|
|
{
|
|
if (RPM > RevLimit)
|
|
{
|
|
_cutoffUntil = totalTime + 0.01f;
|
|
}
|
|
_cutoff = (totalTime < _cutoffUntil);
|
|
}
|
|
|
|
private float CalculateFrictionLoss(float deltaTime)
|
|
{
|
|
float frictionTorque = GetFrictionTorque();
|
|
float frictionPower = frictionTorque * AngularVelocity;
|
|
return frictionPower * deltaTime;
|
|
}
|
|
|
|
private float GetFrictionTorque()
|
|
{
|
|
return RPM switch
|
|
{
|
|
< 500 => 15f,
|
|
< 1000 => 14f,
|
|
< 2000 => 16f,
|
|
< 3000 => 18f,
|
|
< 4000 => 21f,
|
|
< 5000 => 25f,
|
|
< 6000 => 30f,
|
|
< 7000 => 36f,
|
|
_ => 44f
|
|
};
|
|
}
|
|
|
|
private float CalculateCombustionPower(float deltaTime)
|
|
{
|
|
float throttle = GetActualThrottle();
|
|
if (_cutoff) throttle = 0;
|
|
float torque = GetTorqueOutput() * throttle;
|
|
return torque * AngularVelocity * deltaTime;
|
|
}
|
|
|
|
public float GetActualThrottle()
|
|
{
|
|
if (RPM < IdleRPM && Throttle < 0.1f)
|
|
{
|
|
float idleThrottle = (IdleRPM - RPM) / 200f;
|
|
return Math.Clamp(idleThrottle, 0.1f, 0.3f);
|
|
}
|
|
return Throttle;
|
|
}
|
|
|
|
public float GetOmega() => MathF.Sqrt(2f * FlywheelEnergy / MomentOfInertia);
|
|
public float GetRPM() => 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() => _torqueCurve.GetTorqueAtRPM(RPM);
|
|
}
|
|
|
|
public class TorqueCurve
|
|
{
|
|
private List<(float RPM, float Torque)> _points = new List<(float, float)>();
|
|
|
|
public void AddPoint(float rpm, float torque) => _points.Add((rpm, torque));
|
|
|
|
public float GetTorqueAtRPM(float rpm)
|
|
{
|
|
if (rpm <= 400) return 0;
|
|
if (_points.Count == 0) return 0;
|
|
|
|
var orderedPoints = _points.OrderBy(p => p.RPM).ToList();
|
|
if (rpm <= orderedPoints.First().RPM) return orderedPoints.First().Torque;
|
|
if (rpm >= orderedPoints.Last().RPM) return orderedPoints.Last().Torque;
|
|
|
|
for (int i = 0; i < orderedPoints.Count - 1; i++)
|
|
{
|
|
if (rpm >= orderedPoints[i].RPM && rpm <= orderedPoints[i + 1].RPM)
|
|
{
|
|
float t = (rpm - orderedPoints[i].RPM) / (orderedPoints[i + 1].RPM - orderedPoints[i].RPM);
|
|
return PhysicsUtil.Lerp(orderedPoints[i].Torque, orderedPoints[i + 1].Torque, t);
|
|
}
|
|
}
|
|
|
|
return 0f;
|
|
}
|
|
}
|
|
} |