diff --git a/Car simulation/Car simulation.csproj b/Car simulation/Car simulation.csproj index 027dfcc..92d9b96 100644 --- a/Car simulation/Car simulation.csproj +++ b/Car simulation/Car simulation.csproj @@ -1,7 +1,7 @@  - Exe + WinExe net10.0 Car_simulation enable @@ -12,4 +12,10 @@ + + + PreserveNewest + + + diff --git a/Car simulation/Car.cs b/Car simulation/Car.cs index 3f9ae6f..fd8a0a6 100644 --- a/Car simulation/Car.cs +++ b/Car simulation/Car.cs @@ -48,6 +48,33 @@ namespace Car_simulation InitializeAudio(); } + public List GetDisplayData() + { + return new List + { + $"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)" + }; + } + private void InitializeAudio() { try @@ -120,40 +147,15 @@ namespace Car_simulation return dragForce + rollingForce; } - private float CalculateDragForce() + public float CalculateDragForce() { float speed = Speed; return 0.5f * AirDensity * DragCoefficient * FrontalArea * speed * speed; } - private float CalculateRollingResistanceForce() + public float CalculateRollingResistanceForce() { return RollingResistanceCoefficient * Mass * 9.81f; } - - 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($"Total Energy: {WheelSystem.TotalEnergy,7:F0} J"); - Console.WriteLine($" (Wheel Rot: {WheelSystem.GetRotationalEnergy(),7:F0} J)"); - Console.WriteLine($" (Car Trans: {WheelSystem.GetTranslationalEnergy(),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($"Speed Diff: {Drivetrain.GetSpeedDifferenceRPM(),6:F0} RPM"); - Console.WriteLine($"Clutch: {ClutchInput * 100,6:F1}% disengaged"); - Console.WriteLine($"Clutch T: {Drivetrain.ClutchTorque,6:F0} Nm"); - Console.WriteLine($"Clutch Slip: {Drivetrain.GetClutchSlipPercent(),6:F1}%"); - 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)"); - } } } \ No newline at end of file diff --git a/Car simulation/EngineSound.cs b/Car simulation/EngineSound.cs index bd8a66b..eace81c 100644 --- a/Car simulation/EngineSound.cs +++ b/Car simulation/EngineSound.cs @@ -9,7 +9,7 @@ namespace Car_simulation // Audio properties - smaller buffer for less latency private const uint SAMPLE_RATE = 44100; private const ushort CHANNEL_COUNT = 2; // Stereo - private const float BUFFER_DURATION = 0.01f; // 10ms instead of 50ms! + private const float BUFFER_DURATION = 0.05f; // 10ms instead of 50ms! // Engine sound properties - NO SMOOTHING for instant response private volatile float _currentRPM = 800f; // volatile for thread safety @@ -44,8 +44,6 @@ namespace Car_simulation { _harmonicPhases[i] = (float)(_random.NextDouble() * 2 * Math.PI); } - - Console.WriteLine($"EngineSound initialized: {BUFFER_DURATION * 1000:F0}ms buffer, {CylinderCount} cylinders"); } // CALL THIS FROM YOUR PHYSICS THREAD - INSTANT UPDATE diff --git a/Car simulation/Program.cs b/Car simulation/Program.cs index 733ee76..51aaa54 100644 --- a/Car simulation/Program.cs +++ b/Car simulation/Program.cs @@ -2,7 +2,8 @@ using SFML.Window; using SFML.Graphics; using SFML.System; -using System.Diagnostics; +using System.Collections.Generic; +using System; internal class Program { @@ -10,6 +11,18 @@ internal class Program private bool _isRunning = true; private RenderWindow _window; + private Font _font; + private List _displayTexts = new List(); + private RectangleShape _tachometerBackground; + private RectangleShape _tachometerNeedle; + private RectangleShape _speedometerBackground; + private RectangleShape _speedometerNeedle; + + // Colors + private Color _backgroundColor = new Color(20, 20, 30); + private Color _textColor = new Color(220, 220, 220); + private Color _highlightColor = new Color(0, 150, 255); + private Color _warningColor = new Color(255, 100, 100); // Timing for physics private Clock _clock = new Clock(); @@ -28,7 +41,7 @@ internal class Program private void Run() { - _window = new RenderWindow(new VideoMode(800, 600), "Car Simulation", Styles.Default); + _window = new RenderWindow(new VideoMode(1000, 800), "Car Simulation", Styles.Default); _window.SetVisible(true); _window.SetFramerateLimit(60); _window.SetKeyRepeatEnabled(false); @@ -37,6 +50,17 @@ internal class Program _window.KeyPressed += OnKeyPressed; _window.KeyReleased += OnKeyReleased; + // Load font + try + { + _font = new Font("arial.ttf"); + } + catch + { + _font = new Font("C:/Windows/Fonts/arial.ttf"); + } + + InitializeDisplay(); InitializeTrackedKeys(); _clock.Restart(); @@ -54,14 +78,10 @@ internal class Program car.Update(_timePerUpdate.AsSeconds()); _accumulatedTime -= _timePerUpdate; _updateCount++; - - if (_accumulatedTime >= Time.FromSeconds(0.2f)) - { - _accumulatedTime = _timePerUpdate; - } } UpdateDisplay(); + _window.Display(); UpdatePreviousKeyStates(); } @@ -69,111 +89,238 @@ internal class Program Console.WriteLine($"\nSimulation stopped after {_updateCount} updates"); } + private void InitializeDisplay() + { + // Initialize display texts + for (int i = 0; i < 30; i++) + { + Text text = new Text("", _font, 16); + text.FillColor = _textColor; + text.Position = new Vector2f(20, 20 + i * 24); + _displayTexts.Add(text); + } + + // Tachometer + _tachometerBackground = new RectangleShape(new Vector2f(200, 200)); + _tachometerBackground.Position = new Vector2f(700, 50); + _tachometerBackground.FillColor = new Color(40, 40, 50); + _tachometerBackground.OutlineThickness = 2; + _tachometerBackground.OutlineColor = Color.White; + + _tachometerNeedle = new RectangleShape(new Vector2f(80, 4)); + _tachometerNeedle.Position = new Vector2f(800, 150); + _tachometerNeedle.FillColor = Color.Red; + _tachometerNeedle.Origin = new Vector2f(70, 2); + + // Speedometer + _speedometerBackground = new RectangleShape(new Vector2f(200, 200)); + _speedometerBackground.Position = new Vector2f(700, 300); + _speedometerBackground.FillColor = new Color(40, 40, 50); + _speedometerBackground.OutlineThickness = 2; + _speedometerBackground.OutlineColor = Color.White; + + _speedometerNeedle = new RectangleShape(new Vector2f(80, 4)); + _speedometerNeedle.Position = new Vector2f(800, 400); + _speedometerNeedle.FillColor = Color.Green; + _speedometerNeedle.Origin = new Vector2f(70, 2); + } + + private void UpdateDisplay() + { + _window.Clear(_backgroundColor); + + UpdateDisplayTexts(); + + foreach (var text in _displayTexts) + { + if (!string.IsNullOrEmpty(text.DisplayedString)) + _window.Draw(text); + } + + DrawGauges(); + DrawKeyBindings(); + } + + private void UpdateDisplayTexts() + { + // Clear all text + for (int i = 0; i < _displayTexts.Count; i++) + { + _displayTexts[i].DisplayedString = ""; + _displayTexts[i].FillColor = _textColor; + } + + // Update text - using safe indexing + int line = 0; + + if (line < _displayTexts.Count) _displayTexts[line++].DisplayedString = "ENGINE"; + if (line < _displayTexts.Count) _displayTexts[line++].DisplayedString = $" RPM: {car.Engine.RPM,7:F0}"; + if (line < _displayTexts.Count) _displayTexts[line++].DisplayedString = $" Torque: {car.Engine.GetTorqueOutput(),7:F0} Nm"; + if (line < _displayTexts.Count) _displayTexts[line++].DisplayedString = $" Throttle: {car.Engine.GetActualThrottle() * 100,6:F1}%"; + if (line < _displayTexts.Count) _displayTexts[line++].DisplayedString = $" Power: {car.Engine.CurrentPower / 1000,6:F1} kW"; + + if (line < _displayTexts.Count) + { + _displayTexts[line].DisplayedString = $" Status: {(car.Engine.IsRunning ? "RUNNING" : "STALLED")}"; + _displayTexts[line].FillColor = car.Engine.IsRunning ? _textColor : _warningColor; + line++; + } + + line++; // Blank line + + if (line < _displayTexts.Count) _displayTexts[line++].DisplayedString = "DRIVETRAIN"; + if (line < _displayTexts.Count) _displayTexts[line++].DisplayedString = $" Gear: {car.Drivetrain.GetCurrentGearName(),3} (Ratio: {car.Drivetrain.GearRatio:F2}:1)"; + if (line < _displayTexts.Count) _displayTexts[line++].DisplayedString = $" Clutch: {car.ClutchInput * 100,6:F1}% disengaged"; + if (line < _displayTexts.Count) _displayTexts[line++].DisplayedString = $" Clutch Torque: {car.Drivetrain.ClutchTorque,6:F0} Nm"; + + if (line < _displayTexts.Count) + { + _displayTexts[line].DisplayedString = $" Clutch Slip: {car.Drivetrain.GetClutchSlipPercent(),6:F1}%"; + _displayTexts[line].FillColor = car.Drivetrain.GetClutchSlipPercent() > 50 ? _warningColor : _textColor; + line++; + } + + if (line < _displayTexts.Count) _displayTexts[line++].DisplayedString = $" Transmitted: {car.Drivetrain.TransmittedPower / 1000,6:F1} kW"; + if (line < _displayTexts.Count) _displayTexts[line++].DisplayedString = $" Speed Diff: {car.Drivetrain.GetSpeedDifferenceRPM(),6:F0} RPM"; + + line++; // Blank line + + if (line < _displayTexts.Count) _displayTexts[line++].DisplayedString = "VEHICLE"; + if (line < _displayTexts.Count) _displayTexts[line++].DisplayedString = $" Speed: {car.Speed * 3.6f,7:F1} km/h"; + if (line < _displayTexts.Count) _displayTexts[line++].DisplayedString = $" Wheel RPM: {car.WheelSystem.RPM,7:F0}"; + if (line < _displayTexts.Count) _displayTexts[line++].DisplayedString = $" Brake: {car.BrakeInput * 100,6:F1}%"; + + line++; // Blank line + + if (line < _displayTexts.Count) _displayTexts[line++].DisplayedString = "FORCES"; + if (line < _displayTexts.Count) _displayTexts[line++].DisplayedString = $" Total Resistance: {car.CalculateTotalResistanceForce(),6:F1} N"; + if (line < _displayTexts.Count) _displayTexts[line++].DisplayedString = $" Drag: {car.CalculateDragForce(),6:F1} N"; + if (line < _displayTexts.Count) _displayTexts[line++].DisplayedString = $" Rolling: {car.CalculateRollingResistanceForce(),6:F1} N"; + + line++; // Blank line + + if (line < _displayTexts.Count) _displayTexts[line++].DisplayedString = "ENERGY"; + if (line < _displayTexts.Count) _displayTexts[line++].DisplayedString = $" Engine: {car.Engine.FlywheelEnergy,7:F0} J"; + if (line < _displayTexts.Count) _displayTexts[line++].DisplayedString = $" Total: {car.WheelSystem.TotalEnergy,7:F0} J"; + if (line < _displayTexts.Count) _displayTexts[line++].DisplayedString = $" Wheel Rotation: {car.WheelSystem.GetRotationalEnergy(),7:F0} J"; + if (line < _displayTexts.Count) _displayTexts[line++].DisplayedString = $" Car Translation: {car.WheelSystem.GetTranslationalEnergy(),7:F0} J"; + } + + private void DrawGauges() + { + _window.Draw(_tachometerBackground); + + float rpmRatio = Math.Clamp(car.Engine.RPM / 8000f, 0f, 1f); + float tachometerAngle = -90 + (270 * rpmRatio); + _tachometerNeedle.Rotation = tachometerAngle; + _window.Draw(_tachometerNeedle); + + Text tachLabel = new Text("RPM", _font, 20); + tachLabel.FillColor = Color.White; + tachLabel.Position = new Vector2f(770, 70); + _window.Draw(tachLabel); + + Text rpmText = new Text($"{car.Engine.RPM:F0}", _font, 24); + rpmText.FillColor = car.Engine.RPM > 7000 ? _warningColor : Color.White; + rpmText.Position = new Vector2f(765, 100); + _window.Draw(rpmText); + + _window.Draw(_speedometerBackground); + + float speedRatio = Math.Clamp(car.Speed * 3.6f / 200f, 0f, 1f); + float speedometerAngle = -90 + (270 * speedRatio); + _speedometerNeedle.Rotation = speedometerAngle; + _window.Draw(_speedometerNeedle); + + Text speedLabel = new Text("SPEED", _font, 20); + speedLabel.FillColor = Color.White; + speedLabel.Position = new Vector2f(770, 320); + _window.Draw(speedLabel); + + Text speedText = new Text($"{car.Speed * 3.6f:F1} km/h", _font, 24); + speedText.FillColor = Color.White; + speedText.Position = new Vector2f(750, 350); + _window.Draw(speedText); + + Text gearText = new Text($"GEAR {car.Drivetrain.GetCurrentGearName()}", _font, 28); + gearText.FillColor = _highlightColor; + gearText.Position = new Vector2f(750, 520); + _window.Draw(gearText); + } + + private void DrawKeyBindings() + { + Text controls = new Text("CONTROLS\n\nW/S: Throttle/Brake\nUp/Down: Clutch\nLeft/Right: Gear Up/Down\nSpace: Toggle Force Clutch\nESC: Quit", _font, 14); + controls.FillColor = new Color(180, 180, 180); + controls.Position = new Vector2f(250, 625); + _window.Draw(controls); + } + private void InitializeTrackedKeys() { - // Initialize all keys we care about - var keysToTrack = new Keyboard.Key[] - { - Keyboard.Key.W, - Keyboard.Key.Up, - Keyboard.Key.Down, - Keyboard.Key.B, - Keyboard.Key.Space, - Keyboard.Key.Left, - Keyboard.Key.Right, - Keyboard.Key.Escape + var keys = new[] { + Keyboard.Key.W, Keyboard.Key.S, + Keyboard.Key.Down, Keyboard.Key.Up, Keyboard.Key.Space, + Keyboard.Key.Escape, + Keyboard.Key.Right, Keyboard.Key.Left }; - foreach (var key in keysToTrack) + foreach (var key in keys) { - _currentKeyStates[key] = false; _previousKeyStates[key] = false; + _currentKeyStates[key] = false; } } private void OnKeyPressed(object sender, KeyEventArgs e) { - var key = e.Code; - - // Update current state - if (_currentKeyStates.ContainsKey(key)) - { - _currentKeyStates[key] = true; - } + if (_currentKeyStates.ContainsKey(e.Code)) + _currentKeyStates[e.Code] = true; } private void OnKeyReleased(object sender, KeyEventArgs e) { - var key = e.Code; - - // Update current state - if (_currentKeyStates.ContainsKey(key)) - { - _currentKeyStates[key] = false; - } + if (_currentKeyStates.ContainsKey(e.Code)) + _currentKeyStates[e.Code] = false; } private void ProcessInput(float deltaTime) { - // quit - if (IsKeyDown(Keyboard.Key.Escape)) - { - _isRunning = false; - return; - } - - // force clutch - car.ForceClutch = (IsKeyDown(Keyboard.Key.Space)); - - // throttle - if (IsKeyDown(Keyboard.Key.W)) - { - car.ThrottleInput = Math.Min(car.ThrottleInput + 2f * deltaTime, 1.0f); - } + // Throttle/Brake + if (_currentKeyStates[Keyboard.Key.W]) + car.ThrottleInput += deltaTime * 4f; else - { - car.ThrottleInput = Math.Max(car.ThrottleInput - 10f * deltaTime, 0f); - } + car.ThrottleInput -= deltaTime * 8f; - // brake - if (IsKeyDown(Keyboard.Key.B)) - { - car.BrakeInput = Math.Min(car.BrakeInput + 1f * deltaTime, 1.0f); - } + if (_currentKeyStates[Keyboard.Key.S]) + car.BrakeInput += deltaTime * 4f; else - { - car.BrakeInput = Math.Max(car.BrakeInput - 4f * deltaTime, 0f); - } + car.BrakeInput -= deltaTime * 8f; - // clutch - if (IsKeyDown(Keyboard.Key.Up)) - { - car.ClutchInput = Math.Min(car.ClutchInput + 0.1f * deltaTime, 1.0f); - } - else if (IsKeyDown(Keyboard.Key.Down)) - { - car.ClutchInput = Math.Max(car.ClutchInput - 0.1f * deltaTime, 0f); - } + car.ThrottleInput = Math.Clamp(car.ThrottleInput, 0f, 1f); + car.BrakeInput = Math.Clamp(car.BrakeInput, 0f, 1f); - // clutch - if (IsKeyDown(Keyboard.Key.Up)) - { - car.ClutchInput = Math.Min(car.ClutchInput + 1f * deltaTime, 1.0f); - } - else if (IsKeyDown(Keyboard.Key.Down)) - { - car.ClutchInput = Math.Max(car.ClutchInput - 1f * deltaTime, 0f); - } + // Clutch + if (_currentKeyStates[Keyboard.Key.Up]) + car.ClutchInput += deltaTime * 0.5f; + if(_currentKeyStates[Keyboard.Key.Down]) + car.ClutchInput -= deltaTime * 0.5f; - // gear - if (WasKeyPressed(Keyboard.Key.Left)) - { - car.Drivetrain.GearDown(); - } - else if (WasKeyPressed(Keyboard.Key.Right)) - { + car.ClutchInput = Math.Clamp(car.ClutchInput, 0f, 1f); + + // Toggle force clutch + if (_currentKeyStates[Keyboard.Key.Space] && !_previousKeyStates[Keyboard.Key.Space]) + car.ForceClutch = !car.ForceClutch; + + // Gear changes + if (_currentKeyStates[Keyboard.Key.Right] && !_previousKeyStates[Keyboard.Key.Right]) car.Drivetrain.GearUp(); - } + if (_currentKeyStates[Keyboard.Key.Left] && !_previousKeyStates[Keyboard.Key.Left]) + car.Drivetrain.GearDown(); + + // Quit + if (_currentKeyStates[Keyboard.Key.Escape]) + _isRunning = false; } private void UpdatePreviousKeyStates() @@ -181,31 +328,8 @@ internal class Program var keys = new List(_currentKeyStates.Keys); foreach (var key in keys) { - _previousKeyStates[key] = _currentKeyStates[key]; + if (_previousKeyStates.ContainsKey(key)) + _previousKeyStates[key] = _currentKeyStates[key]; } } - - private bool IsKeyDown(Keyboard.Key key) - { - return _currentKeyStates.ContainsKey(key) && _currentKeyStates[key]; - } - - private bool WasKeyPressed(Keyboard.Key key) - { - return IsKeyDown(key) && - (!_previousKeyStates.ContainsKey(key) || !_previousKeyStates[key]); - } - - private void UpdateDisplay() - { - _window.Clear(Color.Black); - - // Render car or simulation visualization here - // For example, if car has Draw() method: - // car.Draw(_window); - - car.DisplayUpdate(); // If this updates console display - - _window.Display(); - } } \ No newline at end of file diff --git a/Car simulation/arial.ttf b/Car simulation/arial.ttf new file mode 100644 index 0000000..7ff88f2 Binary files /dev/null and b/Car simulation/arial.ttf differ