Compare commits
8 Commits
558a1eda07
...
main
| Author | SHA1 | Date | |
|---|---|---|---|
| e22e463f11 | |||
| 66fcc6a2bb | |||
| e12b75be45 | |||
| 68626640c2 | |||
| b02f14ee47 | |||
| b978b821fa | |||
| 562750107a | |||
| 6cb8f716b3 |
@@ -12,6 +12,7 @@
|
|||||||
"NetworkMode": 2,
|
"NetworkMode": 2,
|
||||||
"NetworkInterpolation": true,
|
"NetworkInterpolation": true,
|
||||||
"NetworkOrphaned": 0,
|
"NetworkOrphaned": 0,
|
||||||
|
"NetworkTransmit": true,
|
||||||
"OwnerTransfer": 1,
|
"OwnerTransfer": 1,
|
||||||
"Components": [],
|
"Components": [],
|
||||||
"Children": [
|
"Children": [
|
||||||
@@ -28,6 +29,7 @@
|
|||||||
"NetworkMode": 2,
|
"NetworkMode": 2,
|
||||||
"NetworkInterpolation": true,
|
"NetworkInterpolation": true,
|
||||||
"NetworkOrphaned": 0,
|
"NetworkOrphaned": 0,
|
||||||
|
"NetworkTransmit": true,
|
||||||
"OwnerTransfer": 1,
|
"OwnerTransfer": 1,
|
||||||
"Components": [
|
"Components": [
|
||||||
{
|
{
|
||||||
@@ -197,7 +199,7 @@
|
|||||||
"FaceVelocity": false,
|
"FaceVelocity": false,
|
||||||
"FogStrength": 1,
|
"FogStrength": 1,
|
||||||
"LeadingTrail": true,
|
"LeadingTrail": true,
|
||||||
"Lighting": false,
|
"Lighting": true,
|
||||||
"MotionBlur": false,
|
"MotionBlur": false,
|
||||||
"OnComponentDestroy": null,
|
"OnComponentDestroy": null,
|
||||||
"OnComponentDisabled": null,
|
"OnComponentDisabled": null,
|
||||||
@@ -206,6 +208,7 @@
|
|||||||
"OnComponentStart": null,
|
"OnComponentStart": null,
|
||||||
"OnComponentUpdate": null,
|
"OnComponentUpdate": null,
|
||||||
"Opaque": false,
|
"Opaque": false,
|
||||||
|
"PlaybackSpeed": 1,
|
||||||
"RenderOptions": {
|
"RenderOptions": {
|
||||||
"GameLayer": true,
|
"GameLayer": true,
|
||||||
"OverlayLayer": false,
|
"OverlayLayer": false,
|
||||||
@@ -215,7 +218,7 @@
|
|||||||
"RotationOffset": 0,
|
"RotationOffset": 0,
|
||||||
"Scale": 1,
|
"Scale": 1,
|
||||||
"Shadows": true,
|
"Shadows": true,
|
||||||
"SortMode": "Unsorted",
|
"SortMode": "ByDistance",
|
||||||
"Sprite": {
|
"Sprite": {
|
||||||
"$compiler": "embed",
|
"$compiler": "embed",
|
||||||
"$source": null,
|
"$source": null,
|
||||||
@@ -228,7 +231,8 @@
|
|||||||
"LoopMode": "Loop",
|
"LoopMode": "Loop",
|
||||||
"Frames": [
|
"Frames": [
|
||||||
{
|
{
|
||||||
"Texture": "textures/smoketexturesheet.vtex"
|
"Texture": "textures/smoketexturesheet.vtex",
|
||||||
|
"BroadcastMessages": []
|
||||||
}
|
}
|
||||||
]
|
]
|
||||||
}
|
}
|
||||||
|
|||||||
8
Assets/sounds/tire/skid.wav.meta
Normal file
8
Assets/sounds/tire/skid.wav.meta
Normal file
@@ -0,0 +1,8 @@
|
|||||||
|
{
|
||||||
|
"loop": false,
|
||||||
|
"start": 0,
|
||||||
|
"end": 0,
|
||||||
|
"rate": 44100,
|
||||||
|
"compress": false,
|
||||||
|
"bitrate": 256
|
||||||
|
}
|
||||||
@@ -117,7 +117,7 @@ public partial class Clutch : PowertrainComponent
|
|||||||
ClutchInput = 1f;
|
ClutchInput = 1f;
|
||||||
}
|
}
|
||||||
|
|
||||||
if ( Controller.SwappedBrakes > 0 )
|
if ( Controller.SwappedBrakes > 0 && Controller.SwappedThrottle == 0 )
|
||||||
{
|
{
|
||||||
ClutchInput = 0;
|
ClutchInput = 0;
|
||||||
}
|
}
|
||||||
@@ -173,7 +173,7 @@ public partial class Clutch : PowertrainComponent
|
|||||||
OutputInertia = (inertiaSum + halfClutchInertia) * _clutchEngagement + halfClutchInertia;
|
OutputInertia = (inertiaSum + halfClutchInertia) * _clutchEngagement + halfClutchInertia;
|
||||||
|
|
||||||
// Allow the torque output to be only up to the slip torque valu
|
// Allow the torque output to be only up to the slip torque valu
|
||||||
float outputTorqueClamp = SlipTorque * _clutchEngagement;
|
float outputTorqueClamp = Controller.Engine.EstimatedPeakTorque * 1.5f * _clutchEngagement;
|
||||||
|
|
||||||
OutputTorque = InputTorque;
|
OutputTorque = InputTorque;
|
||||||
OutputTorque = Math.Clamp( OutputTorque, 0, outputTorqueClamp );
|
OutputTorque = Math.Clamp( OutputTorque, 0, outputTorqueClamp );
|
||||||
|
|||||||
@@ -78,7 +78,7 @@ public class Engine : PowertrainComponent, IScenePhysicsEvents
|
|||||||
/// </summary>
|
/// </summary>
|
||||||
[Property] public bool FlyingStartEnabled { get; set; }
|
[Property] public bool FlyingStartEnabled { get; set; }
|
||||||
|
|
||||||
[Property] public bool Ignition { get; set; }
|
[Property] public bool Ignition { get; private set; }
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Power curve with RPM range [0,1] on the X axis and power coefficient [0,1] on Y axis.
|
/// Power curve with RPM range [0,1] on the X axis and power coefficient [0,1] on Y axis.
|
||||||
@@ -225,13 +225,21 @@ public class Engine : PowertrainComponent, IScenePhysicsEvents
|
|||||||
if ( FlyingStartEnabled )
|
if ( FlyingStartEnabled )
|
||||||
{
|
{
|
||||||
FlyingStart();
|
FlyingStart();
|
||||||
|
IsRunning = true;
|
||||||
|
IsActive = true;
|
||||||
}
|
}
|
||||||
else if ( !StarterActive && Controller != null )
|
else if ( !StarterActive && Controller != null )
|
||||||
{
|
{
|
||||||
StarterCoroutine();
|
StarterCoroutine();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
IsRunning = true;
|
||||||
|
IsActive = true;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private async void StarterCoroutine()
|
private async void StarterCoroutine()
|
||||||
{
|
{
|
||||||
if ( Type == EngineType.Electric || StarterActive )
|
if ( Type == EngineType.Electric || StarterActive )
|
||||||
@@ -251,6 +259,11 @@ public class Engine : PowertrainComponent, IScenePhysicsEvents
|
|||||||
{
|
{
|
||||||
startTimer += 0.1f;
|
startTimer += 0.1f;
|
||||||
await Task.DelaySeconds( 0.1f );
|
await Task.DelaySeconds( 0.1f );
|
||||||
|
|
||||||
|
if ( OutputAngularVelocity >= _idleAngularVelocity * 0.8f )
|
||||||
|
{
|
||||||
|
break;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
finally
|
finally
|
||||||
@@ -258,6 +271,7 @@ public class Engine : PowertrainComponent, IScenePhysicsEvents
|
|||||||
_starterTorque = 0;
|
_starterTorque = 0;
|
||||||
StarterActive = false;
|
StarterActive = false;
|
||||||
IsActive = true;
|
IsActive = true;
|
||||||
|
IsRunning = true;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -267,12 +281,15 @@ public class Engine : PowertrainComponent, IScenePhysicsEvents
|
|||||||
Ignition = true;
|
Ignition = true;
|
||||||
StarterActive = false;
|
StarterActive = false;
|
||||||
OutputAngularVelocity = IdleRPM.RPMToAngularVelocity();
|
OutputAngularVelocity = IdleRPM.RPMToAngularVelocity();
|
||||||
|
IsRunning = true;
|
||||||
|
IsActive = true;
|
||||||
}
|
}
|
||||||
|
|
||||||
public void StopEngine()
|
public void StopEngine()
|
||||||
{
|
{
|
||||||
Ignition = false;
|
Ignition = false;
|
||||||
IsActive = true;
|
IsRunning = false;
|
||||||
|
IsActive = false;
|
||||||
OnEngineStop?.Invoke();
|
OnEngineStop?.Invoke();
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -308,11 +325,6 @@ public class Engine : PowertrainComponent, IScenePhysicsEvents
|
|||||||
_revLimiterAngularVelocity = RevLimiterRPM.RPMToAngularVelocity();
|
_revLimiterAngularVelocity = RevLimiterRPM.RPMToAngularVelocity();
|
||||||
if ( _revLimiterAngularVelocity == 0f )
|
if ( _revLimiterAngularVelocity == 0f )
|
||||||
return;
|
return;
|
||||||
// Check for start on throttle
|
|
||||||
|
|
||||||
if ( !IsRunning && !StarterActive && AutoStartOnThrottle && ThrottlePosition > 0.2f )
|
|
||||||
StartEngine();
|
|
||||||
|
|
||||||
|
|
||||||
bool wasRunning = IsRunning;
|
bool wasRunning = IsRunning;
|
||||||
IsRunning = Ignition;
|
IsRunning = Ignition;
|
||||||
@@ -341,6 +353,7 @@ public class Engine : PowertrainComponent, IScenePhysicsEvents
|
|||||||
// Calculate/get torque returned from wheels
|
// Calculate/get torque returned from wheels
|
||||||
|
|
||||||
OutputTorque = generatedTorque - reactionTorque;
|
OutputTorque = generatedTorque - reactionTorque;
|
||||||
|
|
||||||
float returnTorque = ForwardStep( OutputTorque, 0, dt );
|
float returnTorque = ForwardStep( OutputTorque, 0, dt );
|
||||||
|
|
||||||
float totalTorque = generatedTorque + returnTorque + reactionTorque;
|
float totalTorque = generatedTorque + returnTorque + reactionTorque;
|
||||||
@@ -454,7 +467,7 @@ public class Engine : PowertrainComponent, IScenePhysicsEvents
|
|||||||
// if the idle RPM is below the target RPM.
|
// if the idle RPM is below the target RPM.
|
||||||
float idleCorrection = _idleAngularVelocity * 1.08f - OutputAngularVelocity;
|
float idleCorrection = _idleAngularVelocity * 1.08f - OutputAngularVelocity;
|
||||||
idleCorrection = idleCorrection < 0f ? 0f : idleCorrection;
|
idleCorrection = idleCorrection < 0f ? 0f : idleCorrection;
|
||||||
float idleThrottlePosition = Math.Clamp( idleCorrection * 0.01f, 0, 1 );
|
float idleThrottlePosition = Math.Clamp( idleCorrection * 0.01f, 0, 1f );
|
||||||
ThrottlePosition = Math.Max( _userThrottleInput, idleThrottlePosition );
|
ThrottlePosition = Math.Max( _userThrottleInput, idleThrottlePosition );
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -15,7 +15,7 @@ public class Transmission : PowertrainComponent
|
|||||||
/// <summary>
|
/// <summary>
|
||||||
/// A class representing a single ground surface type.
|
/// A class representing a single ground surface type.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
public partial class TransmissionGearingProfile
|
public class TransmissionGearingProfile
|
||||||
{
|
{
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// List of forward gear ratios starting from 1st forward gear.
|
/// List of forward gear ratios starting from 1st forward gear.
|
||||||
|
|||||||
@@ -30,7 +30,6 @@ public abstract partial class VeloXBase
|
|||||||
|
|
||||||
Engine.Controller = this;
|
Engine.Controller = this;
|
||||||
Engine.Inertia = 0.25f;
|
Engine.Inertia = 0.25f;
|
||||||
Engine.Ignition = false;
|
|
||||||
|
|
||||||
if ( !Clutch.IsValid() )
|
if ( !Clutch.IsValid() )
|
||||||
Clutch = new GameObject( Engine.GameObject, true, "Clutch" ).GetOrAddComponent<Clutch>();
|
Clutch = new GameObject( Engine.GameObject, true, "Clutch" ).GetOrAddComponent<Clutch>();
|
||||||
|
|||||||
@@ -4,10 +4,12 @@ namespace VeloX;
|
|||||||
|
|
||||||
public abstract partial class VeloXBase
|
public abstract partial class VeloXBase
|
||||||
{
|
{
|
||||||
[Feature( "Input" )] internal InputResolver Input { get; set; } = new();
|
internal readonly InputResolver Input = new();
|
||||||
[Feature( "Input" )] public Connection Driver { get => Input.Driver; set => Input.Driver = value; }
|
|
||||||
|
|
||||||
|
public Guid DriverId { get; set; }
|
||||||
|
public Connection Driver => Connection.Find( DriverId );
|
||||||
|
|
||||||
|
public bool IsDriver => Connection.Local == Driver;
|
||||||
private bool IsDriverActive => Driver is not null;
|
private bool IsDriverActive => Driver is not null;
|
||||||
|
|
||||||
public Vector2 MouseDelta => IsDriverActive ? Input.MouseDelta : default;
|
public Vector2 MouseDelta => IsDriverActive ? Input.MouseDelta : default;
|
||||||
@@ -34,6 +36,8 @@ public abstract partial class VeloXBase
|
|||||||
public float SwappedBrakes => IsInputSwapped ? Throttle : Brakes;
|
public float SwappedBrakes => IsInputSwapped ? Throttle : Brakes;
|
||||||
|
|
||||||
|
|
||||||
|
public bool AnyInput => Throttle > 0 || Brakes > 0;
|
||||||
|
|
||||||
[Sync]
|
[Sync]
|
||||||
public float VerticalInput
|
public float VerticalInput
|
||||||
{
|
{
|
||||||
@@ -106,15 +110,26 @@ public abstract partial class VeloXBase
|
|||||||
}
|
}
|
||||||
protected void UpdateInput()
|
protected void UpdateInput()
|
||||||
{
|
{
|
||||||
VerticalInput = Input.AnalogMove.x;
|
//VerticalInput = Input.AnalogMove.x;
|
||||||
Handbrake = Input.Down( "Handbrake" ) ? 1 : 0;
|
if ( IsDriverActive )
|
||||||
|
{
|
||||||
|
|
||||||
SteeringAngle = Input.AnalogMove.y;
|
Brakes = Input.Brake;
|
||||||
|
Throttle = Input.Throttle;
|
||||||
|
|
||||||
IsClutching = Input.Down( "Clutch" ) ? 1 : 0;
|
Handbrake = Input.Down( "Handbrake" ) ? 1 : 0;
|
||||||
|
|
||||||
IsShiftingUp = Input.Pressed( "Shift Up" );
|
SteeringAngle = Input.AnalogMove.y;
|
||||||
IsShiftingDown = Input.Pressed( "Shift Down" );
|
|
||||||
|
IsClutching = Input.Down( "Clutch" ) ? 1 : 0;
|
||||||
|
|
||||||
|
IsShiftingUp = Input.Pressed( "Shift Up" );
|
||||||
|
IsShiftingDown = Input.Pressed( "Shift Down" );
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
ResetInput();
|
||||||
|
}
|
||||||
|
|
||||||
if ( TotalSpeed < 150 && Driver is null )
|
if ( TotalSpeed < 150 && Driver is null )
|
||||||
Handbrake = 1;
|
Handbrake = 1;
|
||||||
|
|||||||
@@ -5,7 +5,6 @@ namespace VeloX;
|
|||||||
public abstract partial class VeloXBase
|
public abstract partial class VeloXBase
|
||||||
{
|
{
|
||||||
|
|
||||||
private Vector3 linForce;
|
|
||||||
private Vector3 angForce;
|
private Vector3 angForce;
|
||||||
protected const float BrakeForce = 4500f;
|
protected const float BrakeForce = 4500f;
|
||||||
protected const float HandbrakeForce = 35000f;
|
protected const float HandbrakeForce = 35000f;
|
||||||
@@ -13,18 +12,16 @@ public abstract partial class VeloXBase
|
|||||||
{
|
{
|
||||||
if ( Body.Sleeping && Input.AnalogMove.x == 0 )
|
if ( Body.Sleeping && Input.AnalogMove.x == 0 )
|
||||||
return;
|
return;
|
||||||
|
//Body.PhysicsBody.SetInertiaTensor( new Vector3( 800000, 3000000, 6000000 ), Rotation.Identity );
|
||||||
|
|
||||||
var drag = AngularDrag;
|
var drag = AngularDrag;
|
||||||
var mass = Body.Mass;
|
var mass = Body.Mass;
|
||||||
var angVel = Body.AngularVelocity;
|
var angVel = Body.AngularVelocity;
|
||||||
|
|
||||||
linForce.x = 0;
|
|
||||||
linForce.y = 0;
|
|
||||||
linForce.z = 0;
|
|
||||||
|
|
||||||
angForce = angForce.WithX( angVel.x * drag.x * mass * 1000 );
|
angForce = angForce.WithX( angVel.x * drag.x * mass * 1000 );
|
||||||
angForce = angForce.WithY( angVel.y * drag.y * mass * 1000 );
|
angForce = angForce.WithY( angVel.y * drag.y * mass * 1000 );
|
||||||
angForce = angForce.WithZ( angVel.z * drag.z * mass * 1000 );
|
angForce = angForce.WithZ( angVel.z * drag.z * mass * 1000 );
|
||||||
|
|
||||||
if ( Wheels.Count > 0 )
|
if ( Wheels.Count > 0 )
|
||||||
{
|
{
|
||||||
Vector3 vehVel = Body.Velocity;
|
Vector3 vehVel = Body.Velocity;
|
||||||
@@ -47,23 +44,19 @@ public abstract partial class VeloXBase
|
|||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
if ( TotalSpeed < 1 && Input.AnalogMove.x == 0 )
|
if ( TotalSpeed < 1 && !AnyInput )
|
||||||
{
|
{
|
||||||
v.BrakeTorque = HandbrakeForce;
|
v.BrakeTorque = HandbrakeForce;
|
||||||
}
|
}
|
||||||
|
|
||||||
v.Update( this, in dt );
|
|
||||||
v.DoPhysics( in dt );
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
Body.Velocity = vehVel;
|
Body.Velocity = vehVel;
|
||||||
Body.AngularVelocity = vehAngVel;
|
Body.AngularVelocity = vehAngVel;
|
||||||
}
|
}
|
||||||
|
|
||||||
Body.ApplyForce( linForce );
|
//Body.ApplyForce( linForce );
|
||||||
Body.ApplyTorque( angForce );
|
//Body.ApplyTorque( angForce );
|
||||||
|
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,13 +1,23 @@
|
|||||||
using Sandbox;
|
using Sandbox;
|
||||||
|
using System;
|
||||||
|
|
||||||
namespace VeloX;
|
namespace VeloX;
|
||||||
|
|
||||||
|
|
||||||
public abstract partial class VeloXBase : Component
|
public abstract partial class VeloXBase : Component, IGameObjectNetworkEvents
|
||||||
{
|
{
|
||||||
[Sync] public WaterState WaterState { get; set; }
|
[Sync, Change( nameof( OnEngineIgnitionChange ) )]
|
||||||
[Sync] public bool IsEngineOnFire { get; set; }
|
public bool EngineIgnition { get; set; }
|
||||||
[Property, Sync] public EngineState EngineState { get; set; }
|
|
||||||
|
|
||||||
|
private void OnEngineIgnitionChange( bool oldvalue, bool newvalue )
|
||||||
|
{
|
||||||
|
if ( newvalue )
|
||||||
|
Engine?.StartEngine();
|
||||||
|
else
|
||||||
|
Engine?.StopEngine();
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
[Property] public Vector3 AngularDrag { get; set; } = new( -0.1f, -0.1f, -3 );
|
[Property] public Vector3 AngularDrag { get; set; } = new( -0.1f, -0.1f, -3 );
|
||||||
[Property] public float Mass { get; set; } = 900;
|
[Property] public float Mass { get; set; } = 900;
|
||||||
@@ -16,18 +26,24 @@ public abstract partial class VeloXBase : Component
|
|||||||
|
|
||||||
[Sync( SyncFlags.Interpolate )] public Angles SteerAngle { get; set; }
|
[Sync( SyncFlags.Interpolate )] public Angles SteerAngle { get; set; }
|
||||||
|
|
||||||
public Vector3 LocalVelocity;
|
[Sync( SyncFlags.Interpolate )] public Vector3 LocalVelocity { get; set; }
|
||||||
|
[Sync( SyncFlags.Interpolate )] public Vector3 Velocity { get; set; }
|
||||||
public float ForwardSpeed;
|
public float ForwardSpeed;
|
||||||
public float TotalSpeed;
|
public float TotalSpeed;
|
||||||
|
|
||||||
protected override void OnFixedUpdate()
|
protected override void OnFixedUpdate()
|
||||||
{
|
{
|
||||||
|
if ( !IsProxy )
|
||||||
|
{
|
||||||
|
LocalVelocity = WorldTransform.PointToLocal( WorldPosition + Body.Velocity );
|
||||||
|
Velocity = Body.Velocity;
|
||||||
|
}
|
||||||
|
ForwardSpeed = LocalVelocity.x;
|
||||||
|
|
||||||
|
TotalSpeed = LocalVelocity.Length;
|
||||||
|
|
||||||
if ( IsProxy )
|
if ( IsProxy )
|
||||||
return;
|
return;
|
||||||
|
|
||||||
LocalVelocity = WorldTransform.PointToLocal( WorldPosition + Body.Velocity );
|
|
||||||
ForwardSpeed = LocalVelocity.x;
|
|
||||||
TotalSpeed = LocalVelocity.Length;
|
|
||||||
Body.PhysicsBody.Mass = Mass;
|
Body.PhysicsBody.Mass = Mass;
|
||||||
|
|
||||||
FixedUpdate();
|
FixedUpdate();
|
||||||
|
|||||||
@@ -1,11 +1,16 @@
|
|||||||
namespace VeloX;
|
using System;
|
||||||
|
|
||||||
|
namespace VeloX;
|
||||||
|
|
||||||
public struct Friction
|
public struct Friction
|
||||||
{
|
{
|
||||||
public float SlipCoef { get; set; }
|
|
||||||
public float ForceCoef { get; set; }
|
|
||||||
|
|
||||||
public float Force { get; set; }
|
public float Force { get; set; }
|
||||||
public float Slip { get; set; }
|
public float Slip { get; set; }
|
||||||
public float Speed { get; set; }
|
public float Speed { get; set; }
|
||||||
|
|
||||||
|
|
||||||
|
public override readonly string ToString()
|
||||||
|
{
|
||||||
|
return $"Force:{Math.Round(Force, 2)}\nSlip:{Math.Round(Slip, 2)}\nSpeed:{Math.Round(Speed, 2)}";
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -35,8 +35,6 @@ public partial class VeloXWheel
|
|||||||
public float CounterTorque { get; private set; }
|
public float CounterTorque { get; private set; }
|
||||||
|
|
||||||
//[Property, Range( 0, 2 )] public float BrakeMult { get; set; } = 1f;
|
//[Property, Range( 0, 2 )] public float BrakeMult { get; set; } = 1f;
|
||||||
public Friction ForwardFriction = new();
|
|
||||||
public Friction SidewayFriction = new();
|
|
||||||
public Vector3 FrictionForce;
|
public Vector3 FrictionForce;
|
||||||
|
|
||||||
|
|
||||||
@@ -47,31 +45,32 @@ public partial class VeloXWheel
|
|||||||
public Vector3 ContactRight => hitSidewaysDirection;
|
public Vector3 ContactRight => hitSidewaysDirection;
|
||||||
public Vector3 ContactForward => hitForwardDirection;
|
public Vector3 ContactForward => hitForwardDirection;
|
||||||
|
|
||||||
[Sync]
|
public float LongitudinalSlip => sx;
|
||||||
public float LongitudinalSlip
|
public float LongitudinalSpeed => vx;
|
||||||
{
|
|
||||||
get => ForwardFriction.Slip;
|
|
||||||
private set => ForwardFriction.Slip = value;
|
|
||||||
}
|
|
||||||
|
|
||||||
public float LongitudinalSpeed => ForwardFriction.Speed;
|
|
||||||
public bool IsSkiddingLongitudinally => NormalizedLongitudinalSlip > 0.35f;
|
public bool IsSkiddingLongitudinally => NormalizedLongitudinalSlip > 0.35f;
|
||||||
public float NormalizedLongitudinalSlip => Math.Clamp( Math.Abs( LongitudinalSlip ), 0, 1 );
|
public float NormalizedLongitudinalSlip => Math.Clamp( Math.Abs( LongitudinalSlip ), 0, 1 );
|
||||||
|
|
||||||
|
public float LateralSlip => sy;
|
||||||
[Sync]
|
public float LateralSpeed => vy;
|
||||||
public float LateralSlip
|
|
||||||
{
|
|
||||||
get => SidewayFriction.Slip;
|
|
||||||
private set => SidewayFriction.Slip = value;
|
|
||||||
}
|
|
||||||
|
|
||||||
public float LateralSpeed => SidewayFriction.Speed;
|
|
||||||
public bool IsSkiddingLaterally => NormalizedLateralSlip > 0.35f;
|
public bool IsSkiddingLaterally => NormalizedLateralSlip > 0.35f;
|
||||||
public float NormalizedLateralSlip => Math.Clamp( Math.Abs( LateralSlip ), 0, 1 );
|
public float NormalizedLateralSlip => Math.Clamp( Math.Abs( LateralSlip ), 0, 1 );
|
||||||
|
|
||||||
public bool IsSkidding => IsSkiddingLaterally || IsSkiddingLongitudinally;
|
public bool IsSkidding => IsSkiddingLaterally || IsSkiddingLongitudinally;
|
||||||
public float NormalizedSlip => (NormalizedLateralSlip + NormalizedLongitudinalSlip) / 2f;
|
public float NormalizedSlip => (NormalizedLateralSlip + NormalizedLongitudinalSlip) / 2f;
|
||||||
|
|
||||||
|
|
||||||
|
// speed
|
||||||
|
[Sync] private float vx { get; set; }
|
||||||
|
[Sync] private float vy { get; set; }
|
||||||
|
|
||||||
|
// force
|
||||||
|
[Sync] private float fx { get; set; }
|
||||||
|
[Sync] private float fy { get; set; }
|
||||||
|
|
||||||
|
// slip
|
||||||
|
[Sync] private float sx { get; set; }
|
||||||
|
[Sync] private float sy { get; set; }
|
||||||
|
|
||||||
private void UpdateHitVariables()
|
private void UpdateHitVariables()
|
||||||
{
|
{
|
||||||
if ( IsOnGround )
|
if ( IsOnGround )
|
||||||
@@ -81,13 +80,13 @@ public partial class VeloXWheel
|
|||||||
hitForwardDirection = ContactNormal.Cross( TransformRotationSteer.Right ).Normal;
|
hitForwardDirection = ContactNormal.Cross( TransformRotationSteer.Right ).Normal;
|
||||||
hitSidewaysDirection = Rotation.FromAxis( ContactNormal, 90f ) * hitForwardDirection;
|
hitSidewaysDirection = Rotation.FromAxis( ContactNormal, 90f ) * hitForwardDirection;
|
||||||
|
|
||||||
ForwardFriction.Speed = hitContactVelocity.Dot( hitForwardDirection ).InchToMeter();
|
vx = hitContactVelocity.Dot( hitForwardDirection ).InchToMeter();
|
||||||
SidewayFriction.Speed = hitContactVelocity.Dot( hitSidewaysDirection ).InchToMeter();
|
vy = hitContactVelocity.Dot( hitSidewaysDirection ).InchToMeter();
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
ForwardFriction.Speed = 0f;
|
vx = 0;
|
||||||
SidewayFriction.Speed = 0f;
|
vy = 0;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -97,6 +96,7 @@ public partial class VeloXWheel
|
|||||||
private Vector3 currentPosition;
|
private Vector3 currentPosition;
|
||||||
private Vector3 referenceError;
|
private Vector3 referenceError;
|
||||||
private Vector3 correctiveForce;
|
private Vector3 correctiveForce;
|
||||||
|
public bool wheelIsBlocked;
|
||||||
private void UpdateFriction( float dt )
|
private void UpdateFriction( float dt )
|
||||||
{
|
{
|
||||||
var motorTorque = DriveTorque;
|
var motorTorque = DriveTorque;
|
||||||
@@ -120,26 +120,21 @@ public partial class VeloXWheel
|
|||||||
|
|
||||||
float loadPercent = Math.Clamp( Fz / LoadRating, 0f, 1f );
|
float loadPercent = Math.Clamp( Fz / LoadRating, 0f, 1f );
|
||||||
float slipLoadModifier = 1f - loadPercent * 0.4f;
|
float slipLoadModifier = 1f - loadPercent * 0.4f;
|
||||||
//DebugOverlay.Text( WorldPosition, SidewayFriction.Speed.ToString(), overlay: true );
|
|
||||||
|
|
||||||
|
|
||||||
float mass = Vehicle.Body.Mass;
|
float mass = Vehicle.Body.Mass;
|
||||||
float absForwardSpeed = Math.Abs( ForwardFriction.Speed );
|
float absForwardSpeed = Math.Abs( vx );
|
||||||
float forwardForceClamp = mass * LoadContribution * absForwardSpeed * invDt;
|
float forwardForceClamp = mass * LoadContribution * absForwardSpeed * invDt;
|
||||||
float absSideSpeed = Math.Abs( SidewayFriction.Speed );
|
float absSideSpeed = Math.Abs( vy );
|
||||||
float sideForceClamp = mass * LoadContribution * absSideSpeed * invDt;
|
float sideForceClamp = mass * LoadContribution * absSideSpeed * invDt;
|
||||||
|
|
||||||
float forwardSpeedClamp = 1.5f * (dt / 0.005f);
|
float forwardSpeedClamp = 1.5f * (dt / 0.005f);
|
||||||
forwardSpeedClamp = Math.Clamp( forwardSpeedClamp, 1.5f, 10f );
|
forwardSpeedClamp = Math.Clamp( forwardSpeedClamp, 1.5f, 10f );
|
||||||
float clampedAbsForwardSpeed = Math.Max( absForwardSpeed, forwardSpeedClamp );
|
float clampedAbsForwardSpeed = Math.Max( absForwardSpeed, forwardSpeedClamp );
|
||||||
|
|
||||||
// Calculate effect of camber on friction
|
float peakForwardFrictionForce = 11000 * (1 - MathF.Exp( -0.00014f * forwardLoadFactor ));
|
||||||
float camberFrictionCoeff = Math.Max( 0, Vehicle.WorldRotation.Up.Dot( ContactNormal ) );
|
|
||||||
|
|
||||||
float peakForwardFrictionForce = forwardLoadFactor;
|
|
||||||
float absCombinedBrakeTorque = Math.Max( 0, brakeTorque + RollingResistanceTorque );
|
float absCombinedBrakeTorque = Math.Max( 0, brakeTorque + RollingResistanceTorque );
|
||||||
|
|
||||||
float signedCombinedBrakeTorque = absCombinedBrakeTorque * -Math.Sign( ForwardFriction.Speed );
|
float signedCombinedBrakeTorque = absCombinedBrakeTorque * (vx > 0 ? -1 : 1);
|
||||||
float signedCombinedBrakeForce = signedCombinedBrakeTorque * invRadius;
|
float signedCombinedBrakeForce = signedCombinedBrakeTorque * invRadius;
|
||||||
float motorForce = motorTorque * invRadius;
|
float motorForce = motorTorque * invRadius;
|
||||||
float forwardInputForce = motorForce + signedCombinedBrakeForce;
|
float forwardInputForce = motorForce + signedCombinedBrakeForce;
|
||||||
@@ -149,10 +144,10 @@ public partial class VeloXWheel
|
|||||||
float maxForwardForce = Math.Min( peakForwardFrictionForce, forwardForceClamp );
|
float maxForwardForce = Math.Min( peakForwardFrictionForce, forwardForceClamp );
|
||||||
|
|
||||||
maxForwardForce = absMotorTorque < absBrakeTorque ? maxForwardForce : peakForwardFrictionForce;
|
maxForwardForce = absMotorTorque < absBrakeTorque ? maxForwardForce : peakForwardFrictionForce;
|
||||||
ForwardFriction.Force = forwardInputForce > maxForwardForce ? maxForwardForce
|
fx = forwardInputForce > maxForwardForce ? maxForwardForce
|
||||||
: forwardInputForce < -maxForwardForce ? -maxForwardForce : forwardInputForce;
|
: forwardInputForce < -maxForwardForce ? -maxForwardForce : forwardInputForce;
|
||||||
|
|
||||||
bool wheelIsBlocked = false;
|
wheelIsBlocked = false;
|
||||||
if ( IsOnGround )
|
if ( IsOnGround )
|
||||||
{
|
{
|
||||||
float combinedWheelForce = motorForce + absCombinedBrakeTorque * invRadius * -Math.Sign( AngularVelocity );
|
float combinedWheelForce = motorForce + absCombinedBrakeTorque * invRadius * -Math.Sign( AngularVelocity );
|
||||||
@@ -168,14 +163,14 @@ public partial class VeloXWheel
|
|||||||
AngularVelocity += combinedWheelForce * mRadius * invInertia * dt;
|
AngularVelocity += combinedWheelForce * mRadius * invInertia * dt;
|
||||||
|
|
||||||
// Surface (corrective) force
|
// Surface (corrective) force
|
||||||
float noSlipAngularVelocity = ForwardFriction.Speed * invRadius;
|
float noSlipAngularVelocity = vx * invRadius;
|
||||||
float angularVelocityError = AngularVelocity - noSlipAngularVelocity;
|
float angularVelocityError = AngularVelocity - noSlipAngularVelocity;
|
||||||
|
|
||||||
float angularVelocityCorrectionForce = Math.Clamp( -angularVelocityError * inertia * invRadius * invDt, -maxForwardForce, maxForwardForce );
|
float angularVelocityCorrectionForce = Math.Clamp( -angularVelocityError * inertia * invRadius * invDt, -maxForwardForce, maxForwardForce );
|
||||||
if ( absMotorTorque < absBrakeTorque && Math.Abs( wheelForceClampOverflow ) > Math.Abs( angularVelocityCorrectionForce ) )
|
if ( absMotorTorque < absBrakeTorque && Math.Abs( wheelForceClampOverflow ) > Math.Abs( angularVelocityCorrectionForce ) )
|
||||||
{
|
{
|
||||||
wheelIsBlocked = true;
|
wheelIsBlocked = true;
|
||||||
AngularVelocity += ForwardFriction.Speed > 0 ? 1e-10f : -1e-10f;
|
AngularVelocity += vx > 0 ? 1e-10f : -1e-10f;
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
@@ -193,22 +188,24 @@ public partial class VeloXWheel
|
|||||||
|
|
||||||
float absAngularVelocity = AngularVelocity < 0 ? -AngularVelocity : AngularVelocity;
|
float absAngularVelocity = AngularVelocity < 0 ? -AngularVelocity : AngularVelocity;
|
||||||
float maxCounterTorque = inertia * absAngularVelocity;
|
float maxCounterTorque = inertia * absAngularVelocity;
|
||||||
CounterTorque = Math.Clamp( (signedCombinedBrakeForce - ForwardFriction.Force) * mRadius, -maxCounterTorque, maxCounterTorque );
|
CounterTorque = Math.Clamp( (signedCombinedBrakeForce - fx) * mRadius, -maxCounterTorque, maxCounterTorque );
|
||||||
|
|
||||||
|
sx = (vx - AngularVelocity * mRadius) / clampedAbsForwardSpeed;
|
||||||
|
sx *= slipLoadModifier;
|
||||||
|
|
||||||
ForwardFriction.Slip = (ForwardFriction.Speed - AngularVelocity * mRadius) / clampedAbsForwardSpeed;
|
sy = MathF.Atan2( vy, clampedAbsForwardSpeed );
|
||||||
ForwardFriction.Slip *= slipLoadModifier;
|
sy *= slipLoadModifier;
|
||||||
|
|
||||||
SidewayFriction.Slip = MathF.Atan2( SidewayFriction.Speed, clampedAbsForwardSpeed );
|
float sideSlipSign = sy > 0 ? 1 : -1;
|
||||||
SidewayFriction.Slip *= slipLoadModifier;
|
float absSideSlip = Math.Abs( sy );
|
||||||
|
float peakSideFrictionForce = 18000 * (1 - MathF.Exp( -0.0001f * sideLoadFactor ));
|
||||||
|
|
||||||
float sideSlipSign = SidewayFriction.Slip > 0 ? 1 : -1;
|
float sideForce = -sideSlipSign * Tire.Evaluate( absSideSlip ) * peakSideFrictionForce;
|
||||||
float absSideSlip = Math.Abs( SidewayFriction.Slip );
|
fy = Math.Clamp( sideForce, -sideForceClamp, sideForceClamp );
|
||||||
float peakSideFrictionForce = sideLoadFactor;
|
|
||||||
|
|
||||||
float sideForce = -sideSlipSign * Tire.Evaluate( absSideSlip ) * sideLoadFactor;
|
// Calculate effect of camber on friction
|
||||||
SidewayFriction.Force = Math.Clamp( sideForce, -sideForceClamp, sideForceClamp );
|
float camberFrictionCoeff = Math.Max( 0, Vehicle.WorldRotation.Up.Dot( ContactNormal ) );
|
||||||
SidewayFriction.Force *= camberFrictionCoeff;
|
fy *= camberFrictionCoeff;
|
||||||
|
|
||||||
if ( IsOnGround && absForwardSpeed < 0.12f && absSideSpeed < 0.12f )
|
if ( IsOnGround && absForwardSpeed < 0.12f && absSideSpeed < 0.12f )
|
||||||
{
|
{
|
||||||
@@ -238,56 +235,36 @@ public partial class VeloXWheel
|
|||||||
|
|
||||||
if ( wheelIsBlocked && absAngularVelocity < 0.5f )
|
if ( wheelIsBlocked && absAngularVelocity < 0.5f )
|
||||||
{
|
{
|
||||||
ForwardFriction.Force += correctiveForce.Dot( hitForwardDirection );
|
fx += correctiveForce.Dot( hitForwardDirection );
|
||||||
}
|
}
|
||||||
SidewayFriction.Force += correctiveForce.Dot( hitSidewaysDirection );
|
fy += correctiveForce.Dot( hitSidewaysDirection );
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
lowSpeedReferenceIsSet = false;
|
lowSpeedReferenceIsSet = false;
|
||||||
}
|
}
|
||||||
ForwardFriction.Force = Math.Clamp( ForwardFriction.Force, -peakForwardFrictionForce, peakForwardFrictionForce );
|
|
||||||
|
|
||||||
SidewayFriction.Force = Math.Clamp( SidewayFriction.Force, -peakSideFrictionForce, peakSideFrictionForce );
|
fx = Math.Clamp( fx, -peakForwardFrictionForce, peakForwardFrictionForce );
|
||||||
|
fy = Math.Clamp( fy, -peakSideFrictionForce, peakSideFrictionForce );
|
||||||
|
|
||||||
if ( absForwardSpeed > 2f || absAngularVelocity > 4f )
|
if ( absForwardSpeed > 0.01f || absAngularVelocity > 0.01f )
|
||||||
{
|
{
|
||||||
|
var f = MathF.Sqrt( fx * fx + fy * fy );
|
||||||
|
var d = Math.Abs( new Vector2( sx, sy ).Normal.y );
|
||||||
|
fy = f * d * Math.Sign( fy );
|
||||||
|
|
||||||
float forwardSlipPercent = ForwardFriction.Slip / Tire.GetPeakSlip();
|
|
||||||
float sideSlipPercent = SidewayFriction.Slip / Tire.GetPeakSlip();
|
|
||||||
float slipCircleLimit = MathF.Sqrt( forwardSlipPercent * forwardSlipPercent + sideSlipPercent * sideSlipPercent );
|
|
||||||
if ( slipCircleLimit > 1f )
|
|
||||||
{
|
|
||||||
float beta = MathF.Atan2( sideSlipPercent, forwardSlipPercent * 0.9f );
|
|
||||||
float sinBeta = MathF.Sin( beta );
|
|
||||||
float cosBeta = MathF.Cos( beta );
|
|
||||||
|
|
||||||
float absForwardForce = ForwardFriction.Force < 0 ? -ForwardFriction.Force : ForwardFriction.Force;
|
|
||||||
|
|
||||||
float absSideForce = SidewayFriction.Force < 0 ? -SidewayFriction.Force : SidewayFriction.Force;
|
|
||||||
float f = absForwardForce * cosBeta * cosBeta + absSideForce * sinBeta * sinBeta;
|
|
||||||
|
|
||||||
ForwardFriction.Force = 0.5f * ForwardFriction.Force - f * cosBeta;
|
|
||||||
SidewayFriction.Force = 0.5f * SidewayFriction.Force - f * sinBeta;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
if ( IsOnGround )
|
if ( IsOnGround )
|
||||||
{
|
{
|
||||||
FrictionForce.x = (hitSidewaysDirection.x * SidewayFriction.Force + hitForwardDirection.x * ForwardFriction.Force).MeterToInch();
|
FrictionForce.x = (hitSidewaysDirection.x * fy + hitForwardDirection.x * fx).MeterToInch();
|
||||||
FrictionForce.y = (hitSidewaysDirection.y * SidewayFriction.Force + hitForwardDirection.y * ForwardFriction.Force).MeterToInch();
|
FrictionForce.y = (hitSidewaysDirection.y * fy + hitForwardDirection.y * fx).MeterToInch();
|
||||||
FrictionForce.z = (hitSidewaysDirection.z * SidewayFriction.Force + hitForwardDirection.z * ForwardFriction.Force).MeterToInch();
|
FrictionForce.z = (hitSidewaysDirection.z * fy + hitForwardDirection.z * fx).MeterToInch();
|
||||||
//DebugOverlay.Normal( WorldPosition, hitSidewaysDirection * 10, overlay: true );
|
|
||||||
//DebugOverlay.Normal( WorldPosition, hitForwardDirection * 10, overlay: true );
|
|
||||||
//DebugOverlay.Normal( WorldPosition, FrictionForce / 100, overlay: true );
|
|
||||||
//DebugOverlay.Normal( ContactPosition, Vector3.Up * AngularVelocity, overlay: true );
|
|
||||||
|
|
||||||
//DebugOverlay.Sphere( new( ContactPosition, 4 ), overlay: true );
|
|
||||||
//Vehicle.Body.ApplyForceAt( ContactPosition, FrictionForce );
|
|
||||||
|
|
||||||
|
//DebugOverlay.Normal( WorldPosition, hitSidewaysDirection * 10, overlay: true, color: Color.Red );
|
||||||
|
//DebugOverlay.Normal( WorldPosition, hitForwardDirection * 10, overlay: true, color: Color.Green );
|
||||||
|
//DebugOverlay.Normal( WorldPosition, FrictionForce.ClampLength( 30 ), overlay: true, color: Color.Cyan );
|
||||||
|
//DebugOverlay.ScreenText( Scene.Camera.PointToScreenPixels( WorldPosition ), $"{ForwardFriction}\nMotor:{(int)motorTorque}\nBrake:{(int)brakeTorque}", flags: TextFlag.LeftTop );
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
FrictionForce = Vector3.Zero;
|
FrictionForce = Vector3.Zero;
|
||||||
|
|||||||
@@ -1,4 +1,5 @@
|
|||||||
using Sandbox;
|
using Sandbox;
|
||||||
|
using System;
|
||||||
|
|
||||||
namespace VeloX;
|
namespace VeloX;
|
||||||
|
|
||||||
|
|||||||
@@ -34,7 +34,7 @@ public partial class VeloXWheel
|
|||||||
{
|
{
|
||||||
GameObject go = new()
|
GameObject go = new()
|
||||||
{
|
{
|
||||||
WorldPosition = ContactPosition + Vehicle.WorldRotation.Down * (Radius.MeterToInch() - 0.01f),
|
WorldPosition = ContactPosition + Vehicle.WorldRotation.Down * (Radius.MeterToInch() - 1f),
|
||||||
WorldRotation = Rotation.LookAt( hitSidewaysDirection )
|
WorldRotation = Rotation.LookAt( hitSidewaysDirection )
|
||||||
};
|
};
|
||||||
_skidMark = go.AddComponent<LineRenderer>();
|
_skidMark = go.AddComponent<LineRenderer>();
|
||||||
@@ -45,7 +45,7 @@ public partial class VeloXWheel
|
|||||||
_skidMark.CastShadows = false;
|
_skidMark.CastShadows = false;
|
||||||
_skidMark.Width = Width.MeterToInch() / 2;
|
_skidMark.Width = Width.MeterToInch() / 2;
|
||||||
_skidMark.AutoCalculateNormals = false;
|
_skidMark.AutoCalculateNormals = false;
|
||||||
_skidMark.SplineInterpolation = 4;
|
_skidMark.SplineInterpolation = 1;
|
||||||
go.Flags = go.Flags.WithFlag( GameObjectFlags.Hidden, true );
|
go.Flags = go.Flags.WithFlag( GameObjectFlags.Hidden, true );
|
||||||
go.Flags = go.Flags.WithFlag( GameObjectFlags.NotNetworked, true );
|
go.Flags = go.Flags.WithFlag( GameObjectFlags.NotNetworked, true );
|
||||||
SkidMarks.Enqueue( _skidMark );
|
SkidMarks.Enqueue( _skidMark );
|
||||||
@@ -53,8 +53,7 @@ public partial class VeloXWheel
|
|||||||
|
|
||||||
protected void UpdateSkid()
|
protected void UpdateSkid()
|
||||||
{
|
{
|
||||||
if ( IsProxy )
|
|
||||||
return;
|
|
||||||
while ( SkidMarks.Count > MaxSkid )
|
while ( SkidMarks.Count > MaxSkid )
|
||||||
{
|
{
|
||||||
SkidMarks.Dequeue()?.DestroyGameObject();
|
SkidMarks.Dequeue()?.DestroyGameObject();
|
||||||
@@ -81,7 +80,7 @@ public partial class VeloXWheel
|
|||||||
GameObject go = new()
|
GameObject go = new()
|
||||||
{
|
{
|
||||||
Parent = _skidMark.GameObject,
|
Parent = _skidMark.GameObject,
|
||||||
WorldPosition = ContactPosition + Vehicle.WorldRotation.Down * Radius.MeterToInch(),
|
WorldPosition = ContactPosition + Vehicle.WorldRotation.Down * (Radius.MeterToInch() - 1f),
|
||||||
WorldRotation = Rotation.LookAt( ContactNormal.RotateAround( Vector3.Zero, Rotation.FromRoll( 90 ) ) )
|
WorldRotation = Rotation.LookAt( ContactNormal.RotateAround( Vector3.Zero, Rotation.FromRoll( 90 ) ) )
|
||||||
};
|
};
|
||||||
go.Flags = go.Flags.WithFlag( GameObjectFlags.Hidden, true );
|
go.Flags = go.Flags.WithFlag( GameObjectFlags.Hidden, true );
|
||||||
|
|||||||
@@ -8,9 +8,8 @@ namespace VeloX;
|
|||||||
|
|
||||||
public partial class VeloXWheel
|
public partial class VeloXWheel
|
||||||
{
|
{
|
||||||
private static readonly GameObject SmokePrefab = GameObject.GetPrefab( "prefabs/particles/tire_smoke.prefab" );
|
|
||||||
|
|
||||||
private GameObject SmokeObject { get; set; }
|
private GameObject SmokeObject;
|
||||||
public const float MIN_DRIFT_ANGLE = 10f;
|
public const float MIN_DRIFT_ANGLE = 10f;
|
||||||
public const float MIN_DRIFT_SPEED = 30f;
|
public const float MIN_DRIFT_SPEED = 30f;
|
||||||
public const float MAX_DRIFT_ANGLE = 110f;
|
public const float MAX_DRIFT_ANGLE = 110f;
|
||||||
@@ -19,11 +18,13 @@ public partial class VeloXWheel
|
|||||||
protected override void OnStart()
|
protected override void OnStart()
|
||||||
{
|
{
|
||||||
base.OnStart();
|
base.OnStart();
|
||||||
if ( IsProxy )
|
|
||||||
return;
|
|
||||||
|
|
||||||
if ( !SmokeObject.IsValid() )
|
SmokeObject = GameObject.GetPrefab( "prefabs/particles/tire_smoke.prefab" ).Clone( new CloneConfig() { Parent = GameObject, StartEnabled = true } );
|
||||||
SmokeObject = GameObject.GetPrefab( "prefabs/particles/tire_smoke.prefab" ).Clone( new CloneConfig() { Parent = GameObject, StartEnabled = true } );
|
SmokeObject.Flags = SmokeObject.Flags.WithFlag( GameObjectFlags.NotNetworked, true );
|
||||||
|
|
||||||
|
var emitter = SmokeObject.Components.Get<ParticleSphereEmitter>( FindMode.EverythingInSelfAndDescendants );
|
||||||
|
emitter.Enabled = false;
|
||||||
|
|
||||||
}
|
}
|
||||||
public float GetSlip()
|
public float GetSlip()
|
||||||
{
|
{
|
||||||
@@ -36,12 +37,8 @@ public partial class VeloXWheel
|
|||||||
// return val;
|
// return val;
|
||||||
return val * 5;
|
return val * 5;
|
||||||
}
|
}
|
||||||
protected override void OnUpdate()
|
private void UpdateSmoke()
|
||||||
{
|
{
|
||||||
base.OnUpdate();
|
|
||||||
if ( IsProxy )
|
|
||||||
return;
|
|
||||||
UpdateSkid();
|
|
||||||
SmokeObject.WorldPosition = ContactPosition + Vehicle.WorldRotation.Down * Radius.MeterToInch();
|
SmokeObject.WorldPosition = ContactPosition + Vehicle.WorldRotation.Down * Radius.MeterToInch();
|
||||||
smokeMul = Math.Max( 0, GetSlip() - 3 );
|
smokeMul = Math.Max( 0, GetSlip() - 3 );
|
||||||
|
|
||||||
|
|||||||
@@ -1,4 +1,5 @@
|
|||||||
using Sandbox;
|
using Sandbox;
|
||||||
|
using Sandbox.Utility;
|
||||||
using System;
|
using System;
|
||||||
|
|
||||||
namespace VeloX;
|
namespace VeloX;
|
||||||
@@ -28,7 +29,7 @@ public partial class VeloXWheel : Component
|
|||||||
|
|
||||||
public float RPM { get => AngularVelocity * 60f / MathF.Tau; set => AngularVelocity = value / (60 / MathF.Tau); }
|
public float RPM { get => AngularVelocity * 60f / MathF.Tau; set => AngularVelocity = value / (60 / MathF.Tau); }
|
||||||
|
|
||||||
private Vector3 StartPos { get; set; }
|
[Sync] private Vector3 StartPos { get; set; }
|
||||||
private static Rotation CylinderOffset = Rotation.FromRoll( 90 );
|
private static Rotation CylinderOffset = Rotation.FromRoll( 90 );
|
||||||
|
|
||||||
[Sync] public bool IsOnGround { get; private set; }
|
[Sync] public bool IsOnGround { get; private set; }
|
||||||
@@ -38,13 +39,13 @@ public partial class VeloXWheel : Component
|
|||||||
[Property] public float BrakeTorque { get; set; }
|
[Property] public float BrakeTorque { get; set; }
|
||||||
|
|
||||||
public float Compression { get; protected set; } // meters
|
public float Compression { get; protected set; } // meters
|
||||||
public float LastLength { get; protected set; } // meters
|
[Sync( SyncFlags.Interpolate )] public float LastLength { get; protected set; } // meters
|
||||||
public float Fz { get; protected set; } // N
|
public float Fz { get; protected set; } // N
|
||||||
public float AngularVelocity { get; protected set; } // rad/s
|
public float AngularVelocity { get; protected set; } // rad/s
|
||||||
public float RollAngle { get; protected set; } // degrees
|
[Sync( SyncFlags.Interpolate )] public float RollAngle { get; protected set; }
|
||||||
|
|
||||||
|
|
||||||
private VeloXBase Vehicle;
|
public VeloXBase Vehicle { get; private set; }
|
||||||
[Sync] public Vector3 ContactNormal { get; protected set; }
|
[Sync] public Vector3 ContactNormal { get; protected set; }
|
||||||
[Sync] public Vector3 ContactPosition { get; protected set; }
|
[Sync] public Vector3 ContactPosition { get; protected set; }
|
||||||
Rotation TransformRotationSteer => Vehicle.WorldTransform.RotationToWorld( Vehicle.SteerAngle * SteerMultiplier );
|
Rotation TransformRotationSteer => Vehicle.WorldTransform.RotationToWorld( Vehicle.SteerAngle * SteerMultiplier );
|
||||||
@@ -58,14 +59,9 @@ public partial class VeloXWheel : Component
|
|||||||
Inertia = BaseInertia;
|
Inertia = BaseInertia;
|
||||||
}
|
}
|
||||||
|
|
||||||
internal void Update( VeloXBase vehicle, in float dt )
|
private void UpdateVisuals()
|
||||||
{
|
{
|
||||||
UpdateVisuals( vehicle, dt );
|
WorldRotation = Vehicle.WorldTransform.RotationToWorld( Vehicle.SteerAngle * SteerMultiplier ).RotateAroundAxis( Vector3.Right, -RollAngle );
|
||||||
}
|
|
||||||
|
|
||||||
private void UpdateVisuals( VeloXBase vehicle, in float dt )
|
|
||||||
{
|
|
||||||
WorldRotation = vehicle.WorldTransform.RotationToWorld( vehicle.SteerAngle * SteerMultiplier ).RotateAroundAxis( Vector3.Right, -RollAngle );
|
|
||||||
LocalPosition = StartPos + Vector3.Down * LastLength.MeterToInch();
|
LocalPosition = StartPos + Vector3.Down * LastLength.MeterToInch();
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -79,6 +75,7 @@ public partial class VeloXWheel : Component
|
|||||||
public void UpdateForce()
|
public void UpdateForce()
|
||||||
{
|
{
|
||||||
Vehicle.Body.ApplyForceAt( ContactPosition, FrictionForce + ContactNormal * Fz.MeterToInch() );
|
Vehicle.Body.ApplyForceAt( ContactPosition, FrictionForce + ContactNormal * Fz.MeterToInch() );
|
||||||
|
FrictionForce = 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
internal void StepPhys( VeloXBase vehicle, in float dt )
|
internal void StepPhys( VeloXBase vehicle, in float dt )
|
||||||
@@ -109,7 +106,6 @@ public partial class VeloXWheel : Component
|
|||||||
Compression = wheelTraceData.Compression / hitCount;
|
Compression = wheelTraceData.Compression / hitCount;
|
||||||
ContactNormal = (wheelTraceData.ContactNormal / hitCount).Normal;
|
ContactNormal = (wheelTraceData.ContactNormal / hitCount).Normal;
|
||||||
ContactPosition = wheelTraceData.ContactPosition / hitCount;
|
ContactPosition = wheelTraceData.ContactPosition / hitCount;
|
||||||
//DoSuspensionSounds( vehicle, (RestLength - Compression) * 0.8f);
|
|
||||||
LastLength = RestLength - Compression;
|
LastLength = RestLength - Compression;
|
||||||
|
|
||||||
UpdateHitVariables();
|
UpdateHitVariables();
|
||||||
@@ -118,11 +114,14 @@ public partial class VeloXWheel : Component
|
|||||||
else
|
else
|
||||||
{
|
{
|
||||||
IsOnGround = false;
|
IsOnGround = false;
|
||||||
// Wheel is off the ground
|
|
||||||
Compression = 0f;
|
Compression = 0f;
|
||||||
Fz = 0f;
|
Fz = 0f;
|
||||||
ContactNormal = Vector3.Up;
|
ContactNormal = Vector3.Up;
|
||||||
ContactPosition = WorldPosition;
|
ContactPosition = WorldPosition;
|
||||||
|
LastLength = RestLength;
|
||||||
|
|
||||||
|
UpdateHitVariables();
|
||||||
|
UpdateFriction( dt );
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
@@ -130,15 +129,39 @@ public partial class VeloXWheel : Component
|
|||||||
|
|
||||||
private bool TraceWheel( VeloXBase vehicle, ref WheelTraceData wheelTraceData, Vector3 start, Vector3 end, float width, in float dt )
|
private bool TraceWheel( VeloXBase vehicle, ref WheelTraceData wheelTraceData, Vector3 start, Vector3 end, float width, in float dt )
|
||||||
{
|
{
|
||||||
|
SceneTraceResult trace;
|
||||||
|
if ( IsOnGround && vehicle.TotalSpeed < 550 )
|
||||||
|
{
|
||||||
|
trace = Scene.Trace
|
||||||
|
.FromTo( start, end )
|
||||||
|
.Cylinder( width, Radius.MeterToInch() )
|
||||||
|
.Rotated( vehicle.WorldRotation * CylinderOffset )
|
||||||
|
.UseHitPosition( false )
|
||||||
|
.IgnoreGameObjectHierarchy( Vehicle.GameObject )
|
||||||
|
.WithCollisionRules( Vehicle.GameObject.Tags )
|
||||||
|
.Run();
|
||||||
|
|
||||||
var trace = Scene.Trace
|
if ( trace.StartedSolid )
|
||||||
.FromTo( start, end )
|
{
|
||||||
.Cylinder( width, Radius.MeterToInch() )
|
trace = Scene.Trace
|
||||||
.Rotated( vehicle.WorldRotation * CylinderOffset )
|
.FromTo( start, end + Vehicle.WorldRotation.Down * Radius.MeterToInch() )
|
||||||
.UseHitPosition( false )
|
.UseHitPosition( false )
|
||||||
.IgnoreGameObjectHierarchy( Vehicle.GameObject )
|
.IgnoreGameObjectHierarchy( Vehicle.GameObject )
|
||||||
.WithCollisionRules( Vehicle.GameObject.Tags )
|
.WithCollisionRules( Vehicle.GameObject.Tags )
|
||||||
.Run();
|
.Run();
|
||||||
|
trace.EndPosition += Vehicle.WorldRotation.Up * Math.Min( Radius.MeterToInch(), trace.Distance );
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
trace = Scene.Trace
|
||||||
|
.FromTo( start, end + Vehicle.WorldRotation.Down * Radius.MeterToInch() )
|
||||||
|
.UseHitPosition( false )
|
||||||
|
.IgnoreGameObjectHierarchy( Vehicle.GameObject )
|
||||||
|
.WithCollisionRules( Vehicle.GameObject.Tags )
|
||||||
|
.Run();
|
||||||
|
trace.EndPosition += Vehicle.WorldRotation.Up * Math.Min( Radius.MeterToInch(), trace.Distance );
|
||||||
|
}
|
||||||
|
|
||||||
//DebugOverlay.Trace( trace, overlay: true );
|
//DebugOverlay.Trace( trace, overlay: true );
|
||||||
if ( trace.Hit )
|
if ( trace.Hit )
|
||||||
@@ -179,40 +202,20 @@ public partial class VeloXWheel : Component
|
|||||||
StepRotation( Vehicle, dt );
|
StepRotation( Vehicle, dt );
|
||||||
}
|
}
|
||||||
|
|
||||||
const float HubCoulombNm = 20f;
|
|
||||||
const float HubViscous = 0.1f;
|
|
||||||
private void StepRotation( VeloXBase vehicle, in float dt )
|
private void StepRotation( VeloXBase vehicle, in float dt )
|
||||||
{
|
{
|
||||||
float inertia = MathF.Max( 1f, Inertia );
|
|
||||||
float roadTorque = ForwardFriction.Speed * Radius;
|
|
||||||
float externalTorque = DriveTorque - roadTorque;
|
|
||||||
float rollingResistanceTorque = Fz * Radius * SurfaceResistance;
|
|
||||||
|
|
||||||
float coulombTorque = BrakeTorque + rollingResistanceTorque + HubCoulombNm;
|
|
||||||
|
|
||||||
float omega = AngularVelocity;
|
|
||||||
|
|
||||||
if ( MathF.Abs( omega ) < 1e-6f && MathF.Abs( externalTorque ) <= coulombTorque )
|
|
||||||
{
|
|
||||||
AngularVelocity = 0f;
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
if ( HubViscous > 0f ) omega *= MathF.Exp( -(HubViscous / inertia) * dt );
|
|
||||||
|
|
||||||
if ( coulombTorque > 0f && omega != 0f )
|
|
||||||
{
|
|
||||||
float dir = MathF.Sign( omega );
|
|
||||||
float deltaOmega = (coulombTorque / inertia) * dt;
|
|
||||||
if ( deltaOmega >= MathF.Abs( omega ) ) omega = 0f;
|
|
||||||
else omega -= dir * deltaOmega;
|
|
||||||
}
|
|
||||||
|
|
||||||
if ( MathF.Abs( omega ) < 0.01f ) omega = 0f;
|
|
||||||
}
|
|
||||||
AngularVelocity = omega;
|
|
||||||
|
|
||||||
RollAngle += MathX.RadianToDegree( AngularVelocity ) * dt;
|
RollAngle += MathX.RadianToDegree( AngularVelocity ) * dt;
|
||||||
RollAngle = (RollAngle % 360f + 360f) % 360f;
|
RollAngle = (RollAngle % 360f + 360f) % 360f;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
protected override void OnFixedUpdate()
|
||||||
|
{
|
||||||
|
|
||||||
|
UpdateSmoke();
|
||||||
|
}
|
||||||
|
protected override void OnUpdate()
|
||||||
|
{
|
||||||
|
UpdateVisuals();
|
||||||
|
UpdateSkid();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,6 +1,4 @@
|
|||||||
using Sandbox;
|
using Sandbox;
|
||||||
using System;
|
|
||||||
using System.Diagnostics;
|
|
||||||
using System.Linq;
|
using System.Linq;
|
||||||
namespace VeloX;
|
namespace VeloX;
|
||||||
|
|
||||||
@@ -10,6 +8,9 @@ internal sealed class WheelManager : GameObjectSystem
|
|||||||
|
|
||||||
public WheelManager( Scene scene ) : base( scene )
|
public WheelManager( Scene scene ) : base( scene )
|
||||||
{
|
{
|
||||||
|
if ( Application.IsDedicatedServer )
|
||||||
|
return;
|
||||||
|
|
||||||
Listen( Stage.StartFixedUpdate, -99, UpdateWheels, "UpdateWheels" );
|
Listen( Stage.StartFixedUpdate, -99, UpdateWheels, "UpdateWheels" );
|
||||||
Listen( Stage.StartFixedUpdate, -100, UpdateEngine, "UpdateEngine" );
|
Listen( Stage.StartFixedUpdate, -100, UpdateEngine, "UpdateEngine" );
|
||||||
}
|
}
|
||||||
@@ -24,13 +25,18 @@ internal sealed class WheelManager : GameObjectSystem
|
|||||||
if ( !wheels.Any() ) return;
|
if ( !wheels.Any() ) return;
|
||||||
|
|
||||||
var timeDelta = Time.Delta;
|
var timeDelta = Time.Delta;
|
||||||
Sandbox.Utility.Parallel.ForEach( wheels, item =>
|
//Sandbox.Utility.Parallel.ForEach( wheels, item =>
|
||||||
{
|
//{
|
||||||
if ( !item.IsProxy )
|
// if ( !item.IsProxy && item.IsValid() )
|
||||||
|
// item.DoPhysics( timeDelta );
|
||||||
|
//} );
|
||||||
|
foreach ( var item in wheels )
|
||||||
|
if ( item.IsValid() && !item.IsProxy )
|
||||||
item.DoPhysics( timeDelta );
|
item.DoPhysics( timeDelta );
|
||||||
} );
|
|
||||||
foreach ( var wheel in wheels )
|
foreach ( var wheel in wheels )
|
||||||
wheel.UpdateForce();
|
if ( wheel.IsValid() && !wheel.IsProxy)
|
||||||
|
wheel.UpdateForce();
|
||||||
|
|
||||||
//sw.Stop();
|
//sw.Stop();
|
||||||
|
|
||||||
@@ -46,11 +52,17 @@ internal sealed class WheelManager : GameObjectSystem
|
|||||||
if ( !engines.Any() ) return;
|
if ( !engines.Any() ) return;
|
||||||
|
|
||||||
var timeDelta = Time.Delta;
|
var timeDelta = Time.Delta;
|
||||||
Sandbox.Utility.Parallel.ForEach( engines, item =>
|
//Sandbox.Utility.Parallel.ForEach( engines, item =>
|
||||||
{
|
//{
|
||||||
if ( !item.IsProxy )
|
// foreach ( var wheel in engines )
|
||||||
item.UpdateEngine( timeDelta );
|
// if ( !wheel.IsProxy && wheel.IsValid() )
|
||||||
} );
|
// wheel.UpdateEngine( timeDelta );
|
||||||
|
//} );
|
||||||
|
foreach ( var wheel in engines )
|
||||||
|
if ( wheel.IsValid() )
|
||||||
|
wheel.UpdateEngine( timeDelta );
|
||||||
|
|
||||||
|
|
||||||
//sw.Stop();
|
//sw.Stop();
|
||||||
|
|
||||||
//DebugOverlaySystem.Current.ScreenText( new Vector2( 120, 54 ), $"Engine Sim: {sw.Elapsed.TotalMilliseconds,6:F2} ms", 24 );
|
//DebugOverlaySystem.Current.ScreenText( new Vector2( 120, 54 ), $"Engine Sim: {sw.Elapsed.TotalMilliseconds,6:F2} ms", 24 );
|
||||||
|
|||||||
@@ -15,7 +15,7 @@ public partial class VeloXCar
|
|||||||
if ( !UseABS )
|
if ( !UseABS )
|
||||||
return;
|
return;
|
||||||
|
|
||||||
if ( TotalSpeed < 100 || ABSActive || Steering.AlmostEqual( 0, 1 ) )
|
if ( TotalSpeed < 100 || SteeringAngle.AlmostEqual( 0, 1 ) )
|
||||||
return;
|
return;
|
||||||
|
|
||||||
|
|
||||||
@@ -26,10 +26,10 @@ public partial class VeloXCar
|
|||||||
if ( !wheel.IsOnGround )
|
if ( !wheel.IsOnGround )
|
||||||
continue;
|
continue;
|
||||||
|
|
||||||
if ( wheel.NormalizedLongitudinalSlip >= 0.55f )
|
if ( wheel.wheelIsBlocked )
|
||||||
{
|
{
|
||||||
ABSActive = true;
|
ABSActive = true;
|
||||||
wheel.BrakeTorque *= 0.25f;
|
wheel.BrakeTorque = 0f;
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,6 +1,7 @@
|
|||||||
|
|
||||||
|
|
||||||
using Sandbox;
|
using Sandbox;
|
||||||
|
using Sandbox.Audio;
|
||||||
using System;
|
using System;
|
||||||
|
|
||||||
namespace VeloX;
|
namespace VeloX;
|
||||||
@@ -48,23 +49,27 @@ public partial class VeloXCar
|
|||||||
|
|
||||||
protected virtual void UpdateDrift( float dt )
|
protected virtual void UpdateDrift( float dt )
|
||||||
{
|
{
|
||||||
float driftAngle = GetDriftAngle();
|
|
||||||
|
|
||||||
float mul = (driftAngle - MIN_DRIFT_ANGLE) / (90 - MIN_DRIFT_ANGLE);
|
var avgslip = 0f;
|
||||||
|
foreach ( var item in Wheels )
|
||||||
|
avgslip += item.NormalizedLongitudinalSlip + item.NormalizedLateralSlip;
|
||||||
|
|
||||||
if ( !_skidHandle.IsValid() )
|
float mul = Math.Clamp( avgslip, 0, 2 );
|
||||||
_skidHandle = Sound.PlayFile( SkidSound );
|
|
||||||
|
|
||||||
if ( !_skidHandle.IsValid() )
|
|
||||||
return;
|
|
||||||
|
|
||||||
targetVolume = mul;
|
targetVolume = mul;
|
||||||
targetPitch = 0.75f + 0.25f * mul;
|
targetPitch = 0.75f + 0.25f * mul;
|
||||||
|
|
||||||
|
if ( mul > 0.1f && !_skidHandle.IsValid() )
|
||||||
|
{
|
||||||
|
_skidHandle = Sound.PlayFile( SkidSound );
|
||||||
|
_skidHandle.TargetMixer = Mixer.Default;
|
||||||
|
}
|
||||||
|
|
||||||
|
if ( !_skidHandle.IsValid() )
|
||||||
|
return;
|
||||||
|
|
||||||
_skidHandle.Pitch += (targetPitch - _skidHandle.Pitch) * dt * 5f;
|
_skidHandle.Pitch += (targetPitch - _skidHandle.Pitch) * dt * 5f;
|
||||||
_skidHandle.Volume += (targetVolume - _skidHandle.Volume) * dt * 10f;
|
_skidHandle.Volume += (targetVolume - _skidHandle.Volume) * dt * 10f;
|
||||||
_skidHandle.Position = WorldPosition;
|
_skidHandle.Position = WorldPosition;
|
||||||
|
|
||||||
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -32,7 +32,6 @@ public partial class VeloXCar
|
|||||||
if ( !wheel.IsOnGround )
|
if ( !wheel.IsOnGround )
|
||||||
continue;
|
continue;
|
||||||
|
|
||||||
|
|
||||||
float additionalBrakeTorque = -angle * Math.Sign( wheel.LocalPosition.y ) * 20f;
|
float additionalBrakeTorque = -angle * Math.Sign( wheel.LocalPosition.y ) * 20f;
|
||||||
if ( additionalBrakeTorque > 0 )
|
if ( additionalBrakeTorque > 0 )
|
||||||
{
|
{
|
||||||
|
|||||||
@@ -21,16 +21,11 @@ public partial class VeloXCar
|
|||||||
public int CarDirection { get { return ForwardSpeed < 1 ? 0 : (VelocityAngle < 90 && VelocityAngle > -90 ? 1 : -1); } }
|
public int CarDirection { get { return ForwardSpeed < 1 ? 0 : (VelocityAngle < 90 && VelocityAngle > -90 ? 1 : -1); } }
|
||||||
public float VelocityAngle { get; private set; }
|
public float VelocityAngle { get; private set; }
|
||||||
|
|
||||||
[Sync] public float Steering { get; private set; }
|
|
||||||
|
|
||||||
private float currentSteerAngle;
|
private float currentSteerAngle;
|
||||||
private float inputSteer;
|
|
||||||
private void UpdateSteering( float dt )
|
private void UpdateSteering( float dt )
|
||||||
{
|
{
|
||||||
inputSteer = Input.AnalogMove.y;
|
float targetSteerAngle = SteeringAngle * MaxSteerAngle;
|
||||||
|
|
||||||
|
|
||||||
float targetSteerAngle = inputSteer * MaxSteerAngle;
|
|
||||||
|
|
||||||
if ( !Input.Down( "Jump" ) )
|
if ( !Input.Down( "Jump" ) )
|
||||||
targetSteerAngle *= Math.Clamp( 1 - Math.Clamp( TotalSpeed / 3000, 0f, 0.85f ), -1, 1 );
|
targetSteerAngle *= Math.Clamp( 1 - Math.Clamp( TotalSpeed / 3000, 0f, 0.85f ), -1, 1 );
|
||||||
@@ -42,10 +37,9 @@ public partial class VeloXCar
|
|||||||
if ( TotalSpeed > 150 && CarDirection > 0 && IsOnGround )
|
if ( TotalSpeed > 150 && CarDirection > 0 && IsOnGround )
|
||||||
targetAngle = VelocityAngle * MaxSteerAngleMultiplier;
|
targetAngle = VelocityAngle * MaxSteerAngleMultiplier;
|
||||||
|
|
||||||
float lerpSpeed = Math.Abs( inputSteer ) < 0.1f ? SteerReturnSpeed : SteerInputResponse;
|
float lerpSpeed = Math.Abs( SteeringAngle ) < 0.1f ? SteerReturnSpeed : SteerInputResponse;
|
||||||
|
|
||||||
currentSteerAngle = ExpDecay( currentSteerAngle, targetSteerAngle, lerpSpeed, Time.Delta );
|
currentSteerAngle = ExpDecay( currentSteerAngle, targetSteerAngle, lerpSpeed, Time.Delta );
|
||||||
Steering = currentSteerAngle + targetAngle;
|
SteerAngle = new( 0, Math.Clamp( currentSteerAngle + targetAngle, -MaxSteerAngle, MaxSteerAngle ), 0 );
|
||||||
SteerAngle = new( 0, Math.Clamp( Steering, -MaxSteerAngle, MaxSteerAngle ), 0 );
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
39
Code/Car/VeloXCar.TCS.cs
Normal file
39
Code/Car/VeloXCar.TCS.cs
Normal file
@@ -0,0 +1,39 @@
|
|||||||
|
using Sandbox;
|
||||||
|
using System;
|
||||||
|
|
||||||
|
namespace VeloX;
|
||||||
|
|
||||||
|
|
||||||
|
public partial class VeloXCar
|
||||||
|
{
|
||||||
|
public bool TCSActive { get; private set; } = true;
|
||||||
|
public static bool UseTCS = true;
|
||||||
|
|
||||||
|
private void UpdateTCS()
|
||||||
|
{
|
||||||
|
TCSActive = false;
|
||||||
|
|
||||||
|
if ( !UseTCS )
|
||||||
|
return;
|
||||||
|
|
||||||
|
if ( TotalSpeed < 50 || CarDirection != 1 )
|
||||||
|
return;
|
||||||
|
|
||||||
|
float vehicleSpeed = TotalSpeed.InchToMeter();
|
||||||
|
|
||||||
|
foreach ( var wheel in Wheels )
|
||||||
|
{
|
||||||
|
if ( !wheel.IsOnGround )
|
||||||
|
continue;
|
||||||
|
|
||||||
|
float wheelLinearSpeed = wheel.AngularVelocity * wheel.Radius;
|
||||||
|
float wheelSlip = wheelLinearSpeed - vehicleSpeed;
|
||||||
|
|
||||||
|
if ( wheelSlip > 2.0f )
|
||||||
|
{
|
||||||
|
TCSActive = true;
|
||||||
|
wheel.BrakeTorque = wheelSlip * 1000;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -7,23 +7,15 @@ namespace VeloX;
|
|||||||
[Title( "VeloX - Car" )]
|
[Title( "VeloX - Car" )]
|
||||||
public partial class VeloXCar : VeloXBase
|
public partial class VeloXCar : VeloXBase
|
||||||
{
|
{
|
||||||
|
|
||||||
protected override void FixedUpdate()
|
|
||||||
{
|
|
||||||
|
|
||||||
UpdateInput();
|
|
||||||
PhysicsSimulate();
|
|
||||||
UpdateABS();
|
|
||||||
UpdateESC();
|
|
||||||
}
|
|
||||||
|
|
||||||
protected override void OnFixedUpdate()
|
protected override void OnFixedUpdate()
|
||||||
{
|
{
|
||||||
if ( IsProxy )
|
|
||||||
return;
|
|
||||||
|
|
||||||
base.OnFixedUpdate();
|
base.OnFixedUpdate();
|
||||||
|
|
||||||
|
if ( IsProxy )
|
||||||
|
return;
|
||||||
|
UpdateABS();
|
||||||
|
UpdateESC();
|
||||||
|
UpdateTCS();
|
||||||
var dt = Time.Delta;
|
var dt = Time.Delta;
|
||||||
//EngineThink( dt );
|
//EngineThink( dt );
|
||||||
SimulateAerodinamics( dt );
|
SimulateAerodinamics( dt );
|
||||||
@@ -35,14 +27,24 @@ public partial class VeloXCar : VeloXBase
|
|||||||
|
|
||||||
private void UpdateUnflip( float dt )
|
private void UpdateUnflip( float dt )
|
||||||
{
|
{
|
||||||
if ( Math.Abs( inputSteer ) < 0.1f )
|
if ( Math.Abs( SteeringAngle ) < 0.1f )
|
||||||
return;
|
return;
|
||||||
|
|
||||||
if ( Math.Abs( WorldRotation.Angles().roll ) < 70 )
|
if ( IsOnGround || Math.Abs( WorldRotation.Roll() ) < 60 )
|
||||||
return;
|
return;
|
||||||
|
|
||||||
var angVel = Body.AngularVelocity;
|
var angVel = Body.WorldTransform.PointToLocal( WorldPosition + Body.AngularVelocity );
|
||||||
var force = inputSteer * Mass * Math.Clamp( 1 - angVel.x / 50, 0, 1 ) * 0.05f;
|
|
||||||
|
|
||||||
|
float maxAngularVelocity = 2.0f;
|
||||||
|
|
||||||
|
float velocityFactor = 1.0f - Math.Clamp( Math.Abs( angVel.x ) / maxAngularVelocity, 0f, 1f );
|
||||||
|
|
||||||
|
if ( velocityFactor <= 0.01f )
|
||||||
|
return;
|
||||||
|
|
||||||
|
var force = SteeringAngle * velocityFactor * 150;
|
||||||
|
|
||||||
Body.AngularVelocity -= Body.WorldRotation.Forward * force * dt;
|
Body.AngularVelocity -= Body.WorldRotation.Forward * force * dt;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,15 +1,13 @@
|
|||||||
using Sandbox;
|
using Sandbox;
|
||||||
|
|
||||||
namespace VeloX;
|
namespace VeloX;
|
||||||
|
|
||||||
public class InputResolver
|
public class InputResolver
|
||||||
{
|
{
|
||||||
public Connection Driver { get; internal set; }
|
|
||||||
|
|
||||||
private bool IsDriverActive => Driver is not null;
|
public Vector2 MouseDelta => Input.MouseDelta;
|
||||||
|
public Vector2 MouseWheel => Input.MouseWheel;
|
||||||
public Vector2 MouseDelta => IsDriverActive ? Input.MouseDelta : default;
|
public Angles AnalogLook => Input.AnalogLook;
|
||||||
public Vector2 MouseWheel => IsDriverActive ? Input.MouseWheel : default;
|
|
||||||
public Angles AnalogLook => IsDriverActive ? Input.AnalogLook : default;
|
|
||||||
public Vector3 AnalogMove
|
public Vector3 AnalogMove
|
||||||
{
|
{
|
||||||
get
|
get
|
||||||
@@ -21,43 +19,62 @@ public class InputResolver
|
|||||||
input.y = -Input.GetAnalog( InputAnalog.LeftStickX );
|
input.y = -Input.GetAnalog( InputAnalog.LeftStickX );
|
||||||
return input;
|
return input;
|
||||||
}
|
}
|
||||||
return IsDriverActive ? Input.AnalogMove : default;
|
return Input.AnalogMove;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public bool Down( string action )
|
public bool Down( string action )
|
||||||
{
|
{
|
||||||
return IsDriverActive && Input.Down( action );
|
return Input.Down( action );
|
||||||
|
}
|
||||||
|
|
||||||
|
public float Brake
|
||||||
|
{
|
||||||
|
get
|
||||||
|
{
|
||||||
|
if ( Input.UsingController )
|
||||||
|
return Input.GetAnalog( InputAnalog.LeftTrigger );
|
||||||
|
|
||||||
|
return Input.Down( "Brake" ) ? 1 : 0;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
public float Throttle
|
||||||
|
{
|
||||||
|
get
|
||||||
|
{
|
||||||
|
if ( Input.UsingController )
|
||||||
|
return Input.GetAnalog( InputAnalog.RightTrigger );
|
||||||
|
|
||||||
|
return Input.Down( "Throttle" ) ? 1 : 0;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public bool Pressed( string action )
|
public bool Pressed( string action )
|
||||||
{
|
{
|
||||||
return IsDriverActive && Input.Pressed( action );
|
return Input.Pressed( action );
|
||||||
}
|
}
|
||||||
|
|
||||||
public bool Released( string action )
|
public bool Released( string action )
|
||||||
{
|
{
|
||||||
return IsDriverActive && Input.Released( action );
|
return Input.Released( action );
|
||||||
|
}
|
||||||
|
|
||||||
|
public float GetAnalog( InputAnalog analog )
|
||||||
|
{
|
||||||
|
return Input.GetAnalog( analog );
|
||||||
}
|
}
|
||||||
|
|
||||||
public void TriggerHaptics( float leftMotor, float rightMotor, float leftTrigger = 0f, float rightTrigger = 0f, int duration = 500 )
|
public void TriggerHaptics( float leftMotor, float rightMotor, float leftTrigger = 0f, float rightTrigger = 0f, int duration = 500 )
|
||||||
{
|
{
|
||||||
if ( IsDriverActive )
|
Input.TriggerHaptics( leftMotor, rightMotor, leftTrigger, rightTrigger, duration );
|
||||||
{
|
|
||||||
Input.TriggerHaptics( leftMotor, rightMotor, leftTrigger, rightTrigger, duration );
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
public void TriggerHaptics( HapticEffect effect, float lengthScale = 1, float frequencyScale = 1, float amplitudeScale = 1 )
|
public void TriggerHaptics( HapticEffect effect, float lengthScale = 1, float frequencyScale = 1, float amplitudeScale = 1 )
|
||||||
{
|
{
|
||||||
if ( IsDriverActive )
|
Input.TriggerHaptics( effect, lengthScale, frequencyScale, amplitudeScale );
|
||||||
{
|
|
||||||
Input.TriggerHaptics( effect, lengthScale, frequencyScale, amplitudeScale );
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public void StopAllHaptics()
|
public void StopAllHaptics()
|
||||||
{
|
{
|
||||||
if ( IsDriverActive )
|
Input.StopAllHaptics();
|
||||||
Input.StopAllHaptics();
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -10,7 +10,7 @@ namespace VeloX;
|
|||||||
public class EngineStreamPlayer( EngineStream stream ) : IDisposable
|
public class EngineStreamPlayer( EngineStream stream ) : IDisposable
|
||||||
{
|
{
|
||||||
|
|
||||||
private static readonly Mixer EngineMixer = Mixer.FindMixerByName( "Car Engine" );
|
private static readonly Mixer EngineMixer = Mixer.FindMixerByName( "Engine" );
|
||||||
public EngineStream Stream { get; set; } = stream;
|
public EngineStream Stream { get; set; } = stream;
|
||||||
|
|
||||||
public EngineState EngineState { get; set; }
|
public EngineState EngineState { get; set; }
|
||||||
@@ -103,6 +103,7 @@ public class EngineStreamPlayer( EngineStream stream ) : IDisposable
|
|||||||
channel.Position = position;
|
channel.Position = position;
|
||||||
channel.ListenLocal = isLocal;
|
channel.ListenLocal = isLocal;
|
||||||
channel.Paused = EngineSoundPaused || layer.IsMuted;
|
channel.Paused = EngineSoundPaused || layer.IsMuted;
|
||||||
|
channel.FollowParent = true;
|
||||||
channel.TargetMixer = EngineMixer;
|
channel.TargetMixer = EngineMixer;
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user