214 lines
6.8 KiB
C#
214 lines
6.8 KiB
C#
using System;
|
||
using System.Collections.Generic;
|
||
using Sandbox;
|
||
|
||
namespace VeloX;
|
||
|
||
public partial class Clutch : PowertrainComponent
|
||
{
|
||
protected override void OnAwake()
|
||
{
|
||
base.OnAwake();
|
||
Name ??= "Clutch";
|
||
}
|
||
|
||
/// <summary>
|
||
/// RPM at which automatic clutch will try to engage.
|
||
/// </summary>
|
||
|
||
[Property] public float EngagementRPM { get; set; } = 1200f;
|
||
|
||
public float ThrottleEngagementOffsetRPM = 400f;
|
||
|
||
/// <summary>
|
||
/// Clutch engagement in range [0,1] where 1 is fully engaged clutch.
|
||
/// Affected by Slip Torque field as the clutch can transfer [clutchEngagement * SlipTorque] Nm
|
||
/// meaning that higher value of SlipTorque will result in more sensitive clutch.
|
||
/// </summary>
|
||
[Range( 0, 1 ), Property] public float ClutchInput { get; set; }
|
||
|
||
/// <summary>
|
||
/// Curve representing pedal travel vs. clutch engagement. Should start at 0,0 and end at 1,1.
|
||
/// </summary>
|
||
[Property] public Curve EngagementCurve { get; set; } = new( new List<Curve.Frame>() { new( 0, 0 ), new( 1, 1 ) } );
|
||
|
||
public enum ClutchControlType
|
||
{
|
||
Automatic,
|
||
Manual
|
||
}
|
||
|
||
[Property] public ClutchControlType СontrolType { get; set; } = ClutchControlType.Automatic;
|
||
|
||
/// <summary>
|
||
/// The RPM range in which the clutch will go from disengaged to engaged and vice versa.
|
||
/// E.g. if set to 400 and engagementRPM is 1000, 1000 will mean clutch is fully disengaged and
|
||
/// 1400 fully engaged. Setting it too low might cause clutch to hunt/oscillate.
|
||
/// </summary>
|
||
[Property] public float EngagementRange { get; set; } = 400f;
|
||
|
||
/// <summary>
|
||
/// Torque at which the clutch will slip / maximum torque that the clutch can transfer.
|
||
/// This value also affects clutch engagement as higher slip value will result in clutch
|
||
/// that grabs higher up / sooner. Too high slip torque value combined with low inertia of
|
||
/// powertrain components might cause instability in powertrain solver.
|
||
/// </summary>
|
||
[Property] public float SlipTorque { get; set; } = 500f;
|
||
|
||
|
||
/// <summary>
|
||
/// Amount of torque that will be passed through clutch even when completely disengaged
|
||
/// to emulate torque converter creep on automatic transmissions.
|
||
/// Should be higher than rolling resistance of the wheels to get the vehicle rolling.
|
||
/// </summary>
|
||
[Range( 0, 100f ), Property] public float CreepTorque { get; set; } = 0;
|
||
|
||
[Property] public float CreepSpeedLimit { get; set; } = 1f;
|
||
|
||
|
||
/// <summary>
|
||
/// Clutch engagement based on ClutchInput and the clutchEngagementCurve
|
||
/// </summary>
|
||
[Property, ReadOnly]
|
||
public float Engagement => _clutchEngagement;
|
||
private float _clutchEngagement;
|
||
|
||
protected override void OnStart()
|
||
{
|
||
base.OnStart();
|
||
SlipTorque = Controller.Engine.EstimatedPeakTorque * 1.5f;
|
||
}
|
||
|
||
public override float QueryAngularVelocity( float angularVelocity, float dt )
|
||
{
|
||
InputAngularVelocity = angularVelocity;
|
||
|
||
// Return input angular velocity if InputNameHash or OutputNameHash is 0
|
||
if ( InputNameHash == 0 || OutputNameHash == 0 )
|
||
{
|
||
return InputAngularVelocity;
|
||
}
|
||
|
||
// Adjust clutch engagement based on conditions
|
||
if ( СontrolType == ClutchControlType.Automatic )
|
||
{
|
||
Engine engine = Controller.Engine;
|
||
// Override engagement when shifting to smoothly engage and disengage gears
|
||
if ( Controller.Transmission.IsShifting )
|
||
{
|
||
float shiftProgress = Controller.Transmission.ShiftProgress;
|
||
ClutchInput = MathF.Abs( MathF.Cos( MathF.PI * shiftProgress ) );
|
||
}
|
||
// Clutch engagement calculation for automatic clutch
|
||
else
|
||
{
|
||
|
||
// Calculate engagement
|
||
// Engage the clutch if the input spinning faster than the output, but also if vice versa.
|
||
float throttleInput = Controller.SwappedThrottle;
|
||
float finalEngagementRPM = 500 + ThrottleEngagementOffsetRPM * (throttleInput * throttleInput);
|
||
float referenceRPM = MathF.Max( InputRPM, OutputRPM );
|
||
|
||
ClutchInput = (referenceRPM - finalEngagementRPM) / EngagementRange;
|
||
ClutchInput = Math.Clamp( ClutchInput, 0f, 1f );
|
||
// Avoid disconnecting clutch at high speed
|
||
if ( engine.OutputRPM > engine.IdleRPM * 1.1f && Controller.TotalSpeed > 3f )
|
||
{
|
||
ClutchInput = 1f;
|
||
}
|
||
|
||
//if ( Controller.SwappedBrakes > 0 )
|
||
//{
|
||
// ClutchInput = 0;
|
||
//}
|
||
}
|
||
if ( Controller.IsClutching > 0 )
|
||
{
|
||
ClutchInput = 1 - Controller.IsClutching;
|
||
}
|
||
|
||
}
|
||
else if ( СontrolType == ClutchControlType.Manual )
|
||
{
|
||
// Manual clutch engagement through user input
|
||
ClutchInput = Controller.IsClutching;
|
||
}
|
||
|
||
OutputAngularVelocity = InputAngularVelocity * _clutchEngagement;
|
||
float Wout = Output.QueryAngularVelocity( OutputAngularVelocity, dt ) * _clutchEngagement;
|
||
float Win = angularVelocity * (1f - _clutchEngagement);
|
||
return Wout + Win;
|
||
}
|
||
|
||
public override float QueryInertia()
|
||
{
|
||
if ( OutputNameHash == 0 )
|
||
{
|
||
return Inertia;
|
||
}
|
||
|
||
float I = Inertia + Output.QueryInertia() * _clutchEngagement;
|
||
return I;
|
||
}
|
||
|
||
|
||
public override float ForwardStep( float torque, float inertiaSum, float dt )
|
||
{
|
||
|
||
InputTorque = torque;
|
||
InputInertia = inertiaSum;
|
||
|
||
if ( OutputNameHash == 0 )
|
||
return torque;
|
||
|
||
|
||
// Get the clutch engagement point from the input value
|
||
// Do not use the clutchEnagement directly for any calculations!
|
||
_clutchEngagement = EngagementCurve.Evaluate( ClutchInput );
|
||
_clutchEngagement = Math.Clamp( _clutchEngagement, 0, 1 );
|
||
// Calculate output inertia and torque based on the clutch engagement
|
||
|
||
// Assume half of the inertia is on the input plate and the other half is on the output clutch plate.
|
||
float halfClutchInertia = Inertia * 0.5f;
|
||
OutputInertia = (inertiaSum + halfClutchInertia) * _clutchEngagement + halfClutchInertia;
|
||
|
||
// Allow the torque output to be only up to the slip torque valu
|
||
float outputTorqueClamp = SlipTorque * _clutchEngagement;
|
||
|
||
OutputTorque = InputTorque;
|
||
OutputTorque = Math.Clamp( OutputTorque, 0, outputTorqueClamp );
|
||
float slipOverflowTorque = -Math.Min( outputTorqueClamp - OutputTorque, 0 );
|
||
|
||
// Apply the creep torque commonly caused by torque converter drag in automatic transmissions
|
||
//ApplyCreepTorque( ref OutputTorque, CreepTorque );
|
||
|
||
// Send the torque downstream
|
||
float returnTorque = _output.ForwardStep( OutputTorque, OutputInertia, dt ) * _clutchEngagement;
|
||
|
||
|
||
// Clamp the return torque to the slip torque of the clutch once again
|
||
//returnTorque = Math.Clamp( returnTorque, -SlipTorque, SlipTorque );
|
||
|
||
// Torque returned to the input is a combination of torque returned by the powertrain and the torque that
|
||
// was possibly never sent downstream
|
||
return returnTorque + slipOverflowTorque;
|
||
}
|
||
|
||
|
||
private void ApplyCreepTorque( ref float torque, float creepTorque )
|
||
{
|
||
// Apply creep torque to forward torque
|
||
if ( creepTorque != 0 && Controller.Engine.IsRunning && Controller.TotalSpeed < CreepSpeedLimit )
|
||
{
|
||
bool torqueWithinCreepRange = torque < creepTorque && torque > -creepTorque;
|
||
|
||
if ( torqueWithinCreepRange )
|
||
{
|
||
torque = creepTorque;
|
||
}
|
||
}
|
||
|
||
}
|
||
|
||
}
|