major rework
This commit is contained in:
@@ -1,337 +1,157 @@
|
||||
using Car_simulation;
|
||||
using SFML.Window;
|
||||
using Car_simulation.Core.Models;
|
||||
using Car_simulation.Input;
|
||||
using Car_simulation.UI;
|
||||
using SFML.Graphics;
|
||||
using SFML.System;
|
||||
using System.Collections.Generic;
|
||||
using System;
|
||||
using SFML.Window;
|
||||
|
||||
internal class Program
|
||||
namespace Car_simulation
|
||||
{
|
||||
Car car = new Car();
|
||||
private bool _isRunning = true;
|
||||
|
||||
private RenderWindow _window;
|
||||
private Font _font;
|
||||
private List<Text> _displayTexts = new List<Text>();
|
||||
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();
|
||||
private Time _timePerUpdate = Time.FromSeconds(1.0f / 60.0f); // 60 FPS physics
|
||||
private Time _accumulatedTime = Time.Zero;
|
||||
private float _totalTime = 0.0f;
|
||||
private long _updateCount = 0;
|
||||
|
||||
private Dictionary<Keyboard.Key, bool> _previousKeyStates = new Dictionary<Keyboard.Key, bool>();
|
||||
private Dictionary<Keyboard.Key, bool> _currentKeyStates = new Dictionary<Keyboard.Key, bool>();
|
||||
|
||||
private static void Main(string[] args)
|
||||
internal class Program
|
||||
{
|
||||
Program program = new Program();
|
||||
program.Run();
|
||||
}
|
||||
private Car _car = new Car();
|
||||
private RenderWindow _window;
|
||||
private Font _font;
|
||||
private DisplayManager _displayManager;
|
||||
private CarInputHandler _inputHandler;
|
||||
|
||||
private void Run()
|
||||
{
|
||||
_window = new RenderWindow(new VideoMode(1000, 800), "Car Simulation", Styles.Default);
|
||||
_window.SetVisible(true);
|
||||
_window.SetFramerateLimit(60);
|
||||
_window.SetKeyRepeatEnabled(false);
|
||||
// Timing
|
||||
private Clock _clock = new Clock();
|
||||
private Time _timePerUpdate = Time.FromSeconds(1.0f / 60.0f);
|
||||
private Time _accumulatedTime = Time.Zero;
|
||||
private float _totalTime = 0.0f;
|
||||
private long _updateCount = 0;
|
||||
|
||||
_window.Closed += (sender, e) => _isRunning = false;
|
||||
_window.KeyPressed += OnKeyPressed;
|
||||
_window.KeyReleased += OnKeyReleased;
|
||||
|
||||
// Load font
|
||||
try
|
||||
static void Main(string[] args)
|
||||
{
|
||||
_font = new Font("arial.ttf");
|
||||
}
|
||||
catch
|
||||
{
|
||||
_font = new Font("C:/Windows/Fonts/arial.ttf");
|
||||
Program program = new Program();
|
||||
program.Run();
|
||||
}
|
||||
|
||||
InitializeDisplay();
|
||||
InitializeTrackedKeys();
|
||||
|
||||
_clock.Restart();
|
||||
|
||||
while (_isRunning && _window.IsOpen)
|
||||
private void Run()
|
||||
{
|
||||
_window.DispatchEvents();
|
||||
InitializeWindow();
|
||||
InitializeUI();
|
||||
InitializeInput();
|
||||
|
||||
Time elapsed = _clock.Restart();
|
||||
_accumulatedTime += elapsed;
|
||||
|
||||
while (_accumulatedTime >= _timePerUpdate)
|
||||
while (_window.IsOpen)
|
||||
{
|
||||
ProcessInput(_timePerUpdate.AsSeconds());
|
||||
_totalTime += _timePerUpdate.AsSeconds();
|
||||
car.Update(_timePerUpdate.AsSeconds(), _totalTime);
|
||||
_accumulatedTime -= _timePerUpdate;
|
||||
_updateCount++;
|
||||
_window.DispatchEvents();
|
||||
|
||||
Time elapsed = _clock.Restart();
|
||||
_accumulatedTime += elapsed;
|
||||
|
||||
while (_accumulatedTime >= _timePerUpdate)
|
||||
{
|
||||
Update(_timePerUpdate.AsSeconds());
|
||||
_accumulatedTime -= _timePerUpdate;
|
||||
}
|
||||
|
||||
Render();
|
||||
}
|
||||
|
||||
UpdateDisplay();
|
||||
_window.Close();
|
||||
Console.WriteLine($"\nSimulation stopped after {_updateCount} updates");
|
||||
}
|
||||
|
||||
private void InitializeWindow()
|
||||
{
|
||||
_window = new RenderWindow(new VideoMode(1000, 800), "Car Simulation", Styles.Default);
|
||||
_window.SetVisible(true);
|
||||
_window.SetFramerateLimit(60);
|
||||
_window.SetKeyRepeatEnabled(false);
|
||||
|
||||
_window.Closed += (sender, e) => _window.Close();
|
||||
_window.Resized += OnWindowResized;
|
||||
_window.KeyPressed += OnKeyPressed;
|
||||
}
|
||||
|
||||
private void OnWindowResized(object sender, SizeEventArgs e)
|
||||
{
|
||||
_window.SetView(new View(new FloatRect(0, 0, e.Width, e.Height)));
|
||||
_displayManager?.HandleResize(e.Width, e.Height);
|
||||
}
|
||||
|
||||
private void OnKeyPressed(object sender, KeyEventArgs e)
|
||||
{
|
||||
// Toggle debug info with F3
|
||||
if (e.Code == Keyboard.Key.F3)
|
||||
{
|
||||
_displayManager?.ToggleDebugInfo();
|
||||
}
|
||||
|
||||
// Pass key event to input handler
|
||||
_inputHandler?.OnKeyPressed(e);
|
||||
}
|
||||
|
||||
private void InitializeUI()
|
||||
{
|
||||
try
|
||||
{
|
||||
_font = new Font("arial.ttf");
|
||||
}
|
||||
catch
|
||||
{
|
||||
_font = new Font("C:/Windows/Fonts/arial.ttf");
|
||||
}
|
||||
|
||||
_displayManager = new DisplayManager(_window, _font);
|
||||
}
|
||||
|
||||
private void InitializeInput()
|
||||
{
|
||||
_inputHandler = new CarInputHandler();
|
||||
_window.KeyPressed += (sender, e) => _inputHandler.OnKeyPressed(e);
|
||||
_window.KeyReleased += (sender, e) => _inputHandler.OnKeyReleased(e);
|
||||
}
|
||||
|
||||
private void Update(float deltaTime)
|
||||
{
|
||||
_inputHandler.ProcessInput(deltaTime);
|
||||
_totalTime += deltaTime;
|
||||
|
||||
ApplyInputToCar(deltaTime);
|
||||
_car.Update(deltaTime, _totalTime);
|
||||
|
||||
// Update display with current car state
|
||||
_displayManager.Update(_car, deltaTime, _totalTime, _updateCount);
|
||||
|
||||
if (_inputHandler.InputState.Quit)
|
||||
_window.Close();
|
||||
|
||||
_updateCount++;
|
||||
}
|
||||
|
||||
private void ApplyInputToCar(float deltaTime)
|
||||
{
|
||||
var input = _inputHandler.InputState;
|
||||
|
||||
_car.ThrottleInput += input.ThrottleChange;
|
||||
_car.BrakeInput += input.BrakeChange;
|
||||
|
||||
_car.ThrottleInput = Math.Clamp(_car.ThrottleInput, 0f, 1f);
|
||||
_car.BrakeInput = Math.Clamp(_car.BrakeInput, 0f, 1f);
|
||||
|
||||
if (input.ClutchUp)
|
||||
_car.ClutchInput += deltaTime * 0.5f;
|
||||
if (input.ClutchDown)
|
||||
_car.ClutchInput -= deltaTime * 0.5f;
|
||||
|
||||
_car.ClutchInput = Math.Clamp(_car.ClutchInput, 0f, 1f);
|
||||
|
||||
if (input.ToggleForceClutch)
|
||||
_car.ForceClutch = !_car.ForceClutch;
|
||||
|
||||
if (input.GearUp)
|
||||
_car.Drivetrain.GearUp();
|
||||
if (input.GearDown)
|
||||
_car.Drivetrain.GearDown();
|
||||
}
|
||||
|
||||
private void Render()
|
||||
{
|
||||
_displayManager.Draw();
|
||||
_window.Display();
|
||||
UpdatePreviousKeyStates();
|
||||
}
|
||||
|
||||
_window.Close();
|
||||
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() / 1000,7:F0} KJ";
|
||||
if (line < _displayTexts.Count) _displayTexts[line++].DisplayedString = $" Car Translation: {car.WheelSystem.GetTranslationalEnergy() / 1000,7:F0} KJ";
|
||||
}
|
||||
|
||||
private void DrawGauges()
|
||||
{
|
||||
_window.Draw(_tachometerBackground);
|
||||
|
||||
float rpmRatio = Math.Clamp(car.Engine.RPM / 13000f, 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()
|
||||
{
|
||||
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 keys)
|
||||
{
|
||||
_previousKeyStates[key] = false;
|
||||
_currentKeyStates[key] = false;
|
||||
}
|
||||
}
|
||||
|
||||
private void OnKeyPressed(object sender, KeyEventArgs e)
|
||||
{
|
||||
if (_currentKeyStates.ContainsKey(e.Code))
|
||||
_currentKeyStates[e.Code] = true;
|
||||
}
|
||||
|
||||
private void OnKeyReleased(object sender, KeyEventArgs e)
|
||||
{
|
||||
if (_currentKeyStates.ContainsKey(e.Code))
|
||||
_currentKeyStates[e.Code] = false;
|
||||
}
|
||||
|
||||
private void ProcessInput(float deltaTime)
|
||||
{
|
||||
// Throttle/Brake
|
||||
if (_currentKeyStates[Keyboard.Key.W])
|
||||
car.ThrottleInput += deltaTime * 4f;
|
||||
else
|
||||
car.ThrottleInput -= deltaTime * 8f;
|
||||
|
||||
if (_currentKeyStates[Keyboard.Key.S])
|
||||
car.BrakeInput += deltaTime * 4f;
|
||||
else
|
||||
car.BrakeInput -= deltaTime * 8f;
|
||||
|
||||
car.ThrottleInput = Math.Clamp(car.ThrottleInput, 0f, 1f);
|
||||
car.BrakeInput = Math.Clamp(car.BrakeInput, 0f, 1f);
|
||||
|
||||
// Clutch
|
||||
if (_currentKeyStates[Keyboard.Key.Up])
|
||||
car.ClutchInput += deltaTime * 0.5f;
|
||||
if(_currentKeyStates[Keyboard.Key.Down])
|
||||
car.ClutchInput -= deltaTime * 0.5f;
|
||||
|
||||
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()
|
||||
{
|
||||
var keys = new List<Keyboard.Key>(_currentKeyStates.Keys);
|
||||
foreach (var key in keys)
|
||||
{
|
||||
if (_previousKeyStates.ContainsKey(key))
|
||||
_previousKeyStates[key] = _currentKeyStates[key];
|
||||
}
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user