new powertrain смерть чуркам

This commit is contained in:
Valera
2025-06-13 21:16:20 +07:00
parent ba9afba4d1
commit 964b46e1c5
15 changed files with 868 additions and 416 deletions

View File

@@ -1,354 +1,365 @@
using Sandbox;
using System;
using System.Collections.Generic;
using VeloX.Powertrain;
namespace VeloX;
public partial class VeloXCar
{
[Property, Feature( "Engine" ), Sync] public EngineState EngineState { get; set; }
[Property, Feature( "Engine" )] public EngineStream Stream { get; set; }
public EngineStreamPlayer StreamPlayer { get; set; }
[Property, Feature( "Engine" )] public float MinRPM { get; set; } = 800;
[Property, Feature( "Engine" )] public float MaxRPM { get; set; } = 7000;
[Property, Feature( "Engine" ), Range( -1, 1 )]
public float PowerDistribution
{
get => powerDistribution; set
{
powerDistribution = value;
UpdatePowerDistribution();
}
}
[Property, Feature( "Engine" )] public float FlyWheelMass { get; set; } = 80f;
[Property, Feature( "Engine" )] public float FlyWheelRadius { get; set; } = 0.5f;
[Property, Feature( "Engine" )] public float FlywheelFriction { get; set; } = -6000;
[Property, Feature( "Engine" )] public float FlywheelTorque { get; set; } = 20000;
[Property, Feature( "Engine" )] public float EngineBrakeTorque { get; set; } = 2000;
[Property, Feature( "Engine" )]
public Dictionary<int, float> Gears { get; set; } = new()
{
[-1] = 2.5f,
[0] = 0f,
[1] = 2.8f,
[2] = 1.7f,
[3] = 1.2f,
[4] = 0.9f,
[5] = 0.75f,
[6] = 0.7f
};
[Property, Feature( "Engine" )] public float DifferentialRatio { get; set; } = 1f;
[Property, Feature( "Engine" ), Range( 0, 1 )] public float TransmissionEfficiency { get; set; } = 0.8f;
[Property, Feature( "Engine" )] private float MinRPMTorque { get; set; } = 5000f;
[Property, Feature( "Engine" )] private float MaxRPMTorque { get; set; } = 8000f;
[Sync] public int Gear { get; set; } = 0;
[Sync] public float Clutch { get; set; } = 1;
[Sync( SyncFlags.Interpolate )] public float EngineRPM { get; set; }
public float RPMPercent => (EngineRPM - MinRPM) / MaxRPM;
private const float TAU = MathF.Tau;
private int MinGear { get; set; }
private int MaxGear { get; set; }
[Sync] public bool IsRedlining { get; private set; }
private float flywheelVelocity;
private TimeUntil switchCD = 0;
private float groundedCount;
private float burnout;
private float frontBrake;
private float rearBrake;
private float availableFrontTorque;
private float availableRearTorque;
private float avgSideSlip;
private float avgPoweredRPM;
private float avgForwardSlip;
private float inputThrottle, inputBrake;
private bool inputHandbrake;
private float transmissionRPM;
private float powerDistribution;
public float FlywheelRPM
{
get => flywheelVelocity * 60 / TAU;
set
{
flywheelVelocity = value * TAU / 60; EngineRPM = value;
}
}
private void UpdateGearList()
{
int minGear = 0;
int maxGear = 0;
foreach ( var (gear, ratio) in Gears )
{
if ( gear < minGear )
minGear = gear;
if ( gear > maxGear )
maxGear = gear;
if ( minGear != 0 || maxGear != 0 )
{
SwitchGear( 0, false );
}
}
MinGear = minGear;
MaxGear = maxGear;
}
public void SwitchGear( int index, bool cooldown = true )
{
if ( Gear == index ) return;
index = Math.Clamp( index, MinGear, MaxGear );
if ( index == 0 || !cooldown )
switchCD = 0;
else
switchCD = 0.3f;
Clutch = 1;
Gear = index;
}
public float TransmissionToEngineRPM( int gear ) => avgPoweredRPM * Gears[gear] * DifferentialRatio * 60 / TAU;
public float GetTransmissionMaxRPM( int gear ) => FlywheelRPM / Gears[gear] / DifferentialRatio;
private void UpdatePowerDistribution()
{
if ( Wheels is null ) return;
int frontCount = 0, rearCount = 0;
foreach ( var wheel in Wheels )
{
if ( wheel.IsFront )
frontCount++;
else
rearCount++;
}
float frontDistribution = 0.5f + PowerDistribution * 0.5f;
float rearDistribution = 1 - frontDistribution;
frontDistribution /= frontCount;
rearDistribution /= rearCount;
foreach ( var wheel in Wheels )
if ( wheel.IsFront )
wheel.DistributionFactor = frontDistribution;
else
wheel.DistributionFactor = rearDistribution;
}
private void EngineAccelerate( float torque, float dt )
{
var inertia = 0.5f * FlyWheelMass * FlyWheelRadius * FlyWheelRadius;
var angularAcceleration = torque / inertia;
flywheelVelocity += angularAcceleration * dt;
}
private float GetTransmissionTorque( int gear, float minTorque, float maxTorque )
{
var torque = FlywheelRPM.Remap( MinRPM, MaxRPM, minTorque, maxTorque, true );
torque *= (1 - Clutch);
torque = torque * Gears[gear] * DifferentialRatio * TransmissionEfficiency;
return gear == -1 ? -torque : torque;
}
private void AutoGearSwitch()
{
if ( ForwardSpeed < 100 && Input.Down( "Backward" ) )
{
SwitchGear( -1, false );
return;
}
var currentGear = Gear;
if ( currentGear < 0 && ForwardSpeed < -100 )
return;
if ( Math.Abs( avgForwardSlip ) > 10 )
return;
var gear = Math.Clamp( currentGear, 1, MaxGear );
float minRPM = MinRPM, maxRPM = MaxRPM;
maxRPM *= 0.98f;
float gearRPM;
for ( int i = 1; i <= MaxGear; i++ )
{
gearRPM = TransmissionToEngineRPM( i );
if ( (i == 1 && gearRPM < minRPM) || (gearRPM > minRPM && gearRPM < maxRPM) )
{
gear = i;
break;
}
}
var threshold = minRPM + (maxRPM - minRPM) * (0.5 - Throttle * 0.3);
if ( gear < currentGear && gear > currentGear - 2 && EngineRPM > threshold )
return;
SwitchGear( gear );
}
private float EngineClutch( float dt )
{
if ( !switchCD )
{
inputThrottle = 0;
return 0;
}
if ( inputHandbrake )
return 1;
var absForwardSpeed = Math.Abs( ForwardSpeed );
if ( groundedCount < 1 && absForwardSpeed > 30 )
return 1;
if ( ForwardSpeed < -50 && inputBrake > 0 && Gear < 0 )
return 1;
if ( absForwardSpeed > 200 )
return 0;
return inputThrottle > 0.1f ? 0 : 1;
}
[Property, Feature( "Engine" )] Engine Engine { get; set; }
private void EngineThink( float dt )
{
inputThrottle = Input.Down( "Forward" ) ? 1 : 0;
inputBrake = Input.Down( "Backward" ) ? 1 : 0;
inputHandbrake = Input.Down( "Jump" );
if ( burnout > 0 )
{
SwitchGear( 1, false );
if ( inputThrottle < 0.1f || inputBrake < 0.1f )
burnout = 0;
}
else
AutoGearSwitch();
if ( Gear < 0 )
(inputBrake, inputThrottle) = (inputThrottle, inputBrake);
var rpm = FlywheelRPM;
var clutch = EngineClutch( dt );
if ( inputThrottle > 0.1 && inputBrake > 0.1 && Math.Abs( ForwardSpeed ) < 50 )
{
burnout = MathX.Approach( burnout, 1, dt * 2 );
Clutch = 0;
}
else if ( inputHandbrake )
{
frontBrake = 0f;
rearBrake = 0.5f;
Clutch = 1;
clutch = 1;
}
else
{
if ( (Gear == -1 || Gear == 1) && inputThrottle < 0.05f && inputBrake < 0.1f && groundedCount > 1 && rpm < MinRPM * 1.2f )
inputBrake = 0.2f;
frontBrake = inputBrake * 0.5f;
rearBrake = inputBrake * 0.5f;
}
clutch = MathX.Approach( Clutch, clutch, dt * ((Gear < 2 && inputThrottle > 0.1f) ? 6 : 2) );
Clutch = clutch;
var isRedlining = false;
transmissionRPM = 0;
if ( Gear != 0 )
{
transmissionRPM = TransmissionToEngineRPM( Gear );
transmissionRPM = Gear < 0 ? -transmissionRPM : transmissionRPM;
rpm = (rpm * clutch) + (MathF.Max( 0, transmissionRPM ) * (1 - clutch));
}
var throttle = Throttle;
var gearTorque = GetTransmissionTorque( Gear, MinRPMTorque, MaxRPMTorque );
var availableTorque = gearTorque * throttle;
if ( transmissionRPM < 0 )
{
availableTorque += gearTorque * 2;
}
else
{
var engineBrakeTorque = GetTransmissionTorque( Gear, EngineBrakeTorque, EngineBrakeTorque );
availableTorque -= engineBrakeTorque * (1 - throttle) * 0.5f;
}
var maxRPM = MaxRPM;
if ( rpm < MinRPM )
{
rpm = MinRPM;
}
else if ( rpm > maxRPM )
{
if ( rpm > maxRPM * 1.2f )
availableTorque = 0;
rpm = maxRPM;
if ( Gear != MaxGear || groundedCount < Wheels.Count )
isRedlining = true;
}
FlywheelRPM = Math.Clamp( rpm, 0, maxRPM );
if ( burnout > 0 )
availableTorque += availableTorque * burnout * 0.1f;
var front = 0.5f + PowerDistribution * 0.5f;
var rear = 1 - front;
availableFrontTorque = availableTorque * front;
availableRearTorque = availableTorque * rear;
throttle = MathX.Approach( throttle, inputThrottle, dt * 4 );
EngineAccelerate( FlywheelFriction + FlywheelTorque * throttle, dt );
Throttle = throttle;
IsRedlining = (isRedlining && inputThrottle > 0);
Engine.Throttle = Input.Down( "Forward" ) ? 1 : 0;
Engine.ForwardStep( 0, 0 );
}
//[Property, Feature( "Engine" ), Sync] public EngineState EngineState { get; set; }
//[Property, Feature( "Engine" )] public EngineStream Stream { get; set; }
//public EngineStreamPlayer StreamPlayer { get; set; }
//[Property, Feature( "Engine" )] public float MinRPM { get; set; } = 800;
//[Property, Feature( "Engine" )] public float MaxRPM { get; set; } = 7000;
//[Property, Feature( "Engine" ), Range( -1, 1 )]
//public float PowerDistribution
//{
// get => powerDistribution; set
// {
// powerDistribution = value;
// UpdatePowerDistribution();
// }
//}
//[Property, Feature( "Engine" )] public float FlyWheelMass { get; set; } = 80f;
//[Property, Feature( "Engine" )] public float FlyWheelRadius { get; set; } = 0.5f;
//[Property, Feature( "Engine" )] public float FlywheelFriction { get; set; } = -6000;
//[Property, Feature( "Engine" )] public float FlywheelTorque { get; set; } = 20000;
//[Property, Feature( "Engine" )] public float EngineBrakeTorque { get; set; } = 2000;
//[Property, Feature( "Engine" )]
//public Dictionary<int, float> Gears { get; set; } = new()
//{
// [-1] = 2.5f,
// [0] = 0f,
// [1] = 2.8f,
// [2] = 1.7f,
// [3] = 1.2f,
// [4] = 0.9f,
// [5] = 0.75f,
// [6] = 0.7f
//};
//[Property, Feature( "Engine" )] public float DifferentialRatio { get; set; } = 1f;
//[Property, Feature( "Engine" ), Range( 0, 1 )] public float TransmissionEfficiency { get; set; } = 0.8f;
//[Property, Feature( "Engine" )] private float MinRPMTorque { get; set; } = 5000f;
//[Property, Feature( "Engine" )] private float MaxRPMTorque { get; set; } = 8000f;
//[Sync] public int Gear { get; set; } = 0;
//[Sync] public float Clutch { get; set; } = 1;
//[Sync( SyncFlags.Interpolate )] public float EngineRPM { get; set; }
//public float RPMPercent => (EngineRPM - MinRPM) / MaxRPM;
//private const float TAU = MathF.Tau;
//private int MinGear { get; set; }
//private int MaxGear { get; set; }
//[Sync] public bool IsRedlining { get; private set; }
//private float flywheelVelocity;
//private TimeUntil switchCD = 0;
//private float groundedCount;
//private float burnout;
//private float frontBrake;
//private float rearBrake;
//private float availableFrontTorque;
//private float availableRearTorque;
//private float avgSideSlip;
//private float avgPoweredRPM;
//private float avgForwardSlip;
//private float inputThrottle, inputBrake;
//private bool inputHandbrake;
//private float transmissionRPM;
//private float powerDistribution;
//public float FlywheelRPM
//{
// get => flywheelVelocity * 60 / TAU;
// set
// {
// flywheelVelocity = value * TAU / 60; EngineRPM = value;
// }
//}
//private void UpdateGearList()
//{
// int minGear = 0;
// int maxGear = 0;
// foreach ( var (gear, ratio) in Gears )
// {
// if ( gear < minGear )
// minGear = gear;
// if ( gear > maxGear )
// maxGear = gear;
// if ( minGear != 0 || maxGear != 0 )
// {
// SwitchGear( 0, false );
// }
// }
// MinGear = minGear;
// MaxGear = maxGear;
//}
//public void SwitchGear( int index, bool cooldown = true )
//{
// if ( Gear == index ) return;
// index = Math.Clamp( index, MinGear, MaxGear );
// if ( index == 0 || !cooldown )
// switchCD = 0;
// else
// switchCD = 0.3f;
// Clutch = 1;
// Gear = index;
//}
//public float TransmissionToEngineRPM( int gear ) => avgPoweredRPM * Gears[gear] * DifferentialRatio * 60 / TAU;
//public float GetTransmissionMaxRPM( int gear ) => FlywheelRPM / Gears[gear] / DifferentialRatio;
//private void UpdatePowerDistribution()
//{
// if ( Wheels is null ) return;
// int frontCount = 0, rearCount = 0;
// foreach ( var wheel in Wheels )
// {
// if ( wheel.IsFront )
// frontCount++;
// else
// rearCount++;
// }
// float frontDistribution = 0.5f + PowerDistribution * 0.5f;
// float rearDistribution = 1 - frontDistribution;
// frontDistribution /= frontCount;
// rearDistribution /= rearCount;
// foreach ( var wheel in Wheels )
// if ( wheel.IsFront )
// wheel.DistributionFactor = frontDistribution;
// else
// wheel.DistributionFactor = rearDistribution;
//}
//private void EngineAccelerate( float torque, float dt )
//{
// var inertia = 0.5f * FlyWheelMass * FlyWheelRadius * FlyWheelRadius;
// var angularAcceleration = torque / inertia;
// flywheelVelocity += angularAcceleration * dt;
//}
//private float GetTransmissionTorque( int gear, float minTorque, float maxTorque )
//{
// var torque = FlywheelRPM.Remap( MinRPM, MaxRPM, minTorque, maxTorque, true );
// torque *= (1 - Clutch);
// torque = torque * Gears[gear] * DifferentialRatio * TransmissionEfficiency;
// return gear == -1 ? -torque : torque;
//}
//private void AutoGearSwitch()
//{
// if ( ForwardSpeed < 100 && Input.Down( "Backward" ) )
// {
// SwitchGear( -1, false );
// return;
// }
// var currentGear = Gear;
// if ( currentGear < 0 && ForwardSpeed < -100 )
// return;
// if ( Math.Abs( avgForwardSlip ) > 10 )
// return;
// var gear = Math.Clamp( currentGear, 1, MaxGear );
// float minRPM = MinRPM, maxRPM = MaxRPM;
// maxRPM *= 0.98f;
// float gearRPM;
// for ( int i = 1; i <= MaxGear; i++ )
// {
// gearRPM = TransmissionToEngineRPM( i );
// if ( (i == 1 && gearRPM < minRPM) || (gearRPM > minRPM && gearRPM < maxRPM) )
// {
// gear = i;
// break;
// }
// }
// var threshold = minRPM + (maxRPM - minRPM) * (0.5 - Throttle * 0.3);
// if ( gear < currentGear && gear > currentGear - 2 && EngineRPM > threshold )
// return;
// SwitchGear( gear );
//}
//private float EngineClutch( float dt )
//{
// if ( !switchCD )
// {
// inputThrottle = 0;
// return 0;
// }
// if ( inputHandbrake )
// return 1;
// var absForwardSpeed = Math.Abs( ForwardSpeed );
// if ( groundedCount < 1 && absForwardSpeed > 30 )
// return 1;
// if ( ForwardSpeed < -50 && inputBrake > 0 && Gear < 0 )
// return 1;
// if ( absForwardSpeed > 200 )
// return 0;
// return inputThrottle > 0.1f ? 0 : 1;
//}
//private void EngineThink( float dt )
//{
// inputThrottle = Input.Down( "Forward" ) ? 1 : 0;
// inputBrake = Input.Down( "Backward" ) ? 1 : 0;
// inputHandbrake = Input.Down( "Jump" );
// if ( burnout > 0 )
// {
// SwitchGear( 1, false );
// if ( inputThrottle < 0.1f || inputBrake < 0.1f )
// burnout = 0;
// }
// else
// AutoGearSwitch();
// if ( Gear < 0 )
// (inputBrake, inputThrottle) = (inputThrottle, inputBrake);
// var rpm = FlywheelRPM;
// var clutch = EngineClutch( dt );
// if ( inputThrottle > 0.1 && inputBrake > 0.1 && Math.Abs( ForwardSpeed ) < 50 )
// {
// burnout = MathX.Approach( burnout, 1, dt * 2 );
// Clutch = 0;
// }
// else if ( inputHandbrake )
// {
// frontBrake = 0f;
// rearBrake = 0.5f;
// Clutch = 1;
// clutch = 1;
// }
// else
// {
// if ( (Gear == -1 || Gear == 1) && inputThrottle < 0.05f && inputBrake < 0.1f && groundedCount > 1 && rpm < MinRPM * 1.2f )
// inputBrake = 0.2f;
// frontBrake = inputBrake * 0.5f;
// rearBrake = inputBrake * 0.5f;
// }
// clutch = MathX.Approach( Clutch, clutch, dt * ((Gear < 2 && inputThrottle > 0.1f) ? 6 : 2) );
// Clutch = clutch;
// var isRedlining = false;
// transmissionRPM = 0;
// if ( Gear != 0 )
// {
// transmissionRPM = TransmissionToEngineRPM( Gear );
// transmissionRPM = Gear < 0 ? -transmissionRPM : transmissionRPM;
// rpm = (rpm * clutch) + (MathF.Max( 0, transmissionRPM ) * (1 - clutch));
// }
// var throttle = Throttle;
// var gearTorque = GetTransmissionTorque( Gear, MinRPMTorque, MaxRPMTorque );
// var availableTorque = gearTorque * throttle;
// if ( transmissionRPM < 0 )
// {
// availableTorque += gearTorque * 2;
// }
// else
// {
// var engineBrakeTorque = GetTransmissionTorque( Gear, EngineBrakeTorque, EngineBrakeTorque );
// availableTorque -= engineBrakeTorque * (1 - throttle) * 0.5f;
// }
// var maxRPM = MaxRPM;
// if ( rpm < MinRPM )
// {
// rpm = MinRPM;
// }
// else if ( rpm > maxRPM )
// {
// if ( rpm > maxRPM * 1.2f )
// availableTorque = 0;
// rpm = maxRPM;
// if ( Gear != MaxGear || groundedCount < Wheels.Count )
// isRedlining = true;
// }
// FlywheelRPM = Math.Clamp( rpm, 0, maxRPM );
// if ( burnout > 0 )
// availableTorque += availableTorque * burnout * 0.1f;
// var front = 0.5f + PowerDistribution * 0.5f;
// var rear = 1 - front;
// availableFrontTorque = availableTorque * front;
// availableRearTorque = availableTorque * rear;
// throttle = MathX.Approach( throttle, inputThrottle, dt * 4 );
// EngineAccelerate( FlywheelFriction + FlywheelTorque * throttle, dt );
// Throttle = throttle;
// IsRedlining = (isRedlining && inputThrottle > 0);
//}
}

