From 3ac0b6b86622a168cd714219d3e731513de91c04 Mon Sep 17 00:00:00 2001 From: maxwes08 Date: Fri, 17 Apr 2026 12:38:11 +0200 Subject: [PATCH] fix: clean gitignore for Godot C# --- .editorconfig | 4 + .gitattributes | 2 + .gitignore | 30 +++ Physics.csproj | 7 + Physics.sln | 19 ++ Scripts/Components/Engine.cs | 131 ++++++++++ Scripts/Components/Engine.cs.uid | 1 + Scripts/Components/EngineControlUnit.cs | 48 ++++ Scripts/Components/EngineControlUnit.cs.uid | 1 + Scripts/Core/RotatingComponent.cs | 58 +++++ Scripts/Core/RotatingComponent.cs.uid | 1 + Scripts/Core/TorqueMap.cs | 55 ++++ Scripts/Core/TorqueMap.cs.uid | 1 + Scripts/PhysicsManager.cs | 95 +++++++ Scripts/PhysicsManager.cs.uid | 1 + Scripts/ProceduralEngine.cs | 269 ++++++++++++++++++++ Scripts/ProceduralEngine.cs.uid | 1 + Scripts/Util.cs | 7 + Scripts/Util.cs.uid | 1 + icon.svg | 1 + icon.svg.import | 43 ++++ project.godot | 28 ++ scene.tscn | 37 +++ 23 files changed, 841 insertions(+) create mode 100644 .editorconfig create mode 100644 .gitattributes create mode 100644 .gitignore create mode 100644 Physics.csproj create mode 100644 Physics.sln create mode 100644 Scripts/Components/Engine.cs create mode 100644 Scripts/Components/Engine.cs.uid create mode 100644 Scripts/Components/EngineControlUnit.cs create mode 100644 Scripts/Components/EngineControlUnit.cs.uid create mode 100644 Scripts/Core/RotatingComponent.cs create mode 100644 Scripts/Core/RotatingComponent.cs.uid create mode 100644 Scripts/Core/TorqueMap.cs create mode 100644 Scripts/Core/TorqueMap.cs.uid create mode 100644 Scripts/PhysicsManager.cs create mode 100644 Scripts/PhysicsManager.cs.uid create mode 100644 Scripts/ProceduralEngine.cs create mode 100644 Scripts/ProceduralEngine.cs.uid create mode 100644 Scripts/Util.cs create mode 100644 Scripts/Util.cs.uid create mode 100644 icon.svg create mode 100644 icon.svg.import create mode 100644 project.godot create mode 100644 scene.tscn diff --git a/.editorconfig b/.editorconfig new file mode 100644 index 0000000..f28239b --- /dev/null +++ b/.editorconfig @@ -0,0 +1,4 @@ +root = true + +[*] +charset = utf-8 diff --git a/.gitattributes b/.gitattributes new file mode 100644 index 0000000..8ad74f7 --- /dev/null +++ b/.gitattributes @@ -0,0 +1,2 @@ +# Normalize EOL for all files that Git considers text files. +* text=auto eol=lf diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..d03628a --- /dev/null +++ b/.gitignore @@ -0,0 +1,30 @@ +# Godot 4+ imported files +.import/ +.godot/ + +# Mono / C# build folders +.mono/ +bin/ +obj/ + +# Visual Studio / Rider / VS Code +.vs/ +.vscode/ +*.user +*.suo +*.userprefs +*.sln.docstates + +# OS junk +.DS_Store +Thumbs.db + +# Logs +*.log + +# Build/export folders (optional depending on project) +export/ +build/ + +# Temporary files +*.tmp \ No newline at end of file diff --git a/Physics.csproj b/Physics.csproj new file mode 100644 index 0000000..e457872 --- /dev/null +++ b/Physics.csproj @@ -0,0 +1,7 @@ + + + net8.0 + net9.0 + true + + \ No newline at end of file diff --git a/Physics.sln b/Physics.sln new file mode 100644 index 0000000..e23f953 --- /dev/null +++ b/Physics.sln @@ -0,0 +1,19 @@ +Microsoft Visual Studio Solution File, Format Version 12.00 +# Visual Studio 2012 +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Physics", "Physics.csproj", "{6DC8B879-E534-4466-8C36-228A171D7521}" +EndProject +Global + GlobalSection(SolutionConfigurationPlatforms) = preSolution + Debug|Any CPU = Debug|Any CPU + ExportDebug|Any CPU = ExportDebug|Any CPU + ExportRelease|Any CPU = ExportRelease|Any CPU + EndGlobalSection + GlobalSection(ProjectConfigurationPlatforms) = postSolution + {6DC8B879-E534-4466-8C36-228A171D7521}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {6DC8B879-E534-4466-8C36-228A171D7521}.Debug|Any CPU.Build.0 = Debug|Any CPU + {6DC8B879-E534-4466-8C36-228A171D7521}.ExportDebug|Any CPU.ActiveCfg = ExportDebug|Any CPU + {6DC8B879-E534-4466-8C36-228A171D7521}.ExportDebug|Any CPU.Build.0 = ExportDebug|Any CPU + {6DC8B879-E534-4466-8C36-228A171D7521}.ExportRelease|Any CPU.ActiveCfg = ExportRelease|Any CPU + {6DC8B879-E534-4466-8C36-228A171D7521}.ExportRelease|Any CPU.Build.0 = ExportRelease|Any CPU + EndGlobalSection +EndGlobal diff --git a/Scripts/Components/Engine.cs b/Scripts/Components/Engine.cs new file mode 100644 index 0000000..e5d0fe6 --- /dev/null +++ b/Scripts/Components/Engine.cs @@ -0,0 +1,131 @@ +using System; + +public class Engine : RotatingComponent +{ + public double ThrottlePosition { get; set; } + public double CurrentPower { get; private set; } + public double CombustionPower { get; private set; } + public int CylinderCount { get; private set; } + public EngineControlUnit Ecu = new EngineControlUnit(); + + // Friction + private const double _baseFriction = 12.0; // Seals, oil pump, valvetrain (Nm) + private const double _linearFriction = 0.025; // Hydrodynamic bearing drag (Nm/(rad/s)) + private const double _quadraticFriction = 0.00002; // Windage & churning (Nm/(rad/s)²) + + // Engine geometry + private double _displacementCC; + private double _compressionRatio; + + // Volumetric efficiency tuning – now in rad/s + public double VolumetricEfficiencyPeak { get; set; } = 1.15; // was 1.10 + public double AngularVelocityForVEPeak { get; set; } = 550.0; // 5250 RPM (rad/s = 550) + public double AngularVelocityForVEMin { get; set; } = 800.0; // 7639 RPM + public double VEminAtRedline { get; set; } = 0.85; // much higher at redline + + // Physics constants + private const double ATM_PRESSURE_KPA = 101.3; + private const double AIR_DENSITY_KG_PER_M3 = 1.225; + private const double FUEL_HEAT_VALUE_J_PER_KG = 43e6; + private const double STOICHIOMETRIC_AIR_FUEL_RATIO = 14.7; + + public Engine(double inertia, int cylinders, double displacementCC, double compressionRatio) + { + CylinderCount = cylinders; + _displacementCC = displacementCC; + _compressionRatio = compressionRatio; + + MomentOfInertia = inertia; + AngularVelocity = 100; // rad/s (~955 RPM) + } + + public override void Update(double dt) + { + Ecu.Update(dt, this); + + ApplyThrottleTorque(); + ApplyFrictionTorque(); + + CurrentPower = AccumulatedTorque * AngularVelocity; + + base.Update(dt); + } + + private double ComputeIndicatedTorque() + { + double w = AngularVelocity; // rad/s + double throttle = Math.Clamp(ThrottlePosition, 0.0, 1.0); + + // Volumetric Efficiency (WOT) as function of w (rad/s) + double veWOT; + if (w <= AngularVelocityForVEPeak) + { + // Start at a realistic 0.75 at zero RPM, peaking at your target + double t = w / AngularVelocityForVEPeak; + veWOT = 0.75 + (VolumetricEfficiencyPeak - 0.75) * t * (2 - t); + } + else + { + // Linear drop from VE_peak to VEminAtRedline + double t = (w - AngularVelocityForVEPeak) / (AngularVelocityForVEMin - AngularVelocityForVEPeak); + t = Math.Clamp(t, 0.0, 1.0); + veWOT = VolumetricEfficiencyPeak - t * (VolumetricEfficiencyPeak - VEminAtRedline); + } + veWOT = Math.Clamp(veWOT, 0.25, VolumetricEfficiencyPeak); + + // Intake loss + double maxEngineDemandSpeed = 700.0; // rad/s, roughly 6700 RPM + double throttleArea = Math.Pow(throttle, 1.5); + double engineDemand = (w / maxEngineDemandSpeed) * veWOT; + + const double intakeResistance = 0.03; // was 0.5 – now physically realistic + double mapFraction = throttleArea / (throttleArea + intakeResistance * engineDemand); + + if (throttle == 0) mapFraction = 0; + + double manifoldPressureKpa = ATM_PRESSURE_KPA * mapFraction; + manifoldPressureKpa = Math.Clamp(manifoldPressureKpa, 0, ATM_PRESSURE_KPA); + + double veActual = veWOT * (manifoldPressureKpa / ATM_PRESSURE_KPA); + + // Exhaust loss (backpressure) + double exhaustBackpressureKpa = 2.0e-5 * w * w; + double exhaustLossFactor = 1.0 - Math.Min(0.25, exhaustBackpressureKpa / ATM_PRESSURE_KPA); + + // Air & fuel mass per cycle + double displacementM3 = _displacementCC * 1e-6; + double airMassPerCycleKg = veActual * AIR_DENSITY_KG_PER_M3 * displacementM3; + double fuelMassPerCycleKg = airMassPerCycleKg / STOICHIOMETRIC_AIR_FUEL_RATIO; + double energyPerCycleJ = fuelMassPerCycleKg * FUEL_HEAT_VALUE_J_PER_KG; + + // Thermal efficiency + double gamma = 1.4; + double ottoEfficiency = 1.0 - 1.0 / Math.Pow(_compressionRatio, gamma - 1.0); + double thermalEfficiency = 0.65 * ottoEfficiency; + + double workPerCycleJ = energyPerCycleJ * thermalEfficiency * exhaustLossFactor; + + double indicatedTorque = workPerCycleJ / (4.0 * Math.PI); + indicatedTorque = Math.Min(600.0, Math.Max(0.0, indicatedTorque)); + + return indicatedTorque; + } + + public void ApplyThrottleTorque() + { + double torque = ComputeIndicatedTorque(); + CombustionPower = torque * AngularVelocity; + ApplyTorque(torque); + } + + public void ApplyFrictionTorque() + { + // Friction uses angular velocity (rad/s) directly + double w = AngularVelocity; + double frictionMag = _baseFriction + + _linearFriction * Math.Abs(w) + + _quadraticFriction * w * w; + double frictionTorque = -Math.Sign(w) * frictionMag; + ApplyTorque(frictionTorque); + } +} \ No newline at end of file diff --git a/Scripts/Components/Engine.cs.uid b/Scripts/Components/Engine.cs.uid new file mode 100644 index 0000000..c7c8fdd --- /dev/null +++ b/Scripts/Components/Engine.cs.uid @@ -0,0 +1 @@ +uid://c3gg5mg8rjw50 diff --git a/Scripts/Components/EngineControlUnit.cs b/Scripts/Components/EngineControlUnit.cs new file mode 100644 index 0000000..9228119 --- /dev/null +++ b/Scripts/Components/EngineControlUnit.cs @@ -0,0 +1,48 @@ +using System; + +public class EngineControlUnit +{ + public double Throttle; + public bool IsInNeutral = true; + + private const double _revLimit = 7000; + private const double _idleRpm = 800; + private const double _idleSensitivity = 1e-2; + private const double _throttleSensitivity = 20; + private const double _neutralThrottleLimit = 0.2; + + private double _idlePosition = 0; + private double _targetThrottle = 0; + private double _currentThrottle = 0; + + public EngineControlUnit() + { + + } + + public void Update(double dt, Engine engine) + { + if (engine.RPM > _revLimit) + { + engine.ThrottlePosition = 0; return; + } + + _targetThrottle = Math.Clamp(GetIdleThrottle(dt, engine.RPM) + Throttle, 0, 1); + _currentThrottle = Util.Lerp(_currentThrottle, _targetThrottle, dt * _throttleSensitivity); + + if (IsInNeutral) _currentThrottle = Math.Min(_currentThrottle, _neutralThrottleLimit); + + engine.ThrottlePosition = _currentThrottle; + } + + public double GetIdleThrottle(double dt, double rpm) + { + if (rpm > _idleRpm) return 0; + + double diff = rpm - _idleRpm; + + _idlePosition = Math.Clamp(_idlePosition - (diff * _idleSensitivity * dt), 0, 1.0); + + return _idlePosition; + } +} \ No newline at end of file diff --git a/Scripts/Components/EngineControlUnit.cs.uid b/Scripts/Components/EngineControlUnit.cs.uid new file mode 100644 index 0000000..041c888 --- /dev/null +++ b/Scripts/Components/EngineControlUnit.cs.uid @@ -0,0 +1 @@ +uid://0k3uqu06s0im diff --git a/Scripts/Core/RotatingComponent.cs b/Scripts/Core/RotatingComponent.cs new file mode 100644 index 0000000..d7388b1 --- /dev/null +++ b/Scripts/Core/RotatingComponent.cs @@ -0,0 +1,58 @@ +using System; + +public abstract class RotatingComponent +{ + public double MomentOfInertia { get; set; } // kg·m² + public double AngularVelocity { get; set; } // rad/s + public double AngularPosition { get; set; } // rad (0..2π, wrapped) + public double TotalAngularPosition { get; set; } // rad (never wraps) + public double AccumulatedTorque {get; private set; } + + public RotatingComponent(double momentOfInertia = 0.1, double angularVelocity = 0, double angularPosition = 0) + { + MomentOfInertia = momentOfInertia; + AngularVelocity = angularVelocity; + AngularPosition = angularPosition; + TotalAngularPosition = angularPosition; + AccumulatedTorque = 0; + } + + public virtual void ApplyTorque(double torqueNm) + { + AccumulatedTorque += torqueNm; + } + + public virtual void Update(double dt) + { + if (dt <= 0) return; + + if (MomentOfInertia > 0) + { + double angularAcceleration = AccumulatedTorque / MomentOfInertia; + AngularVelocity += angularAcceleration * dt; + } + + double deltaAngle = AngularVelocity * dt; + TotalAngularPosition += deltaAngle; + AngularPosition += deltaAngle; + + // Wrap AngularPosition to 0..2π for sin/cos + AngularPosition = AngularPosition % (2 * Math.PI); + if (AngularPosition < 0) AngularPosition += 2 * Math.PI; + + AccumulatedTorque = 0; + } + + public double RPM => AngularVelocity * 60 / (2 * Math.PI); + public double TotalAngleDeg => TotalAngularPosition * 180 / Math.PI; + + public void SetRPM(double rpm) + { + AngularVelocity = rpm * 2 * Math.PI / 60; + } + + public virtual void ResetTorque() + { + AccumulatedTorque = 0; + } +} \ No newline at end of file diff --git a/Scripts/Core/RotatingComponent.cs.uid b/Scripts/Core/RotatingComponent.cs.uid new file mode 100644 index 0000000..2e897a7 --- /dev/null +++ b/Scripts/Core/RotatingComponent.cs.uid @@ -0,0 +1 @@ +uid://cym3n25hyryxy diff --git a/Scripts/Core/TorqueMap.cs b/Scripts/Core/TorqueMap.cs new file mode 100644 index 0000000..f11c33f --- /dev/null +++ b/Scripts/Core/TorqueMap.cs @@ -0,0 +1,55 @@ +using System; +using System.Collections.Generic; +using System.Linq; + +public class TorqueMap +{ + public Dictionary points; + + public TorqueMap() + { + points = new Dictionary(); + } + + public void AddPoint(int rpm, double torque) + { + points.Add(rpm, torque); + } + + public double GetTorque(int rpm) + { + if (points == null || points.Count == 0) + throw new System.InvalidOperationException("Torque map has no points."); + + var sortedRpms = points.Keys.OrderBy(r => r).ToList(); + int firstRpm = sortedRpms[0]; + int lastRpm = sortedRpms[sortedRpms.Count - 1]; + + if (rpm <= firstRpm) + return points[firstRpm]; + if (rpm >= lastRpm) + return points[lastRpm]; + + for (int i = 0; i < sortedRpms.Count - 1; i++) + { + int r1 = sortedRpms[i]; + int r2 = sortedRpms[i + 1]; + if (rpm >= r1 && rpm <= r2) + { + double t1 = points[r1]; + double t2 = points[r2]; + return t1 + (rpm - r1) * (t2 - t1) / (r2 - r1); + } + } + + throw new System.InvalidOperationException("Interpolation failed."); + } + + public double GetTorque(double angularVelocityRadPerSec) + { + // Convert rad/s to RPM: 1 rad/s = 60/(2π) RPM + double rpmDouble = angularVelocityRadPerSec * 60.0 / (2.0 * Math.PI); + int rpm = (int)rpmDouble; // or use rounding if preferred: (int)Math.Round(rpmDouble) + return GetTorque(rpm); + } +} \ No newline at end of file diff --git a/Scripts/Core/TorqueMap.cs.uid b/Scripts/Core/TorqueMap.cs.uid new file mode 100644 index 0000000..06cc6c2 --- /dev/null +++ b/Scripts/Core/TorqueMap.cs.uid @@ -0,0 +1 @@ +uid://bn0khacav57pi diff --git a/Scripts/PhysicsManager.cs b/Scripts/PhysicsManager.cs new file mode 100644 index 0000000..7612292 --- /dev/null +++ b/Scripts/PhysicsManager.cs @@ -0,0 +1,95 @@ +using Godot; +using System; + +public partial class PhysicsManager : Node2D +{ + private const double TARGET_DT = 1.0 / 2000.0; + private const double VALUE_SENSITIVITY = 5; + + private double _accumulator = 0.0; + private double _simulationTime = 0.0; + + private double _rpmSmooth = 0, _powerSmooth = 0, _throttleSmooth = 0; + + private Label _rpmLabel, _powerLabel, _throttleLabel; + + private Engine _engine; + + [Export] public ProceduralEngine EngineAudio; + + public override void _Ready() + { + _rpmLabel = GetNode