major rework
This commit is contained in:
306
Car simulation/UI/DisplayManager.cs
Normal file
306
Car simulation/UI/DisplayManager.cs
Normal file
@@ -0,0 +1,306 @@
|
||||
using SFML.Graphics;
|
||||
using SFML.System;
|
||||
using Car_simulation.Core.Models;
|
||||
using Car_simulation.UI.Instruments;
|
||||
|
||||
namespace Car_simulation.UI
|
||||
{
|
||||
public class DisplayManager
|
||||
{
|
||||
private InstrumentPanel _instrumentPanel;
|
||||
private RenderWindow _window;
|
||||
private Font _font;
|
||||
|
||||
private Color _backgroundColor = new Color(20, 20, 30);
|
||||
private Text _fpsText;
|
||||
private Clock _fpsClock;
|
||||
private int _frameCount = 0;
|
||||
private float _fpsUpdateInterval = 0.5f;
|
||||
private float _fpsAccumulator = 0f;
|
||||
|
||||
// Debug information
|
||||
private bool _showDebugInfo = false;
|
||||
private RectangleShape _debugPanel;
|
||||
private List<Text> _debugTexts;
|
||||
|
||||
// Performance tracking
|
||||
private Queue<float> _frameTimes = new Queue<float>();
|
||||
private const int FRAME_TIME_HISTORY = 60;
|
||||
private Text _performanceText;
|
||||
|
||||
public DisplayManager(RenderWindow window, Font font)
|
||||
{
|
||||
_window = window;
|
||||
_font = font;
|
||||
|
||||
Initialize();
|
||||
}
|
||||
|
||||
private void Initialize()
|
||||
{
|
||||
_instrumentPanel = new InstrumentPanel(_font, new Vector2f(750, 50), new Vector2f(750, 275));
|
||||
|
||||
// FPS counter
|
||||
_fpsText = new Text("FPS: 0", _font, 12);
|
||||
_fpsText.FillColor = new Color(180, 180, 180);
|
||||
_fpsText.Position = new Vector2f(10, _window.Size.Y - 25);
|
||||
|
||||
_fpsClock = new Clock();
|
||||
|
||||
// Performance text
|
||||
_performanceText = new Text("", _font, 12);
|
||||
_performanceText.FillColor = new Color(180, 180, 220);
|
||||
_performanceText.Position = new Vector2f(_window.Size.X - 200, _window.Size.Y - 25);
|
||||
|
||||
// Debug panel
|
||||
InitializeDebugPanel();
|
||||
}
|
||||
|
||||
private void InitializeDebugPanel()
|
||||
{
|
||||
_debugPanel = new RectangleShape(new Vector2f(300, 200));
|
||||
_debugPanel.Position = new Vector2f(_window.Size.X - 320, 20);
|
||||
_debugPanel.FillColor = new Color(0, 0, 0, 200);
|
||||
_debugPanel.OutlineThickness = 1;
|
||||
_debugPanel.OutlineColor = Color.Cyan;
|
||||
|
||||
_debugTexts = new List<Text>();
|
||||
for (int i = 0; i < 10; i++)
|
||||
{
|
||||
Text text = new Text("", _font, 12);
|
||||
text.FillColor = Color.Cyan;
|
||||
text.Position = new Vector2f(_window.Size.X - 310, 30 + i * 18);
|
||||
_debugTexts.Add(text);
|
||||
}
|
||||
}
|
||||
|
||||
public void Update(Car car, float deltaTime, float totalTime, long updateCount)
|
||||
{
|
||||
UpdateFPS(deltaTime);
|
||||
UpdatePerformanceInfo(deltaTime);
|
||||
|
||||
_instrumentPanel.Update(car);
|
||||
|
||||
if (_showDebugInfo)
|
||||
{
|
||||
UpdateDebugInfo(car, deltaTime, totalTime, updateCount);
|
||||
}
|
||||
}
|
||||
|
||||
private void UpdateFPS(float deltaTime)
|
||||
{
|
||||
_frameCount++;
|
||||
_fpsAccumulator += deltaTime;
|
||||
|
||||
if (_fpsAccumulator >= _fpsUpdateInterval)
|
||||
{
|
||||
float fps = _frameCount / _fpsAccumulator;
|
||||
_fpsText.DisplayedString = $"FPS: {fps:F1}";
|
||||
|
||||
_frameCount = 0;
|
||||
_fpsAccumulator = 0f;
|
||||
}
|
||||
}
|
||||
|
||||
private void UpdatePerformanceInfo(float deltaTime)
|
||||
{
|
||||
// Track frame times for smoothing
|
||||
_frameTimes.Enqueue(deltaTime * 1000); // Convert to milliseconds
|
||||
if (_frameTimes.Count > FRAME_TIME_HISTORY)
|
||||
{
|
||||
_frameTimes.Dequeue();
|
||||
}
|
||||
|
||||
// Calculate average frame time
|
||||
float avgFrameTime = 0;
|
||||
foreach (var time in _frameTimes)
|
||||
{
|
||||
avgFrameTime += time;
|
||||
}
|
||||
avgFrameTime /= _frameTimes.Count;
|
||||
|
||||
_performanceText.DisplayedString = $"Frame: {deltaTime * 1000:F1}ms (Avg: {avgFrameTime:F1}ms)";
|
||||
}
|
||||
|
||||
private void UpdateDebugInfo(Car car, float deltaTime, float totalTime, long updateCount)
|
||||
{
|
||||
int line = 0;
|
||||
|
||||
if (line < _debugTexts.Count)
|
||||
_debugTexts[line++].DisplayedString = $"Time: {totalTime:F2}s";
|
||||
|
||||
if (line < _debugTexts.Count)
|
||||
_debugTexts[line++].DisplayedString = $"Updates: {updateCount}";
|
||||
|
||||
if (line < _debugTexts.Count)
|
||||
_debugTexts[line++].DisplayedString = $"Delta: {deltaTime:F4}s";
|
||||
|
||||
if (line < _debugTexts.Count)
|
||||
_debugTexts[line++].DisplayedString = $"Position: ({car.Position.X:F1}, {car.Position.Y:F1})";
|
||||
|
||||
if (line < _debugTexts.Count)
|
||||
_debugTexts[line++].DisplayedString = $"Velocity: {car.Velocity.Length:F2} m/s";
|
||||
|
||||
if (line < _debugTexts.Count)
|
||||
_debugTexts[line++].DisplayedString = $"Engine Omega: {car.Engine.AngularVelocity:F2} rad/s";
|
||||
|
||||
if (line < _debugTexts.Count)
|
||||
_debugTexts[line++].DisplayedString = $"Wheel Omega: {car.WheelSystem.AngularVelocity:F2} rad/s";
|
||||
|
||||
if (line < _debugTexts.Count)
|
||||
_debugTexts[line++].DisplayedString = $"Clutch Eng: {car.Drivetrain.ClutchEngagement:P0}";
|
||||
|
||||
if (line < _debugTexts.Count)
|
||||
_debugTexts[line++].DisplayedString = $"Force Clutch: {car.ForceClutch}";
|
||||
}
|
||||
|
||||
public void Draw()
|
||||
{
|
||||
_window.Clear(_backgroundColor);
|
||||
|
||||
_instrumentPanel.Draw(_window);
|
||||
|
||||
// Draw FPS counter
|
||||
_window.Draw(_fpsText);
|
||||
|
||||
// Draw performance info
|
||||
_window.Draw(_performanceText);
|
||||
|
||||
// Draw debug panel if enabled
|
||||
if (_showDebugInfo)
|
||||
{
|
||||
_window.Draw(_debugPanel);
|
||||
foreach (var text in _debugTexts)
|
||||
{
|
||||
if (!string.IsNullOrEmpty(text.DisplayedString))
|
||||
_window.Draw(text);
|
||||
}
|
||||
}
|
||||
|
||||
// Draw version info
|
||||
DrawVersionInfo();
|
||||
}
|
||||
|
||||
private void DrawVersionInfo()
|
||||
{
|
||||
Text versionText = new Text("Car Simulation v1.0", _font, 10);
|
||||
versionText.FillColor = new Color(100, 100, 100);
|
||||
versionText.Position = new Vector2f(_window.Size.X - 120, _window.Size.Y - 15);
|
||||
_window.Draw(versionText);
|
||||
}
|
||||
|
||||
public void ToggleDebugInfo()
|
||||
{
|
||||
_showDebugInfo = !_showDebugInfo;
|
||||
}
|
||||
|
||||
public bool ShowDebugInfo => _showDebugInfo;
|
||||
|
||||
public void HandleResize(uint width, uint height)
|
||||
{
|
||||
// Update FPS text position
|
||||
_fpsText.Position = new Vector2f(10, height - 25);
|
||||
|
||||
// Update performance text position
|
||||
_performanceText.Position = new Vector2f(width - 200, height - 25);
|
||||
|
||||
// Update debug panel position
|
||||
_debugPanel.Position = new Vector2f(width - 320, 20);
|
||||
|
||||
// Update debug text positions
|
||||
for (int i = 0; i < _debugTexts.Count; i++)
|
||||
{
|
||||
_debugTexts[i].Position = new Vector2f(width - 310, 30 + i * 18);
|
||||
}
|
||||
|
||||
// You might want to reposition the instrument panel too
|
||||
// For now, the instrument panel is fixed, but you could add resize handling
|
||||
}
|
||||
|
||||
public void DrawCustomMessage(string message, Vector2f position, uint fontSize, Color color)
|
||||
{
|
||||
Text customText = new Text(message, _font, fontSize);
|
||||
customText.FillColor = color;
|
||||
customText.Position = position;
|
||||
_window.Draw(customText);
|
||||
}
|
||||
|
||||
public void DrawProgressBar(Vector2f position, Vector2f size, float progress, Color fillColor, Color bgColor)
|
||||
{
|
||||
// Draw background
|
||||
RectangleShape background = new RectangleShape(size);
|
||||
background.Position = position;
|
||||
background.FillColor = bgColor;
|
||||
background.OutlineThickness = 1;
|
||||
background.OutlineColor = Color.White;
|
||||
_window.Draw(background);
|
||||
|
||||
// Draw fill
|
||||
if (progress > 0)
|
||||
{
|
||||
RectangleShape fill = new RectangleShape(new Vector2f(size.X * Math.Clamp(progress, 0, 1), size.Y));
|
||||
fill.Position = position;
|
||||
fill.FillColor = fillColor;
|
||||
_window.Draw(fill);
|
||||
}
|
||||
|
||||
// Draw percentage text
|
||||
Text progressText = new Text($"{progress:P0}", _font, 12);
|
||||
progressText.FillColor = Color.White;
|
||||
progressText.Position = new Vector2f(
|
||||
position.X + size.X / 2 - progressText.GetLocalBounds().Width / 2,
|
||||
position.Y + size.Y / 2 - progressText.CharacterSize / 2
|
||||
);
|
||||
_window.Draw(progressText);
|
||||
}
|
||||
|
||||
public void DrawGraph(string title, List<float> data, Vector2f position, Vector2f size, Color lineColor)
|
||||
{
|
||||
if (data.Count < 2) return;
|
||||
|
||||
// Draw graph background
|
||||
RectangleShape graphBackground = new RectangleShape(size);
|
||||
graphBackground.Position = position;
|
||||
graphBackground.FillColor = new Color(0, 0, 0, 128);
|
||||
graphBackground.OutlineThickness = 1;
|
||||
graphBackground.OutlineColor = Color.White;
|
||||
_window.Draw(graphBackground);
|
||||
|
||||
// Draw title
|
||||
Text titleText = new Text(title, _font, 12);
|
||||
titleText.FillColor = Color.White;
|
||||
titleText.Position = new Vector2f(position.X + 5, position.Y + 5);
|
||||
_window.Draw(titleText);
|
||||
|
||||
// Find min and max values
|
||||
float minValue = data.Min();
|
||||
float maxValue = data.Max();
|
||||
float valueRange = Math.Max(maxValue - minValue, 0.001f);
|
||||
|
||||
// Draw data points as line
|
||||
VertexArray line = new VertexArray(PrimitiveType.LineStrip);
|
||||
|
||||
for (int i = 0; i < data.Count; i++)
|
||||
{
|
||||
float x = position.X + (i / (float)(data.Count - 1)) * size.X;
|
||||
float normalizedValue = (data[i] - minValue) / valueRange;
|
||||
float y = position.Y + size.Y - normalizedValue * size.Y;
|
||||
|
||||
line.Append(new Vertex(new Vector2f(x, y), lineColor));
|
||||
}
|
||||
|
||||
_window.Draw(line);
|
||||
|
||||
// Draw min/max labels
|
||||
Text minText = new Text($"{minValue:F0}", _font, 10);
|
||||
minText.FillColor = Color.White;
|
||||
minText.Position = new Vector2f(position.X + size.X - 30, position.Y + size.Y - 15);
|
||||
_window.Draw(minText);
|
||||
|
||||
Text maxText = new Text($"{maxValue:F0}", _font, 10);
|
||||
maxText.FillColor = Color.White;
|
||||
maxText.Position = new Vector2f(position.X + size.X - 30, position.Y + 5);
|
||||
_window.Draw(maxText);
|
||||
}
|
||||
}
|
||||
}
|
||||
164
Car simulation/UI/Instruments/Gauge.cs
Normal file
164
Car simulation/UI/Instruments/Gauge.cs
Normal file
@@ -0,0 +1,164 @@
|
||||
using SFML.Graphics;
|
||||
using SFML.System;
|
||||
|
||||
namespace Car_simulation.UI.Instruments
|
||||
{
|
||||
public class Gauge
|
||||
{
|
||||
private RectangleShape _background;
|
||||
private RectangleShape _needle;
|
||||
private Font _font;
|
||||
private Text _label;
|
||||
private Text _valueText;
|
||||
private Color _normalColor = Color.White;
|
||||
private Color _warningColor = new Color(255, 100, 100);
|
||||
|
||||
public Vector2f Position { get; set; }
|
||||
public float Size { get; set; } = 200f;
|
||||
|
||||
private float _value = 0f;
|
||||
private float _minValue;
|
||||
private float _maxValue;
|
||||
private float _valuePerMark;
|
||||
private float _linesPerMark;
|
||||
private string _displayText;
|
||||
|
||||
public Gauge(Font font, Vector2f position, float size, float minValue, float maxValue, float valuePerMark, string _displayText, float linesPerMark)
|
||||
{
|
||||
_minValue = minValue;
|
||||
_maxValue = maxValue;
|
||||
_linesPerMark = linesPerMark
|
||||
_valuePerMark = valuePerMark;
|
||||
_font = font;
|
||||
Position = position;
|
||||
Size = size;
|
||||
Initialize();
|
||||
}
|
||||
|
||||
private void Initialize()
|
||||
{
|
||||
_background = new RectangleShape(new Vector2f(Size, Size));
|
||||
_background.Position = Position;
|
||||
_background.FillColor = new Color(40, 40, 50);
|
||||
_background.OutlineThickness = 2;
|
||||
_background.OutlineColor = Color.White;
|
||||
|
||||
float needleLength = Size * 0.4f;
|
||||
_needle = new RectangleShape(new Vector2f(needleLength, 4));
|
||||
_needle.Position = new Vector2f(
|
||||
Position.X + Size / 2,
|
||||
Position.Y + Size / 2
|
||||
);
|
||||
_needle.FillColor = Color.Red;
|
||||
_needle.Origin = new Vector2f(needleLength * 0.875f, 2);
|
||||
|
||||
_label = new Text(_displayText, _font, (uint)(Size * 0.1f));
|
||||
_label.FillColor = Color.White;
|
||||
_label.Position = new Vector2f(
|
||||
Position.X + Size / 2 - _label.GetLocalBounds().Width / 2,
|
||||
Position.Y + Size * 0.80f
|
||||
);
|
||||
|
||||
_valueText = new Text("0", _font, (uint)(Size * 0.12f));
|
||||
_valueText.FillColor = _normalColor;
|
||||
_valueText.Position = new Vector2f(
|
||||
Position.X + Size / 2 - 20,
|
||||
Position.Y + Size
|
||||
);
|
||||
}
|
||||
|
||||
public void Update(float value)
|
||||
{
|
||||
_value = value;
|
||||
|
||||
_minValue
|
||||
float ratio = Math.Clamp(_value / _maxValue, 0f, 1f);
|
||||
float needleAngle = -45 + (270 * ratio);
|
||||
|
||||
_needle.Rotation = needleAngle;
|
||||
_valueText.DisplayedString = $"{_value:F0}";
|
||||
|
||||
FloatRect bounds = _valueText.GetLocalBounds();
|
||||
_valueText.Origin = new Vector2f(bounds.Width / 2, bounds.Height / 2);
|
||||
_valueText.Position = new Vector2f(
|
||||
Position.X + Size / 2,
|
||||
Position.Y + Size * 0.35f
|
||||
);
|
||||
}
|
||||
|
||||
public void Draw(RenderWindow window)
|
||||
{
|
||||
window.Draw(_background);
|
||||
|
||||
DrawTickMarks(window);
|
||||
|
||||
window.Draw(_needle);
|
||||
window.Draw(_label);
|
||||
window.Draw(_valueText);
|
||||
}
|
||||
|
||||
private void DrawTickMarks(RenderWindow window)
|
||||
{
|
||||
float targetAngle = 270;
|
||||
int marks = (int)MathF.Ceiling(_maxValue / 1000) * 2;
|
||||
|
||||
for (int i = marks; i >= 0; i--)
|
||||
{
|
||||
float angle = 135 + (i * (targetAngle / marks)); // 270° divided into 10 segments
|
||||
float startRadius = Size * 0.45f;
|
||||
float endRadius = Size * 0.42f;
|
||||
|
||||
if (i % 2 == 0) // Major tick
|
||||
{
|
||||
endRadius = Size * 0.38f;
|
||||
|
||||
float rpmValue = (i * 1000);
|
||||
Text label = new Text($"{rpmValue / 2000:F0}", _font, 12);
|
||||
label.FillColor = Color.White;
|
||||
|
||||
float labelRadius = Size * 0.32f;
|
||||
float _radAngle = angle * (float)Math.PI / 180f;
|
||||
Vector2f labelPos = new Vector2f(
|
||||
Position.X + Size / 2 + labelRadius * (float)Math.Cos(_radAngle),
|
||||
Position.Y + Size / 2 + labelRadius * (float)Math.Sin(_radAngle)
|
||||
);
|
||||
|
||||
FloatRect bounds = label.GetLocalBounds();
|
||||
label.Origin = new Vector2f(bounds.Width / 2, bounds.Height / 2);
|
||||
label.Position = labelPos;
|
||||
window.Draw(label);
|
||||
}
|
||||
|
||||
float radAngle = angle * (float)Math.PI / 180f;
|
||||
Vector2f startPos = new Vector2f(
|
||||
Position.X + Size / 2 + startRadius * (float)Math.Cos(radAngle),
|
||||
Position.Y + Size / 2 + startRadius * (float)Math.Sin(radAngle)
|
||||
);
|
||||
Vector2f endPos = new Vector2f(
|
||||
Position.X + Size / 2 + endRadius * (float)Math.Cos(radAngle),
|
||||
Position.Y + Size / 2 + endRadius * (float)Math.Sin(radAngle)
|
||||
);
|
||||
|
||||
Vertex[] line = new Vertex[2]
|
||||
{
|
||||
new Vertex(startPos, Color.White),
|
||||
new Vertex(endPos, Color.White)
|
||||
};
|
||||
|
||||
window.Draw(line, PrimitiveType.Lines);
|
||||
}
|
||||
}
|
||||
|
||||
public void SetPosition(Vector2f position)
|
||||
{
|
||||
Position = position;
|
||||
Initialize(); // Re-initialize with new position
|
||||
}
|
||||
|
||||
public void SetSize(float size)
|
||||
{
|
||||
Size = size;
|
||||
Initialize(); // Re-initialize with new size
|
||||
}
|
||||
}
|
||||
}
|
||||
73
Car simulation/UI/Instruments/InstrumentPanel.cs
Normal file
73
Car simulation/UI/Instruments/InstrumentPanel.cs
Normal file
@@ -0,0 +1,73 @@
|
||||
using Car_simulation.Core.Models;
|
||||
using SFML.Graphics;
|
||||
using SFML.System;
|
||||
|
||||
namespace Car_simulation.UI.Instruments
|
||||
{
|
||||
public class InstrumentPanel
|
||||
{
|
||||
private List<Text> _displayTexts = new List<Text>();
|
||||
private Tachometer _tachometer;
|
||||
private Speedometer _speedometer;
|
||||
private Font _font;
|
||||
|
||||
private Color _textColor = Color.White;
|
||||
|
||||
public InstrumentPanel(Font font, Vector2f tachometerPosition, Vector2f speedometerPosition)
|
||||
{
|
||||
_font = font;
|
||||
_tachometer = new Tachometer(font, tachometerPosition, 200, 7000);
|
||||
_speedometer = new Speedometer(font, speedometerPosition, 200);
|
||||
InitializeDisplayTexts();
|
||||
}
|
||||
|
||||
private void InitializeDisplayTexts()
|
||||
{
|
||||
for (int i = 0; i < 30; i++)
|
||||
{
|
||||
Text text = new Text("", _font, 16);
|
||||
text.FillColor = Color.White;
|
||||
text.Position = new Vector2f(20, 20 + i * 24);
|
||||
_displayTexts.Add(text);
|
||||
}
|
||||
}
|
||||
|
||||
public void Update(Car_simulation.Core.Models.Car car)
|
||||
{
|
||||
UpdateTextDisplay(car);
|
||||
_tachometer.Update(car.Engine.RPM);
|
||||
_speedometer.Update(car.Speed);
|
||||
}
|
||||
|
||||
private void UpdateTextDisplay(Car car)
|
||||
{
|
||||
var displayData = car.GetDisplayData();
|
||||
|
||||
// Just put each line in a text element
|
||||
for (int i = 0; i < _displayTexts.Count; i++)
|
||||
{
|
||||
if (i < displayData.Count)
|
||||
{
|
||||
_displayTexts[i].DisplayedString = displayData[i];
|
||||
_displayTexts[i].FillColor = _textColor;
|
||||
}
|
||||
else
|
||||
{
|
||||
_displayTexts[i].DisplayedString = "";
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public void Draw(RenderWindow window)
|
||||
{
|
||||
foreach (var text in _displayTexts)
|
||||
{
|
||||
if (!string.IsNullOrEmpty(text.DisplayedString))
|
||||
window.Draw(text);
|
||||
}
|
||||
|
||||
_tachometer.Draw(window);
|
||||
_speedometer.Draw(window);
|
||||
}
|
||||
}
|
||||
}
|
||||
201
Car simulation/UI/Instruments/Speedometer.cs
Normal file
201
Car simulation/UI/Instruments/Speedometer.cs
Normal file
@@ -0,0 +1,201 @@
|
||||
using SFML.Graphics;
|
||||
using SFML.System;
|
||||
|
||||
namespace Car_simulation.UI.Instruments
|
||||
{
|
||||
public class Speedometer
|
||||
{
|
||||
private RectangleShape _background;
|
||||
private RectangleShape _needle;
|
||||
private Font _font;
|
||||
private Text _label;
|
||||
private Text _speedText;
|
||||
private Text _gearText;
|
||||
private Color _normalColor = Color.White;
|
||||
private Color _highlightColor = new Color(0, 150, 255);
|
||||
|
||||
public Vector2f Position { get; set; }
|
||||
public float Size { get; set; } = 200f;
|
||||
|
||||
private float _currentSpeed = 0f;
|
||||
private string _currentGear = "N";
|
||||
private const float MAX_SPEED = 200f; // km/h
|
||||
|
||||
public Speedometer(Font font)
|
||||
{
|
||||
_font = font;
|
||||
Initialize();
|
||||
}
|
||||
|
||||
public Speedometer(Font font, Vector2f position, float size)
|
||||
{
|
||||
_font = font;
|
||||
Position = position;
|
||||
Size = size;
|
||||
Initialize();
|
||||
}
|
||||
|
||||
private void Initialize()
|
||||
{
|
||||
// Background
|
||||
_background = new RectangleShape(new Vector2f(Size, Size));
|
||||
_background.Position = Position;
|
||||
_background.FillColor = new Color(40, 40, 50);
|
||||
_background.OutlineThickness = 2;
|
||||
_background.OutlineColor = Color.White;
|
||||
|
||||
// Needle
|
||||
float needleLength = Size * 0.4f;
|
||||
_needle = new RectangleShape(new Vector2f(needleLength, 4));
|
||||
_needle.Position = new Vector2f(
|
||||
Position.X + Size / 2,
|
||||
Position.Y + Size / 2
|
||||
);
|
||||
_needle.FillColor = Color.Green;
|
||||
_needle.Origin = new Vector2f(needleLength * 0.875f, 2);
|
||||
|
||||
// Labels
|
||||
_label = new Text("SPEED", _font, (uint)(Size * 0.1f));
|
||||
_label.FillColor = Color.White;
|
||||
_label.Position = new Vector2f(
|
||||
Position.X + Size / 2 - _label.GetLocalBounds().Width / 2,
|
||||
Position.Y + Size * 0.05f
|
||||
);
|
||||
|
||||
_speedText = new Text("0 km/h", _font, (uint)(Size * 0.1f));
|
||||
_speedText.FillColor = _normalColor;
|
||||
_speedText.Position = new Vector2f(
|
||||
Position.X + Size / 2 - 40,
|
||||
Position.Y + Size * 0.25f
|
||||
);
|
||||
|
||||
_gearText = new Text($"GEAR {_currentGear}", _font, (uint)(Size * 0.14f));
|
||||
_gearText.FillColor = _highlightColor;
|
||||
_gearText.Position = new Vector2f(
|
||||
Position.X + Size * 0.5f,
|
||||
Position.Y + Size * 1.1f
|
||||
);
|
||||
}
|
||||
|
||||
public void Update(float speed)
|
||||
{
|
||||
_currentSpeed = speed;
|
||||
float speedKmh = speed * 3.6f;
|
||||
|
||||
// Update needle angle (-90° to +180° rotation)
|
||||
float speedRatio = Math.Clamp(speedKmh / MAX_SPEED, 0f, 1f);
|
||||
float needleAngle = -90 + (270 * speedRatio);
|
||||
_needle.Rotation = needleAngle;
|
||||
|
||||
// Update speed text
|
||||
_speedText.DisplayedString = $"{speedKmh:F1} km/h";
|
||||
|
||||
// Center speed text
|
||||
FloatRect bounds = _speedText.GetLocalBounds();
|
||||
_speedText.Origin = new Vector2f(bounds.Width / 2, bounds.Height / 2);
|
||||
_speedText.Position = new Vector2f(
|
||||
Position.X + Size / 2,
|
||||
Position.Y + Size * 0.35f
|
||||
);
|
||||
}
|
||||
|
||||
public void UpdateGear(string gear)
|
||||
{
|
||||
_currentGear = gear;
|
||||
_gearText.DisplayedString = $"GEAR {gear}";
|
||||
|
||||
// Center gear text
|
||||
FloatRect bounds = _gearText.GetLocalBounds();
|
||||
_gearText.Origin = new Vector2f(bounds.Width / 2, bounds.Height / 2);
|
||||
_gearText.Position = new Vector2f(
|
||||
Position.X + Size / 2,
|
||||
Position.Y + Size * 1.1f
|
||||
);
|
||||
}
|
||||
|
||||
public void Draw(RenderWindow window)
|
||||
{
|
||||
window.Draw(_background);
|
||||
|
||||
// Draw tick marks
|
||||
DrawTickMarks(window);
|
||||
|
||||
window.Draw(_needle);
|
||||
window.Draw(_label);
|
||||
window.Draw(_speedText);
|
||||
window.Draw(_gearText);
|
||||
}
|
||||
|
||||
private void DrawTickMarks(RenderWindow window)
|
||||
{
|
||||
for (int i = 0; i <= 10; i++)
|
||||
{
|
||||
float angle = -90 + (i * 27); // 270° divided into 10 segments
|
||||
float startRadius = Size * 0.45f;
|
||||
float endRadius = Size * 0.42f;
|
||||
|
||||
if (i % 2 == 0) // Major tick
|
||||
{
|
||||
endRadius = Size * 0.38f;
|
||||
|
||||
// Add number label for major ticks
|
||||
float speedValue = (i * MAX_SPEED / 10f);
|
||||
Text label = new Text($"{speedValue:F0}", _font, 12);
|
||||
label.FillColor = Color.White;
|
||||
|
||||
float labelRadius = Size * 0.32f;
|
||||
float _radAngle = angle * (float)Math.PI / 180f;
|
||||
Vector2f labelPos = new Vector2f(
|
||||
Position.X + Size / 2 + labelRadius * (float)Math.Cos(_radAngle),
|
||||
Position.Y + Size / 2 + labelRadius * (float)Math.Sin(_radAngle)
|
||||
);
|
||||
|
||||
FloatRect bounds = label.GetLocalBounds();
|
||||
label.Origin = new Vector2f(bounds.Width / 2, bounds.Height / 2);
|
||||
label.Position = labelPos;
|
||||
window.Draw(label);
|
||||
}
|
||||
|
||||
float radAngle = angle * (float)Math.PI / 180f;
|
||||
Vector2f startPos = new Vector2f(
|
||||
Position.X + Size / 2 + startRadius * (float)Math.Cos(radAngle),
|
||||
Position.Y + Size / 2 + startRadius * (float)Math.Sin(radAngle)
|
||||
);
|
||||
Vector2f endPos = new Vector2f(
|
||||
Position.X + Size / 2 + endRadius * (float)Math.Cos(radAngle),
|
||||
Position.Y + Size / 2 + endRadius * (float)Math.Sin(radAngle)
|
||||
);
|
||||
|
||||
Vertex[] line = new Vertex[2]
|
||||
{
|
||||
new Vertex(startPos, Color.White),
|
||||
new Vertex(endPos, Color.White)
|
||||
};
|
||||
|
||||
window.Draw(line, PrimitiveType.Lines);
|
||||
}
|
||||
}
|
||||
|
||||
public void SetPosition(Vector2f position)
|
||||
{
|
||||
Position = position;
|
||||
Initialize(); // Re-initialize with new position
|
||||
}
|
||||
|
||||
public void SetSize(float size)
|
||||
{
|
||||
Size = size;
|
||||
Initialize(); // Re-initialize with new size
|
||||
}
|
||||
|
||||
public void SetGearColor(Color color)
|
||||
{
|
||||
_gearText.FillColor = color;
|
||||
}
|
||||
|
||||
public void SetNeedleColor(Color color)
|
||||
{
|
||||
_needle.FillColor = color;
|
||||
}
|
||||
}
|
||||
}
|
||||
161
Car simulation/UI/Instruments/Tachometer.cs
Normal file
161
Car simulation/UI/Instruments/Tachometer.cs
Normal file
@@ -0,0 +1,161 @@
|
||||
using SFML.Graphics;
|
||||
using SFML.System;
|
||||
|
||||
namespace Car_simulation.UI.Instruments
|
||||
{
|
||||
public class Tachometer
|
||||
{
|
||||
private RectangleShape _background;
|
||||
private RectangleShape _needle;
|
||||
private Font _font;
|
||||
private Text _label;
|
||||
private Text _rpmText;
|
||||
private Color _normalColor = Color.White;
|
||||
private Color _warningColor = new Color(255, 100, 100);
|
||||
|
||||
public Vector2f Position { get; set; }
|
||||
public float Size { get; set; } = 200f;
|
||||
|
||||
private float _currentRPM = 0f;
|
||||
private float _maxRpm = 7000f;
|
||||
|
||||
public Tachometer(Font font, Vector2f position, float size, float maxRpm)
|
||||
{
|
||||
_maxRpm = maxRpm;
|
||||
_font = font;
|
||||
Position = position;
|
||||
Size = size;
|
||||
Initialize();
|
||||
}
|
||||
|
||||
private void Initialize()
|
||||
{
|
||||
// Background
|
||||
_background = new RectangleShape(new Vector2f(Size, Size));
|
||||
_background.Position = Position;
|
||||
_background.FillColor = new Color(40, 40, 50);
|
||||
_background.OutlineThickness = 2;
|
||||
_background.OutlineColor = Color.White;
|
||||
|
||||
// Needle
|
||||
float needleLength = Size * 0.4f;
|
||||
_needle = new RectangleShape(new Vector2f(needleLength, 4));
|
||||
_needle.Position = new Vector2f(
|
||||
Position.X + Size / 2,
|
||||
Position.Y + Size / 2
|
||||
);
|
||||
_needle.FillColor = Color.Red;
|
||||
_needle.Origin = new Vector2f(needleLength * 0.875f, 2); // Offset origin to create pivot point
|
||||
|
||||
// Labels
|
||||
_label = new Text("RPM", _font, (uint)(Size * 0.1f));
|
||||
_label.FillColor = Color.White;
|
||||
_label.Position = new Vector2f(
|
||||
Position.X + Size / 2 - _label.GetLocalBounds().Width / 2,
|
||||
Position.Y + Size * 0.80f
|
||||
);
|
||||
|
||||
_rpmText = new Text("0", _font, (uint)(Size * 0.12f));
|
||||
_rpmText.FillColor = _normalColor;
|
||||
_rpmText.Position = new Vector2f(
|
||||
Position.X + Size / 2 - 20,
|
||||
Position.Y + Size
|
||||
);
|
||||
}
|
||||
|
||||
public void Update(float rpm)
|
||||
{
|
||||
_currentRPM = rpm;
|
||||
|
||||
float rpmRatio = Math.Clamp(rpm / _maxRpm, 0f, 1f);
|
||||
float needleAngle = -45 + (270 * rpmRatio);
|
||||
_needle.Rotation = needleAngle;
|
||||
|
||||
// Update RPM text
|
||||
_rpmText.DisplayedString = $"{rpm:F0}";
|
||||
|
||||
// Center RPM text
|
||||
FloatRect bounds = _rpmText.GetLocalBounds();
|
||||
_rpmText.Origin = new Vector2f(bounds.Width / 2, bounds.Height / 2);
|
||||
_rpmText.Position = new Vector2f(
|
||||
Position.X + Size / 2,
|
||||
Position.Y + Size * 0.35f
|
||||
);
|
||||
}
|
||||
|
||||
public void Draw(RenderWindow window)
|
||||
{
|
||||
window.Draw(_background);
|
||||
|
||||
DrawTickMarks(window);
|
||||
|
||||
window.Draw(_needle);
|
||||
window.Draw(_label);
|
||||
window.Draw(_rpmText);
|
||||
}
|
||||
|
||||
private void DrawTickMarks(RenderWindow window)
|
||||
{
|
||||
float targetAngle = 270;
|
||||
int marks = (int)MathF.Ceiling(_maxRpm / 1000) * 2;
|
||||
|
||||
for (int i = marks; i >= 0; i--)
|
||||
{
|
||||
float angle = 135 + (i * (targetAngle / marks)); // 270° divided into 10 segments
|
||||
float startRadius = Size * 0.45f;
|
||||
float endRadius = Size * 0.42f;
|
||||
|
||||
if (i % 2 == 0) // Major tick
|
||||
{
|
||||
endRadius = Size * 0.38f;
|
||||
|
||||
float rpmValue = (i * 1000);
|
||||
Text label = new Text($"{rpmValue / 2000:F0}", _font, 12);
|
||||
label.FillColor = Color.White;
|
||||
|
||||
float labelRadius = Size * 0.32f;
|
||||
float _radAngle = angle * (float)Math.PI / 180f;
|
||||
Vector2f labelPos = new Vector2f(
|
||||
Position.X + Size / 2 + labelRadius * (float)Math.Cos(_radAngle),
|
||||
Position.Y + Size / 2 + labelRadius * (float)Math.Sin(_radAngle)
|
||||
);
|
||||
|
||||
FloatRect bounds = label.GetLocalBounds();
|
||||
label.Origin = new Vector2f(bounds.Width / 2, bounds.Height / 2);
|
||||
label.Position = labelPos;
|
||||
window.Draw(label);
|
||||
}
|
||||
|
||||
float radAngle = angle * (float)Math.PI / 180f;
|
||||
Vector2f startPos = new Vector2f(
|
||||
Position.X + Size / 2 + startRadius * (float)Math.Cos(radAngle),
|
||||
Position.Y + Size / 2 + startRadius * (float)Math.Sin(radAngle)
|
||||
);
|
||||
Vector2f endPos = new Vector2f(
|
||||
Position.X + Size / 2 + endRadius * (float)Math.Cos(radAngle),
|
||||
Position.Y + Size / 2 + endRadius * (float)Math.Sin(radAngle)
|
||||
);
|
||||
|
||||
Vertex[] line = new Vertex[2]
|
||||
{
|
||||
new Vertex(startPos, Color.White),
|
||||
new Vertex(endPos, Color.White)
|
||||
};
|
||||
|
||||
window.Draw(line, PrimitiveType.Lines);
|
||||
}
|
||||
}
|
||||
|
||||
public void SetPosition(Vector2f position)
|
||||
{
|
||||
Position = position;
|
||||
Initialize(); // Re-initialize with new position
|
||||
}
|
||||
|
||||
public void SetSize(float size)
|
||||
{
|
||||
Size = size;
|
||||
Initialize(); // Re-initialize with new size
|
||||
}
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user