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

221 lines
7.9 KiB
C#
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

using static SFML.Window.Mouse;
namespace Car_simulation
{
public class Car
{
public Vector2 Position = new Vector2(0, 0);
public Vector2 Velocity = new Vector2(0, 0);
public float Speed => Velocity.Length;
public float Mass = 1500f; // kg
public int WheelCount = 4;
public int DrivenWheels = 2;
public float ThrottleInput = 0f;
public float BrakeInput = 0f;
public float ClutchInput = 1f; // 0 = engaged, 1 = disengaged
public bool ForceClutch = false;
public float SteeringInput = 0f;
// Aerodynamics
private const float AirDensity = 1.225f;
public float DragCoefficient = 0.1f;
public float FrontalArea = 2.2f; // m²
public float RollingResistanceCoefficient = 0.015f;
// Components
public Engine Engine;
public Drivetrain Drivetrain;
public WheelSystem WheelSystem;
private EngineSound _engineSound;
private bool _audioEnabled = true;
public Car()
{
Engine = new Engine();
WheelSystem = new WheelSystem();
Drivetrain = new Drivetrain(Engine, WheelSystem);
// Initial setup
WheelSystem.WheelCount = WheelCount;
WheelSystem.DrivenWheels = DrivenWheels;
InitializeAudio();
}
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 void Update(float deltaTime)
{
Engine.Throttle = ThrottleInput;
Drivetrain.ClutchEngagement = 1f - ClutchInput; // Convert: 0 input = 1 engagement
if (ForceClutch)
Drivetrain.ClutchEngagement = 0f;
float resistanceTorque = CalculateResistanceTorque();
WheelSystem.ResistanceTorque = resistanceTorque;
Drivetrain.Update(deltaTime);
WheelSystem.ApplyResistance(deltaTime);
float engineLoad = Drivetrain.CalculateEngineLoad(deltaTime);
Engine.Update(deltaTime, engineLoad);
UpdateVehicleMotion(deltaTime);
ApplyBraking(deltaTime);
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}");
}
}
private void UpdateVehicleMotion(float deltaTime)
{
// Calculate net force
float tractiveForce = CalculateTractiveForce();
float resistanceForce = CalculateTotalResistanceForce();
float netForce = tractiveForce - resistanceForce;
// Calculate acceleration: a = F / m
float acceleration = netForce / Mass;
// Update velocity: v = v₀ + a·Δt
if (Velocity.Length > 0)
{
Vector2 direction = Velocity.Normalized();
float newSpeed = Velocity.Length + acceleration * deltaTime;
newSpeed = Math.Max(newSpeed, 0); // Don't go backwards without reverse gear
Velocity = direction * newSpeed;
}
else
{
// Starting from standstill
Velocity = new Vector2(acceleration * deltaTime, 0);
}
Position += Velocity * deltaTime;
// Sync wheel speed with actual vehicle speed (with slip allowance)
float currentWheelSpeed = Velocity.Length;
WheelSystem.SetSpeed(currentWheelSpeed);
}
private float CalculateTractiveForce()
{
// 1. Get the torque available at the wheels
float wheelTorque = Drivetrain.ClutchTorque * Drivetrain.Efficiency;
// 2. Convert torque to theoretical force: F = τ / r
float theoreticalForce = wheelTorque / WheelSystem.Radius;
// 3. Account for weight distribution and driven wheels
// Normal load on driven wheels = (DrivenWheels / WheelCount) * Weight
float drivenWheelNormalLoad = (DrivenWheels / (float)WheelCount) * Mass * 9.81f;
// 4. Calculate maximum tractive force based on friction (tire grip)
float frictionCoefficient = 1.2f; // Typical tire on dry asphalt
float maxTractiveForce = drivenWheelNormalLoad * frictionCoefficient;
// 5. Limit the force by what the tires can actually grip
// Also handle direction (forward/reverse)
if (theoreticalForce > 0)
{
return Math.Min(theoreticalForce, maxTractiveForce);
}
else
{
// For reverse or engine braking
return Math.Max(theoreticalForce, -maxTractiveForce);
}
}
private void ApplyBraking(float deltaTime)
{
if (BrakeInput <= 0) return;
float brakeTorque = BrakeInput * 500f; // 500 Nm max brake torque
WheelSystem.ApplyTorque(-brakeTorque, deltaTime);
}
public float CalculateTotalResistanceForce()
{
float dragForce = CalculateDragForce();
float rollingForce = CalculateRollingResistanceForce();
return dragForce + rollingForce;
}
private float CalculateDragForce()
{
// F_drag = 0.5 * ρ * Cd * A * v²
float speed = Speed;
return 0.5f * AirDensity * DragCoefficient * FrontalArea * speed * speed;
}
private float CalculateRollingResistanceForce()
{
// F_rolling = C_r * m * g
return RollingResistanceCoefficient * Mass * 9.81f;
}
// Convert resistance force to wheel torque
public float CalculateResistanceTorque()
{
float totalForce = CalculateTotalResistanceForce();
return totalForce * WheelSystem.Radius;
}
public void DisplayUpdate()
{
Console.SetCursorPosition(0, 0);
Console.WriteLine($"Engine Energy: {Engine.FlywheelEnergy,7:F0} J");
Console.WriteLine($"Engine Torque: {Engine.GetTorqueOutput(),7:F0} Nm");
Console.WriteLine($"Engine RPM: {Engine.RPM,7:F0}");
Console.WriteLine($"Wheel Energy: {WheelSystem.WheelEnergy,7:F0} J");
Console.WriteLine($"Wheel RPM: {WheelSystem.RPM,7:F0}");
Console.WriteLine($"Vehicle: {Speed * 3.6f,7:F1} km/h");
Console.WriteLine($"Throttle: {Engine.GetActualThrottle() * 100,6:F1}%");
Console.WriteLine($"Power: {Engine.CurrentPower / 1000,6:F1} kW");
Console.WriteLine($"Transmitted: {Drivetrain.TransmittedPower / 1000,6:F1} kW");
Console.WriteLine($"Brake: {BrakeInput * 100,6:F1}%");
Console.WriteLine($"Clutch: {ClutchInput * 100,6:F1}% disengaged");
Console.WriteLine($"Speed Diff: {Drivetrain.GetSpeedDifferenceRPM(),6:F0} RPM");
Console.WriteLine($"Clutch T: {Drivetrain.ClutchTorque,6:F0} Nm");
Console.WriteLine($"Resistance: {CalculateTotalResistanceForce(),6:F1} N");
Console.WriteLine($"Drag: {CalculateDragForce(),6:F1} N");
Console.WriteLine($"Rolling: {CalculateRollingResistanceForce(),6:F1} N");
Console.WriteLine($"Gear: {Drivetrain.GetCurrentGearName(),3} (Ratio: {Drivetrain.GearRatio:F2}:1)");
}
}
}