View File

@@ -22,7 +22,7 @@ public partial class VeloXCar
var inputSteer = Input.AnalogMove.y;
var absInputSteer = Math.Abs( inputSteer );
var sideSlip = Math.Clamp( avgSideSlip, -1, 1 );
var sideSlip = Math.Clamp( 0, -1, 1 );
var steerConeFactor = Math.Clamp( TotalSpeed / SteerConeMaxSpeed, 0, 1 );
var steerCone = 1 - steerConeFactor * (1 - SteerConeMaxAngle);

View File

@@ -2,17 +2,11 @@
public partial class VeloXCar
{
private float avgSideSlip;
private float avgPoweredRPM;
private float avgForwardSlip;
private void WheelThink( in float dt )
{
var maxRPM = GetTransmissionMaxRPM( Gear );
var frontTorque = availableFrontTorque;
var rearTorque = availableRearTorque;
groundedCount = 0;
float avgRPM = 0, totalSideSlip = 0, totalForwardSlip = 0;
foreach ( var w in Wheels )
@@ -24,20 +18,8 @@ public partial class VeloXCar
var rpm = w.RPM;
avgRPM += rpm * w.DistributionFactor;
w.Torque = w.DistributionFactor * (w.IsFront ? frontTorque : rearTorque);
w.Brake = w.IsFront ? frontBrake : rearBrake;
if ( inputHandbrake && !w.IsFront )
w.RPM = 0;
if ( rpm > maxRPM )
w.RPM = maxRPM;
if ( w.IsOnGround )
groundedCount++;
}
avgPoweredRPM = avgRPM;
avgSideSlip = totalSideSlip / Wheels.Count;
avgForwardSlip = totalForwardSlip / Wheels.Count;

View File

@@ -10,27 +10,27 @@ public partial class VeloXCar : VeloXBase
protected override void OnStart()
{
base.OnStart();
StreamPlayer = new( Stream );
if ( IsDriver )
{
UpdateGearList();
UpdatePowerDistribution();
}
//StreamPlayer = new( Stream );
//if ( IsDriver )
//{
// UpdateGearList();
// UpdatePowerDistribution();
//}
}
protected override void OnUpdate()
{
base.OnUpdate();
if ( StreamPlayer is not null )
{
//if ( StreamPlayer is not null )
//{
StreamPlayer.Throttle = Throttle;
StreamPlayer.RPMPercent = RPMPercent;
StreamPlayer.EngineState = EngineState;
StreamPlayer.IsRedlining = IsRedlining;
// StreamPlayer.Throttle = Throttle;
// StreamPlayer.RPMPercent = RPMPercent;
// StreamPlayer.EngineState = EngineState;
// StreamPlayer.IsRedlining = IsRedlining;
StreamPlayer.Update( Time.Delta, WorldPosition );
}
// StreamPlayer.Update( Time.Delta, WorldPosition );
//}
}
protected override void OnFixedUpdate()
@@ -40,9 +40,10 @@ public partial class VeloXCar : VeloXBase
base.OnFixedUpdate();
Brake = Math.Clamp( frontBrake + rearBrake + (Input.Down( "Jump" ) ? 1 : 0), 0, 1 );
Brake = Math.Clamp( (Input.Down( "Jump" ) ? 1 : 0), 0, 1 );
var dt = Time.Delta;
EngineThink( dt );
WheelThink( dt );
UpdateSteering( dt );