major rework
This commit is contained in:
29
Car simulation/Core/Components/Aerodynamics.cs
Normal file
29
Car simulation/Core/Components/Aerodynamics.cs
Normal file
@@ -0,0 +1,29 @@
|
||||
using Car_simulation.Core.Physics;
|
||||
|
||||
namespace Car_simulation.Core.Components
|
||||
{
|
||||
public class Aerodynamics
|
||||
{
|
||||
public float DragCoefficient { get; set; } = 0.3f;
|
||||
public float FrontalArea { get; set; } = 2.2f; // m²
|
||||
public float RollingResistanceCoefficient { get; set; } = 0.015f;
|
||||
|
||||
private readonly ResistanceCalculator _resistanceCalculator = new ResistanceCalculator();
|
||||
|
||||
public float CalculateDragForce(float speed)
|
||||
{
|
||||
return _resistanceCalculator.CalculateDragForce(speed, DragCoefficient, FrontalArea);
|
||||
}
|
||||
|
||||
public float CalculateRollingResistanceForce(float mass)
|
||||
{
|
||||
return _resistanceCalculator.CalculateRollingResistanceForce(mass, RollingResistanceCoefficient);
|
||||
}
|
||||
|
||||
public float CalculateTotalResistanceForce(float speed, float mass)
|
||||
{
|
||||
return _resistanceCalculator.CalculateTotalResistanceForce(
|
||||
speed, mass, DragCoefficient, FrontalArea, RollingResistanceCoefficient);
|
||||
}
|
||||
}
|
||||
}
|
||||
30
Car simulation/Core/Components/BrakeSystem.cs
Normal file
30
Car simulation/Core/Components/BrakeSystem.cs
Normal file
@@ -0,0 +1,30 @@
|
||||
using Car_simulation.Core.Physics;
|
||||
|
||||
namespace Car_simulation.Core.Components
|
||||
{
|
||||
public class BrakeSystem : ICarComponent
|
||||
{
|
||||
public float BrakeInput { get; set; } = 0f;
|
||||
public float MaxBrakeTorque { get; set; } = 3000f;
|
||||
|
||||
private WheelSystem _wheelSystem;
|
||||
|
||||
public BrakeSystem(WheelSystem wheelSystem)
|
||||
{
|
||||
_wheelSystem = wheelSystem;
|
||||
}
|
||||
|
||||
public void Update(float deltaTime)
|
||||
{
|
||||
if (BrakeInput <= 0) return;
|
||||
|
||||
float brakeTorque = BrakeInput * MaxBrakeTorque;
|
||||
_wheelSystem.ApplyTorque(-brakeTorque, deltaTime);
|
||||
}
|
||||
|
||||
public float GetBrakeTorque()
|
||||
{
|
||||
return BrakeInput * MaxBrakeTorque;
|
||||
}
|
||||
}
|
||||
}
|
||||
126
Car simulation/Core/Components/Drivetrain.cs
Normal file
126
Car simulation/Core/Components/Drivetrain.cs
Normal file
@@ -0,0 +1,126 @@
|
||||
using Car_simulation.Core.Physics;
|
||||
|
||||
namespace Car_simulation.Core.Components
|
||||
{
|
||||
public class Drivetrain : ICarComponent
|
||||
{
|
||||
public Engine Engine { get; private set; }
|
||||
public WheelSystem WheelSystem { get; private set; }
|
||||
|
||||
private int _currentGear = 1;
|
||||
public float[] GearRatios { get; set; } = { 3.8f, 2.5f, 1.8f, 1.3f, 1.0f, 0.8f, 0.65f };
|
||||
public float FinalDriveRatio { get; set; } = 4.0f;
|
||||
public float Efficiency { get; set; } = 0.95f;
|
||||
public float ClutchEngagement { get; set; } = 0f;
|
||||
|
||||
public float MaxClutchTorque { get; set; } = 400f;
|
||||
public float ClutchStiffness { get; set; } = 50f;
|
||||
|
||||
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;
|
||||
}
|
||||
|
||||
float expectedWheelOmega = Engine.AngularVelocity / TotalRatio;
|
||||
float actualWheelOmega = WheelSystem.AngularVelocity;
|
||||
float omegaDifference = actualWheelOmega - expectedWheelOmega;
|
||||
|
||||
float maxClutchTorque = MaxClutchTorque * ClutchEngagement;
|
||||
float desiredTorque = -omegaDifference * ClutchStiffness;
|
||||
desiredTorque = Math.Clamp(desiredTorque, -maxClutchTorque, maxClutchTorque);
|
||||
|
||||
if (desiredTorque > 0)
|
||||
{
|
||||
float engineTorque = Engine.GetTorqueOutput() * Engine.GetActualThrottle();
|
||||
float maxEngineTorqueAtWheels = engineTorque * TotalRatio * Efficiency;
|
||||
desiredTorque = Math.Min(desiredTorque, maxEngineTorqueAtWheels);
|
||||
}
|
||||
|
||||
ClutchTorque = desiredTorque;
|
||||
|
||||
float energyTransferred = 0f;
|
||||
|
||||
if (omegaDifference > 0.01f) // Wheels → Engine
|
||||
{
|
||||
float power = ClutchTorque * Engine.AngularVelocity;
|
||||
energyTransferred = power * deltaTime;
|
||||
float wheelEnergyLoss = Math.Abs(energyTransferred);
|
||||
float engineEnergyGain = wheelEnergyLoss * Efficiency;
|
||||
|
||||
WheelSystem.TotalEnergy -= wheelEnergyLoss;
|
||||
Engine.FlywheelEnergy += engineEnergyGain;
|
||||
}
|
||||
else if (omegaDifference < -0.01f) // Engine → Wheels
|
||||
{
|
||||
float power = -ClutchTorque * Engine.AngularVelocity;
|
||||
energyTransferred = power * deltaTime;
|
||||
float engineEnergyLoss = Math.Abs(energyTransferred);
|
||||
float wheelEnergyGain = engineEnergyLoss * Efficiency;
|
||||
|
||||
Engine.FlywheelEnergy -= engineEnergyLoss;
|
||||
WheelSystem.TotalEnergy += wheelEnergyGain;
|
||||
}
|
||||
|
||||
TransmittedPower = energyTransferred / deltaTime;
|
||||
|
||||
if (maxClutchTorque > 0)
|
||||
{
|
||||
float torqueRatio = Math.Abs(ClutchTorque) / maxClutchTorque;
|
||||
ClutchSlipRatio = torqueRatio;
|
||||
}
|
||||
else
|
||||
{
|
||||
ClutchSlipRatio = 1f;
|
||||
}
|
||||
}
|
||||
|
||||
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() => ClutchSlipRatio * 100f;
|
||||
|
||||
public void GearUp() { if (_currentGear < GearRatios.Length) _currentGear++; }
|
||||
public void GearDown() { if (_currentGear > 1) _currentGear--; }
|
||||
}
|
||||
}
|
||||
163
Car simulation/Core/Components/Engine.cs
Normal file
163
Car simulation/Core/Components/Engine.cs
Normal file
@@ -0,0 +1,163 @@
|
||||
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;
|
||||
}
|
||||
}
|
||||
}
|
||||
7
Car simulation/Core/Components/ICarComponent.cs
Normal file
7
Car simulation/Core/Components/ICarComponent.cs
Normal file
@@ -0,0 +1,7 @@
|
||||
namespace Car_simulation.Core.Components
|
||||
{
|
||||
public interface ICarComponent
|
||||
{
|
||||
void Update(float deltaTime);
|
||||
}
|
||||
}
|
||||
95
Car simulation/Core/Components/WheelSystem.cs
Normal file
95
Car simulation/Core/Components/WheelSystem.cs
Normal file
@@ -0,0 +1,95 @@
|
||||
using Car_simulation.Core.Physics;
|
||||
|
||||
namespace Car_simulation.Core.Components
|
||||
{
|
||||
public class WheelSystem : ICarComponent
|
||||
{
|
||||
// Physical properties
|
||||
public float Radius { get; set; } = 0.3f;
|
||||
public float WheelInertia { get; set; } = 2.0f;
|
||||
public float CarMass { get; set; } = 1500f;
|
||||
public int WheelCount { get; set; } = 4;
|
||||
public int DrivenWheels { get; set; } = 2;
|
||||
|
||||
// State
|
||||
public float TotalEnergy { get; set; } = 0f;
|
||||
public float AngularVelocity => GetOmega();
|
||||
public float RPM => GetRPM();
|
||||
public float CarSpeed => GetCarSpeed();
|
||||
|
||||
// Derived properties
|
||||
public float GetTotalRotationalInertia() => WheelInertia * WheelCount;
|
||||
public float GetEquivalentCarInertia() => CarMass * Radius * Radius;
|
||||
public float GetTotalInertia() => GetTotalRotationalInertia() + GetEquivalentCarInertia();
|
||||
|
||||
// Calculations
|
||||
public float GetOmega()
|
||||
{
|
||||
if (TotalEnergy <= 0 || GetTotalInertia() <= 0) return 0f;
|
||||
return MathF.Sqrt(2f * TotalEnergy / GetTotalInertia());
|
||||
}
|
||||
|
||||
public float GetRPM() => AngularVelocity * PhysicsUtil.RAD_PER_SEC_TO_RPM;
|
||||
public float GetCarSpeed() => AngularVelocity * Radius;
|
||||
|
||||
public float GetRotationalEnergy()
|
||||
{
|
||||
float omega = GetOmega();
|
||||
return 0.5f * GetTotalRotationalInertia() * omega * omega;
|
||||
}
|
||||
|
||||
public float GetTranslationalEnergy()
|
||||
{
|
||||
float speed = GetCarSpeed();
|
||||
return 0.5f * CarMass * speed * speed;
|
||||
}
|
||||
|
||||
public float GetEnergyFromSpeed(float speed)
|
||||
{
|
||||
float omega = speed / Radius;
|
||||
float rotationalEnergy = 0.5f * GetTotalRotationalInertia() * omega * omega;
|
||||
float translationalEnergy = 0.5f * CarMass * speed * speed;
|
||||
return rotationalEnergy + translationalEnergy;
|
||||
}
|
||||
|
||||
public void SetSpeed(float speed) => TotalEnergy = GetEnergyFromSpeed(speed);
|
||||
|
||||
public void ApplyWork(float work)
|
||||
{
|
||||
TotalEnergy += work;
|
||||
TotalEnergy = Math.Max(TotalEnergy, 0);
|
||||
}
|
||||
|
||||
public void ApplyTorque(float torque, float deltaTime)
|
||||
{
|
||||
if (torque == 0) return;
|
||||
float work = torque * AngularVelocity * deltaTime;
|
||||
ApplyWork(work);
|
||||
}
|
||||
|
||||
public void ApplyResistance(float resistanceTorque, float deltaTime)
|
||||
{
|
||||
if (resistanceTorque <= 0 || AngularVelocity == 0) return;
|
||||
|
||||
float omega = AngularVelocity;
|
||||
if (MathF.Abs(omega) < 0.1f) return;
|
||||
|
||||
float resistanceSign = -MathF.Sign(omega);
|
||||
float alpha = (resistanceSign * resistanceTorque) / GetTotalInertia();
|
||||
float omegaNew = omega + alpha * deltaTime;
|
||||
|
||||
if (MathF.Sign(omegaNew) != MathF.Sign(omega))
|
||||
{
|
||||
omegaNew = 0;
|
||||
}
|
||||
|
||||
float energyNew = 0.5f * GetTotalInertia() * omegaNew * omegaNew;
|
||||
TotalEnergy = Math.Max(energyNew, 0);
|
||||
}
|
||||
|
||||
public void Update(float deltaTime)
|
||||
{
|
||||
// WheelSystem updates are handled by Car through other components
|
||||
}
|
||||
}
|
||||
}
|
||||
153
Car simulation/Core/Models/Car.cs
Normal file
153
Car simulation/Core/Models/Car.cs
Normal file
@@ -0,0 +1,153 @@
|
||||
using Car_simulation.Core.Components;
|
||||
using Car_simulation.Core.Physics;
|
||||
using Car_simulation.Audio;
|
||||
|
||||
namespace Car_simulation.Core.Models
|
||||
{
|
||||
public class Car
|
||||
{
|
||||
// Basic properties
|
||||
public Vector2 Position = new Vector2(0, 0);
|
||||
public float Mass { get; set; } = 2000f;
|
||||
|
||||
// Inputs
|
||||
public float ThrottleInput = 0f;
|
||||
public float BrakeInput = 0f;
|
||||
public float ClutchInput = 1f;
|
||||
public bool ForceClutch = false;
|
||||
public float SteeringInput = 0f;
|
||||
|
||||
// Components
|
||||
public Engine Engine { get; private set; }
|
||||
public Drivetrain Drivetrain { get; private set; }
|
||||
public WheelSystem WheelSystem { get; private set; }
|
||||
public BrakeSystem BrakeSystem { get; private set; }
|
||||
public Aerodynamics Aerodynamics { get; private set; }
|
||||
|
||||
// Derived properties
|
||||
public Vector2 Velocity => new Vector2(WheelSystem.CarSpeed, 0);
|
||||
public float Speed => WheelSystem.CarSpeed;
|
||||
|
||||
// Audio
|
||||
private EngineSound _engineSound;
|
||||
private bool _audioEnabled = true;
|
||||
|
||||
public Car()
|
||||
{
|
||||
InitializeComponents();
|
||||
InitializeAudio();
|
||||
}
|
||||
|
||||
private void InitializeComponents()
|
||||
{
|
||||
Engine = new Engine();
|
||||
WheelSystem = new WheelSystem();
|
||||
WheelSystem.CarMass = Mass;
|
||||
WheelSystem.WheelCount = 4;
|
||||
WheelSystem.DrivenWheels = 2;
|
||||
|
||||
Drivetrain = new Drivetrain(Engine, WheelSystem);
|
||||
BrakeSystem = new BrakeSystem(WheelSystem);
|
||||
Aerodynamics = new Aerodynamics();
|
||||
}
|
||||
|
||||
private void InitializeAudio()
|
||||
{
|
||||
try
|
||||
{
|
||||
_engineSound = new EngineSound();
|
||||
_engineSound.SetEngineState(Engine.IdleRPM, 0f);
|
||||
_engineSound.StartSound();
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
Console.WriteLine($"Audio initialization failed: {ex.Message}");
|
||||
_audioEnabled = false;
|
||||
}
|
||||
}
|
||||
|
||||
public List<string> GetDisplayData()
|
||||
{
|
||||
return new List<string>
|
||||
{
|
||||
$"Engine Energy: {Engine.FlywheelEnergy,7:F0} J",
|
||||
$"Engine Torque: {Engine.GetTorqueOutput(),7:F0} Nm",
|
||||
$"Engine RPM: {Engine.RPM,7:F0}",
|
||||
$"Total Energy: {WheelSystem.TotalEnergy,7:F0} J",
|
||||
$" (Wheel Rot: {WheelSystem.GetRotationalEnergy(),7:F0} J)",
|
||||
$" (Car Trans: {WheelSystem.GetTranslationalEnergy(),7:F0} J)",
|
||||
$"Wheel RPM: {WheelSystem.RPM,7:F0}",
|
||||
$"Vehicle: {Speed * 3.6f,7:F1} km/h",
|
||||
$"Throttle: {Engine.GetActualThrottle() * 100,6:F1}%",
|
||||
$"Power: {Engine.CurrentPower / 1000,6:F1} kW",
|
||||
$"Transmitted: {Drivetrain.TransmittedPower / 1000,6:F1} kW",
|
||||
$"Brake: {BrakeInput * 100,6:F1}%",
|
||||
$"Speed Diff: {Drivetrain.GetSpeedDifferenceRPM(),6:F0} RPM",
|
||||
$"Clutch: {ClutchInput * 100,6:F1}% disengaged",
|
||||
$"Clutch T: {Drivetrain.ClutchTorque,6:F0} Nm",
|
||||
$"Clutch Slip: {Drivetrain.GetClutchSlipPercent(),6:F1}%",
|
||||
$"Resistance: {CalculateTotalResistanceForce(),6:F1} N",
|
||||
$"Drag: {CalculateDragForce(),6:F1} N",
|
||||
$"Rolling: {CalculateRollingResistanceForce(),6:F1} N",
|
||||
$"Gear: {Drivetrain.GetCurrentGearName(),3} (Ratio: {Drivetrain.GearRatio:F2}:1)"
|
||||
};
|
||||
}
|
||||
|
||||
public void Update(float deltaTime, float totalTime)
|
||||
{
|
||||
// Update inputs
|
||||
Engine.Throttle = ThrottleInput;
|
||||
Drivetrain.ClutchEngagement = 1f - ClutchInput;
|
||||
BrakeSystem.BrakeInput = BrakeInput;
|
||||
|
||||
if (ForceClutch)
|
||||
Drivetrain.ClutchEngagement = 0f;
|
||||
|
||||
// Update components
|
||||
Engine.UpdateWithTime(deltaTime, totalTime);
|
||||
Drivetrain.Update(deltaTime);
|
||||
|
||||
// Apply resistance
|
||||
float resistanceForce = CalculateTotalResistanceForce();
|
||||
WheelSystem.ApplyResistance(resistanceForce * WheelSystem.Radius, deltaTime);
|
||||
|
||||
// Apply braking
|
||||
BrakeSystem.Update(deltaTime);
|
||||
|
||||
// Update position
|
||||
Position += Velocity * deltaTime;
|
||||
|
||||
// Update audio
|
||||
if (_audioEnabled)
|
||||
UpdateAudio();
|
||||
}
|
||||
|
||||
private void UpdateAudio()
|
||||
{
|
||||
try
|
||||
{
|
||||
float throttle = Engine.GetActualThrottle();
|
||||
_engineSound.SetEngineState(Engine.RPM, throttle);
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
Console.WriteLine($"Audio update error: {ex.Message}");
|
||||
}
|
||||
}
|
||||
|
||||
public float CalculateTotalResistanceForce()
|
||||
{
|
||||
return Aerodynamics.CalculateTotalResistanceForce(Speed, Mass);
|
||||
}
|
||||
|
||||
public float CalculateDragForce()
|
||||
{
|
||||
return Aerodynamics.CalculateDragForce(Speed);
|
||||
}
|
||||
|
||||
public float CalculateRollingResistanceForce()
|
||||
{
|
||||
return Aerodynamics.CalculateRollingResistanceForce(Mass);
|
||||
}
|
||||
}
|
||||
}
|
||||
14
Car simulation/Core/Models/CarState.cs
Normal file
14
Car simulation/Core/Models/CarState.cs
Normal file
@@ -0,0 +1,14 @@
|
||||
namespace Car_simulation.Core.Models
|
||||
{
|
||||
public class CarState
|
||||
{
|
||||
public float Speed { get; set; }
|
||||
public float RPM { get; set; }
|
||||
public float Throttle { get; set; }
|
||||
public float Brake { get; set; }
|
||||
public string Gear { get; set; }
|
||||
public float ClutchSlip { get; set; }
|
||||
public float EnginePower { get; set; }
|
||||
public float TransmittedPower { get; set; }
|
||||
}
|
||||
}
|
||||
30
Car simulation/Core/Physics/PhysicsUtil.cs
Normal file
30
Car simulation/Core/Physics/PhysicsUtil.cs
Normal file
@@ -0,0 +1,30 @@
|
||||
namespace Car_simulation.Core.Physics
|
||||
{
|
||||
public static class PhysicsUtil
|
||||
{
|
||||
public const float G = 9.81f;
|
||||
public const float AirDensity = 1.225f;
|
||||
public const float RAD_PER_SEC_TO_RPM = 60f / (2f * MathF.PI);
|
||||
public const float RPM_TO_RAD_PER_SEC = (2f * MathF.PI) / 60f;
|
||||
|
||||
public static float Lerp(float a, float b, float t)
|
||||
{
|
||||
t = Math.Clamp(t, 0f, 1f);
|
||||
return a + (b - a) * t;
|
||||
}
|
||||
|
||||
public static float RPMToOmega(float rpm) => rpm * MathF.PI * 2f / 60f;
|
||||
public static float OmegaToRPM(float omega) => omega * 60f / (2f * MathF.PI);
|
||||
|
||||
public static float CalculateRotationalEnergy(float inertia, float omega)
|
||||
{
|
||||
return 0.5f * inertia * omega * omega;
|
||||
}
|
||||
|
||||
public static float CalculateOmegaFromEnergy(float energy, float inertia)
|
||||
{
|
||||
if (energy <= 0) return 0;
|
||||
return MathF.Sqrt(2f * energy / inertia);
|
||||
}
|
||||
}
|
||||
}
|
||||
25
Car simulation/Core/Physics/ResistanceCalculator.cs
Normal file
25
Car simulation/Core/Physics/ResistanceCalculator.cs
Normal file
@@ -0,0 +1,25 @@
|
||||
using Car_simulation.Core.Components;
|
||||
|
||||
namespace Car_simulation.Core.Physics
|
||||
{
|
||||
public class ResistanceCalculator
|
||||
{
|
||||
public float CalculateDragForce(float speed, float dragCoefficient, float frontalArea)
|
||||
{
|
||||
return 0.5f * PhysicsUtil.AirDensity * dragCoefficient * frontalArea * speed * speed;
|
||||
}
|
||||
|
||||
public float CalculateRollingResistanceForce(float mass, float rollingResistanceCoefficient)
|
||||
{
|
||||
return rollingResistanceCoefficient * mass * PhysicsUtil.G;
|
||||
}
|
||||
|
||||
public float CalculateTotalResistanceForce(float speed, float mass,
|
||||
float dragCoefficient, float frontalArea, float rollingResistanceCoefficient)
|
||||
{
|
||||
float dragForce = CalculateDragForce(speed, dragCoefficient, frontalArea);
|
||||
float rollingForce = CalculateRollingResistanceForce(mass, rollingResistanceCoefficient);
|
||||
return dragForce + rollingForce;
|
||||
}
|
||||
}
|
||||
}
|
||||
38
Car simulation/Core/Physics/Vector2.cs
Normal file
38
Car simulation/Core/Physics/Vector2.cs
Normal file
@@ -0,0 +1,38 @@
|
||||
namespace Car_simulation.Core.Physics
|
||||
{
|
||||
public struct Vector2
|
||||
{
|
||||
public float X, Y;
|
||||
|
||||
public Vector2(float x, float y) { X = x; Y = y; }
|
||||
|
||||
public float Length => MathF.Sqrt(X * X + Y * Y);
|
||||
public float LengthSquared => X * X + Y * Y;
|
||||
|
||||
public Vector2 Normalized()
|
||||
{
|
||||
float length = Length;
|
||||
if (length > 0.0001f)
|
||||
return new Vector2(X / length, Y / length);
|
||||
return new Vector2(0, 0);
|
||||
}
|
||||
|
||||
public void Normalize()
|
||||
{
|
||||
float length = Length;
|
||||
if (length > 0.0001f)
|
||||
{
|
||||
X /= length;
|
||||
Y /= length;
|
||||
}
|
||||
}
|
||||
|
||||
public static Vector2 Normalize(Vector2 v) => v.Normalized();
|
||||
|
||||
public static Vector2 operator *(Vector2 v, float s) => new Vector2(v.X * s, v.Y * s);
|
||||
public static Vector2 operator *(float s, Vector2 v) => new Vector2(v.X * s, v.Y * s);
|
||||
public static Vector2 operator /(Vector2 v, float s) => new Vector2(v.X / s, v.Y / s);
|
||||
public static Vector2 operator +(Vector2 a, Vector2 b) => new Vector2(a.X + b.X, a.Y + b.Y);
|
||||
public static Vector2 operator -(Vector2 a, Vector2 b) => new Vector2(a.X - b.X, a.Y - b.Y);
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user