I love the smell of UnrealEd crashing in the morning. – tarquin
Legacy:Manual Shift Car
From Unreal Wiki, The Unreal Engine Documentation Site
Contents |
Breakdown
This is a convertion of ONSWheeledCraft. This class allows for a stick shift or otherwise known as a manual transmission car. Why stick shift?
- It doesn't cost much in terms of fps
- Lets you rev it in neutral :)
- Fun to drive
- Will allow rear/front/all wheel drive
- Holding the brake and the rear wheels keep spinning (no rear brakes)
- I hope you can use what I (CIpen) have created
The Goods
To start the class off
class WheeledClutch_Brake extends ONSVehicle;
Critical variables
var() float WheelSoftness;
var() float WheelPenScale;
var() float WheelPenOffset;
var() float WheelRestitution;
var() float WheelAdhesion;
var() float WheelInertia; // I believe this is how much to resist change in motion
//I(CIpen) made these an array so we could have different slips on each surface
var() array<float> WheelLongSlip; // I think this is so we can have the wheels slip when you take off
var() array<InterpCurve> WheelLongFrictionFunc;//this is the curve for how slippery the wheels are allong the X axis(forwards slip)
var() array<InterpCurve> WheelLatSlipFunc;//this is the curve for how slippery the wheels are allong the Y axis(left/right slip)
var() array<float> WheelLongFrictionScale; // quick way to change the ammount of friction without having to draw out
var() array<float> WheelLatFrictionScale; // a new friction curve
var() float WheelHandbrakeSlip;
var() float WheelHandbrakeFriction;
var() float WheelSuspensionTravel;
var() float WheelSuspensionOffset;
var() float WheelSuspensionMaxRenderTravel;
var() float FTScale;
var() float ChassisTorqueScale;
var() float MinBrakeFriction;
var() InterpCurve MaxSteerAngleCurve; // degrees, this makes the wheels turn like real car wheels*
var() float GearRatios[8]; // 0 is reverse, 1-4 f
var() int NumForwardGears;
var() float TransRatio; // Other(constant)gears i.e. so we can have driveshaft ratio(if one applies)
var() float ChangeUpPoint; //EngineRPM that signals gear up change
var() float ChangeDownPoint; //EngineRPM that signals gear down change
var int bGearUp;
var int bGearDown;
var() float LSDFactor;
var() float EngineBrakeFactor;
var() float EngineBrakeRPMScale;
var() float MaxBrakeTorque;
var() float SteerSpeed; // How fast it turns
var() float TurnDamping;
var() float StopThreshold;
var() float HandbrakeThresh;
var() float EngineInertia; // Pre-gear box engine (piston mass)
var bool bClutching; // If holding the clutch
var bool bOldClutching;
var() float IdleRPM; //RPM to idle at
var() float EngineRPMSoundRange; //RPM sound range
var() name SteerBoneName;
var() EAxis SteerBoneAxis;
var() float SteerBoneMaxAngle; // degrees
var float OutputBrake; //How much we are braking
var float OutputGas; //How much are we giving gas
var float OutputPitch;
var bool OutputHandbrake;
var int Gear; //What gear we are in
var float ForwardVel;
var bool bIsInverted;
var bool bIsDriving;
var float NumPoweredWheels;
var float NeutralRPM;
var InterpCurve RPMtoGas; // This is what I use to convert the current in gear RPM to the amount of gas so the RPM doesn't go to zero when we hit the clutch
var() InterpCurve TorqueCurve; // Engine output torque
var() InterpCurve EngineS;
var() InterpCurve BrakeCurve; //This simulates more lifelike braking, where you apply braking
var float Gas, Gas2, hBrake;
var float RPM2; //Neutral RPM
var float RPM; //Neutral RPM
var float NRPM;
var bool bBraking; //if we are braking
var bool bThrot, bNThrot, bNoGas;
var bool bInGear; //if we are completly in gear
var bool bRadians;
var float TotalSpinVel;
var float EngineRPM;
var float WheelRPM;// used to get the wheel RPM at anytime(so we don't change EngineRPM which is what the engine sound is tied to)
var float CarMPH;
var float ETorque;
var float ActualSteering;
var float SteerBoneAngle;
var float EnginePitch;
var Vector worldForward;
var Vector worldRight;
var Vector worldUp;
var Matrix carTM;
var Rotator SteerRot;
var bool bCurrentOnGround;
var float DeltaPitch;
var float DeltaHeading;
var float DeltaRoll;
var float VRate;
var vector one, two, three;
var vector Dist1, Dist2; // unused but this was for doing the daredevil stuff
var float Dist3;
var bool bBrakeFrontWheelsOnly, bBrakeFrontWheels;
var Vector UDForce, UDTorque;
var Vector WForce, WTorque;
var Coords WheelCoords;
var Coords OldCoords;
var Coords Coords;
var Vector ForwardsInOldPlane;
var Pawn OldDriver;
struct native SCarState
{
var vector ChassisPosition;
var Quat ChassisQuaternion;
var vector ChassisLinVel;
var vector ChassisAngVel;
var byte ServerHandbrake;
var byte ServerBrake;
var byte ServerGas;
var byte ServerGear;
var byte ServerSteering;
var int ServerViewPitch;
var int ServerViewYaw;
};
var byte FudgeByte;
var SCarState CarState;
var SCarState OldCarState;
var KRigidBodyState ChassisState;
var bool bNewCarState;
var bool bOldVehicleOnGround;
var float TheDeltaTime;
Not so critical variables
var class<CameraEffect> myBlur;
var myMotionBlur ClientMotionBlur;
var array<ONSDirtSlipEffect> Dust; // FL, FR, RL, RR
var() float DustSlipRate;
var() float DustSlipThresh;
var() float RevMeterPosX;
var() float RevMeterPosY;
var() float RevMeterScale;
var() float RevMeterSizeY;
var() bool bDoStuntInfo;
var() bool bAllowAirControl;
var() bool bAllowChargingJump;
var bool bAllowBigWheels;
var bool bPushDown; //jump is being charged
var bool bOldPushDown;
var bool bAllWheelsOnGround;
var() float MaxJumpForce;
var float JumpForce;
var() float MaxJumpSpin;
var float JumpSpin;
var() float JumpChargeTime;
var float DesiredJumpForce; //used by AI
var string JumpFeedbackForce;
var sound JumpSound;
var() float JumpMeterOriginX;
var() float JumpMeterOriginY;
var() float JumpMeterWidth;
var() float JumpMeterHeight;
var() float JumpMeterSpacing;
var() color JumpMeterColor;
var() color SpinMeterColor;
var Texture JumpMeterTexture;
var() float AirTurnTorque;
var() float AirPitchTorque;
var() float AirPitchDamping;
var() float AirRollTorque;
var() float AirRollDamping;
var() float MinAirControlDamping;
var float FenderBenderSpeed;
var() bool bMakeBrakeLights;
var() vector BrakeLightOffset[2];
var ONSBrakelightCorona BrakeLight[2];
var() Material BrakeLightMaterial;
var Rotator OldRotation;
var Vector LastOnGroundLocation;
var float LastOnGroundTime;
var float InAirSpin; // Degrees
var float InAirPitch; // Degrees
var float InAirRoll; // Degrees
var float InAirTime; // Second
var float InAirDistance; // Meters
var int DaredevilPoints;
var config int IntSteerBoneAngle;
var() float DaredevilThreshInAirSpin;
var() float DaredevilThreshInAirPitch;
var() float DaredevilThreshInAirRoll;
var() float DaredevilThreshInAirTime;
var() float DaredevilThreshInAirDistance;
var() class<LocalMessage> DaredevilMessageClass;
Replication =
replication
{
reliable if (Role == ROLE_Authority)
CarState, FudgeByte; // make sure we get what we need to the client
reliable if (bNetInitial && Role == ROLE_Authority)
bAllowAirControl, bAllowChargingJump; // showoff stuff
reliable if (bNetInitial && bDoStuntInfo && Role == ROLE_Authority)
DaredevilThreshInAirDistance, DaredevilThreshInAirTime, DaredevilThreshInAirSpin, DaredevilThreshInAirPitch, DaredevilThreshInAirRoll;
}
Functions
- PostNetBeginPlay
simulated function PostNetBeginPlay()
{
local int i;
NumPoweredWheels = 0.0;
for(i=0; i<=1; i++)
{
NumPoweredWheels += 1.0;
}
SVehicleUpdateParams();
Super.PostNetBeginPlay();
}
- PostNetReceive
- Here is were we deal with stuff coming from the server.
simulated function PostNetReceive()
{
Super.PostNetReceive();
if(OldCarState.ChassisPosition == CarState.ChassisPosition &&
OldCarState.ChassisQuaternion.X == CarState.ChassisQuaternion.X &&
OldCarState.ChassisQuaternion.Y == CarState.ChassisQuaternion.Y &&
OldCarState.ChassisQuaternion.Z == CarState.ChassisQuaternion.Z &&
OldCarState.ChassisQuaternion.W == CarState.ChassisQuaternion.W &&
OldCarState.ChassisLinVel == CarState.ChassisLinVel &&
OldCarState.ChassisAngVel == CarState.ChassisAngVel &&
OldCarState.ServerHandbrake == CarState.ServerHandbrake &&
OldCarState.ServerBrake == CarState.ServerBrake &&
OldCarState.ServerGas == CarState.ServerGas &&
OldCarState.ServerGear == CarState.ServerGear &&
OldCarState.ServerSteering == CarState.ServerSteering &&
OldCarState.ServerViewPitch == CarState.ServerViewPitch &&
OldCarState.ServerViewYaw == CarState.ServerViewYaw)
return;
ChassisState.Position.X = CarState.ChassisPosition.X;
ChassisState.Position.Y = CarState.ChassisPosition.Y;
ChassisState.Position.Z = CarState.ChassisPosition.Z;
ChassisState.Quaternion = CarState.ChassisQuaternion;
ChassisState.LinVel.X = 0.1f * CarState.ChassisLinVel.X;
ChassisState.LinVel.Y = 0.1f * CarState.ChassisLinVel.Y;
ChassisState.LinVel.Z = 0.1f * CarState.ChassisLinVel.Z;
ChassisState.AngVel.X = 0.001f * CarState.ChassisAngVel.X;
ChassisState.AngVel.Y = 0.001f * CarState.ChassisAngVel.Y;
ChassisState.AngVel.Z = 0.001f * CarState.ChassisAngVel.Z;
bNewCarState = true;
OldCarState.ChassisPosition = CarState.ChassisPosition;
OldCarState.ChassisQuaternion = CarState.ChassisQuaternion;
OldCarState.ChassisLinVel = CarState.ChassisLinVel;
OldCarState.ChassisAngVel = CarState.ChassisAngVel;
OldCarState.ServerHandbrake = CarState.ServerHandbrake;
OldCarState.ServerBrake = CarState.ServerBrake;
OldCarState.ServerGas = CarState.ServerGas;
OldCarState.ServerGear = CarState.ServerGear;
OldCarState.ServerSteering = CarState.ServerSteering;
OldCarState.ServerViewPitch = CarState.ServerViewPitch;
OldCarState.ServerViewYaw = CarState.ServerViewYaw;
OutputPitch = RangeByteToFloat(CarState.ServerHandbrake);
OutputHandbrake = (OutputPitch > 0.01);
OutputBrake = RangeByteToFloat(CarState.ServerBrake);
OutputGas = RangeByteToFloat(CarState.ServerGas);
Gear = CarState.ServerGear;
Steering = RangeByteToFloat(CarState.ServerSteering);
DriverViewPitch = CarState.ServerViewPitch;
DriverViewYaw = CarState.ServerViewYaw;
}
- PrecacheAnnouncer
simulated function PrecacheAnnouncer(AnnouncerVoice V, bool bRewardSounds)
{
if (bRewardSounds && !bSoundsPrecached)
V.PrecacheSound('fender_bender');
Super.PrecacheAnnouncer(V, bRewardSounds);
}
- KUpdateState
- This function tells when we should update.
event bool KUpdateState(out KRigidBodyState newState)
{
if(Role == ROLE_Authority || !bNewCarState)
return false;
newState = ChassisState;
bNewCarState = false;
return true;
}
- SVehicleUpdateParams
event SVehicleUpdateParams()
{
local int i;
Super.SVehicleUpdateParams();
for(i=0; i<Wheels.Length; i++)
{
Wheels[i].Softness = WheelSoftness;
Wheels[i].PenScale = WheelPenScale;
Wheels[i].PenOffset = WheelPenOffset;
Wheels[i].LongSlip = WheelLongSlip[0];
Wheels[i].LatSlipFunc = WheelLatSlipFunc[0];
Wheels[i].Restitution = WheelRestitution;
Wheels[i].Adhesion = WheelAdhesion;
Wheels[i].WheelInertia = WheelInertia;
Wheels[i].LongFrictionFunc = WheelLongFrictionFunc[0];
Wheels[i].HandbrakeFrictionFactor = WheelHandbrakeFriction;
Wheels[i].HandbrakeSlipFactor = WheelHandbrakeSlip;
Wheels[i].SuspensionTravel = WheelSuspensionTravel;
Wheels[i].SuspensionOffset = WheelSuspensionOffset;
Wheels[i].SuspensionMaxRenderTravel = WheelSuspensionMaxRenderTravel;
}
if(Level.NetMode != NM_DedicatedServer && bMakeBrakeLights)
{
for(i=0; i<2; i++)
{
if (BrakeLight[i] != None)
{
BrakeLight[i].SetBase(None);
BrakeLight[i].SetLocation( Location + (BrakelightOffset[i] >> Rotation) );
BrakeLight[i].SetBase(self);
BrakeLight[i].SetRelativeRotation( rot(0,32768,0) );
BrakeLight[i].Skins[0] = BrakeLightMaterial;
}
}
}
}
- UpdateVehicle
- This is the function were everything happens
Also see Manual Shift Car/C++ Version
function UpdateVehicle(float DeltaTime)
{
local Matrix carTM;
local Vector worldUp, worldRight, worldForward;
local KarmaParams KP;
local KRigidBodyState rbState;
local int i;
local float maxSteerAngle, maxSteer, deltaSteer;
local float DriveTorque, GripTorque, EngineTorque, EngineBraking, EngineWheelRatio;
local float BrakeTorque;
local float NewTotalSpinVel, LSDSplit, EvenSplit, UseSplit;
local float TransInertia;
local float LimitBrakeTorque;
local float WheelTorque, VehicleForce, TransAcc;
local float TurnAngVel, DampingScale, TurnDampingMag;
local float PitchAngVel, RollAngVel, PitchDampingMag, RollDampingMag;
local Vector AirControlTorque, AngVel;
local float VRate;
local Vector Force, Torque;
local int z;
WForce = vect(0, 0, 0);
WTorque = vect(0, 0, 0);
if(!KIsAwake())
return;
KP = KarmaParams(KParams);
if(KP == None)
return;
if(Controller != None)
{
//Calc up (z), right(y) and forward (x) vectors
GetAxes(Rotation, worldRight, worldForward, worldUp);
/////////// STEERING ///////////
maxSteerAngle = InterpCurveEval(MaxSteerAngleCurve, VRate);
if(maxSteerAngle==0)
maxSteer = DeltaTime * SteerSpeed * maxSteerAngle;
else
maxSteer = DeltaTime * SteerSpeed;
deltaSteer = (-Steering * maxSteerAngle) - ActualSteering; // Amount we want to move (target - current)
deltaSteer = Clamp(deltaSteer, -maxSteer, maxSteer);
ActualSteering += deltaSteer;
if(bAuto==True)
{
EngineTorque = OutputGas * InterpCurveEval(TorqueCurve, EngineRPM);
EngineBraking = (1.0f - OutputGas) * (EngineBrakeRPMScale*EngineRPM * EngineBrakeRPMScale*EngineRPM * EngineBrakeFactor);
EngineTorque -= EngineBraking;
EngineWheelRatio = GearRatios[Gear] * TransRatio;
NewTotalSpinVel=0.0;
EngineRPM = 0.0;
for(i=0; i<Wheels.length; i++)
{
EvenSplit = 1/NumPoweredWheels;
if(TotalSpinVel > 0.1)
LSDSplit = (TotalSpinVel - Wheels[i].SpinVel)/((NumPoweredWheels-1) * TotalSpinVel);
else
LSDSplit = EvenSplit;
UseSplit = ((1-LSDFactor) * EvenSplit) + (LSDFactor * LSDSplit);
DriveTorque = UseSplit * (EngineTorque / EngineWheelRatio);
GripTorque = FTScale * Wheels[i].WheelRadius * Wheels[i].TireLoad * WheelLongFrictionScale[0] * InterpCurveEval(Wheels[i].LongFrictionFunc, Abs(Wheels[i].SlipVel));
if(Wheels[i].SlipVel < 0.0)
GripTorque *= -1.0;
TransInertia = (EngineInertia / Abs(GearRatios[Gear] * TransRatio)) + Wheels[i].WheelInertia;
if(Wheels[i].SpinVel > 0.0)
BrakeTorque = -OutputBrake * MaxBrakeTorque;
else
BrakeTorque = OutputBrake * MaxBrakeTorque;
LimitBrakeTorque = ( Abs(Wheels[i].SpinVel) * TransInertia ) / TheDeltaTime; // Size of torque needed to completely stop wheel spinning.
BrakeTorque = Clamp(BrakeTorque, -LimitBrakeTorque, LimitBrakeTorque); // Never apply more than this!
WheelTorque = DriveTorque + BrakeTorque - GripTorque;
VehicleForce = GripTorque / (FTScale * Wheels[i].WheelRadius);
if( OutputBrake > 0.0 || (DriveTorque + BrakeTorque) * Wheels[i].SpinVel < 0.0)
{
Wheels[i].DriveForce = 0.0;
Wheels[i].LongFriction = Abs(VehicleForce) + (OutputBrake * MinBrakeFriction);
}
else
{
Wheels[i].DriveForce = VehicleForce;
Wheels[i].LongFriction = 0.0;
}
if (Wheels[i].bWheelOnGround)
Wheels[i].ChassisTorque = -1.0 * (DriveTorque + BrakeTorque) * ChassisTorqueScale;
else
Wheels[i].ChassisTorque = 0.0;
TransAcc = WheelTorque / TransInertia;
Wheels[i].SpinVel += TransAcc * DeltaTime;
if(Gear == 0 && Wheels[i].SpinVel > 0.0)
Wheels[i].SpinVel = 0.0;
else if(Gear > 0 && Wheels[i].SpinVel < 0.0)
Wheels[i].SpinVel = 0.0;
NewTotalSpinVel += Wheels[i].SpinVel;
EngineRPM += Wheels[i].SpinVel / EngineWheelRatio;
Wheels[i].LatFriction = WheelLatFrictionScale[0] * Wheels[i].TireLoad;
Wheels[i].LatSlip = InterpCurveEval(Wheels[i].LatSlipFunc, Wheels[i].SlipAngle);
if(OutputHandbrake && Wheels[i].bHandbrakeWheel)
{
Wheels[i].LatFriction *= Wheels[i].HandbrakeFrictionFactor;
Wheels[i].LatSlip *= Wheels[i].HandbrakeSlipFactor;
}
if(Wheels[i].SteerType == VST_Steered)
Wheels[i].Steer = ActualSteering;
else if(Wheels[i].SteerType == VST_Inverted)
Wheels[i].Steer = -ActualSteering;
else
Wheels[i].Steer = 0.0;
}
EngineRPM /= NumPoweredWheels;
EngineRPM = Max(EngineRPM, 0.01); // ensure always positive!
TotalSpinVel = NewTotalSpinVel;
}
///////////////STICK SHIFT///////////////////////////////
else
{
if(bClutching)
{
EngineTorque = InterpCurveEval(TorqueCurve, EngineRPM);
}
else
{
EngineTorque = InterpCurveEval(TorqueCurve, EngineRPM);
if(EngineTorque >= 0)
{
// Calculate torque at output of engine. Combination of throttle, current RPM and engine braking.
EngineTorque = OutputGas * InterpCurveEval(TorqueCurve, EngineRPM);
}
else if(EngineTorque < 0)
{
EngineTorque = InterpCurveEval(TorqueCurve, EngineRPM);
}
EngineBraking = (1.0f - OutputGas) * (EngineBrakeRPMScale*EngineRPM * EngineBrakeRPMScale*EngineRPM * EngineBrakeFactor);
}
// EngineRPM = OutputGas * InterpCurveEval(RPMCurve, EngineTorque);
EngineTorque -= EngineBraking;
ETorque = EngineTorque;
//DebugInfo = FString::Printf(TEXT("OutputBrake: %f EngineRPM: %f EngineTorque: %f"), OutputBrake, EngineRPM, EngineTorque);
// Total gear ratio between engine and differential (ie before being split between wheels).
// A higher gear ratio and the torque at the wheels is reduced.
EngineWheelRatio = GearRatios[Gear] * TransRatio;
if(bClutching)
{
//EngineRPM = 0.0;
WheelRPM = 0.0;
}
else
{
if(Wheels[i].bPoweredWheel)
{
// Reset engine RPM. We calculate this by adding the component of each wheel spinning.
NewTotalSpinVel=0.0;
if(bInGear)
{
WheelRPM = 0.0;
EngineRPM = 0.0;
}
else if(!bInGear)
WheelRPM = 0.0;
}
}
// Do model for each wheel.
// Okay this needs to be fixed because the way this is, all the wheels get power
for(i=0;i<Wheels.length; i++)
{
//Wheels[i];
/////////// DRIVE ///////////
// Heuristic to divide torque up so that the wheels that are spinning slower get more of it.
// Sum of LSDFactor across all wheels should be 1.
// JTODO: Do we need to handle the case of vehicles with different size wheels?
EvenSplit = 1/NumPoweredWheels;
// If no wheels are spinning, just do an even split.
if(TotalSpinVel > 0.1)
LSDSplit = (TotalSpinVel - Wheels[i].SpinVel)/((NumPoweredWheels-1) * TotalSpinVel);
else
LSDSplit = EvenSplit;
UseSplit = ((1-LSDFactor) * EvenSplit) + (LSDFactor * LSDSplit);
if(bClutching)
{
EngineRPM = RPM;
//DriveTorque = UseSplit * (EngineTorque / EngineWheelRatio);
}
else
{
if(Wheels[i].bPoweredWheel)
{
//EngineRPM = RPM;
// Calculate Drive Torque : applied at wheels (ie after gearbox and differential)
// This is an 'open differential' ie. equal torque to each wheel
DriveTorque = UseSplit * (EngineTorque / EngineWheelRatio);
}
}
/////////// LONGITUDINAL ///////////
// Calculate Grip Torque : longitudinal force against ground * distance of action (radius of tyre)
// LongFrictionFunc is assumed to be reflected for negative Slip Ratio
GripTorque = FTScale * Wheels[i].WheelRadius * Wheels[i].TireLoad * WheelLongFrictionScale[0] * InterpCurveEval(Wheels[i].LongFrictionFunc, Abs(Wheels[i].SlipVel));
if(Wheels[i].SlipVel < 0.0)
GripTorque *= -1.0;
if(Wheels[i].bPoweredWheel)
{
// GripTorque can't be more than the torque needed to invert slip ratio.
TransInertia = (EngineInertia / Abs(GearRatios[Gear] * TransRatio)) + Wheels[i].WheelInertia;
}
//FLOAT SlipAngVel = Wheels[i].SlipVel/Wheels[i].WheelRadius;
if(bBraking)
{
// Brake torque acts to stop wheels (ie against direction of motion)
BrakeTorque = 0.0;
if(Wheels[i].SpinVel > 0.0)
BrakeTorque = -OutputBrake * MaxBrakeTorque;
else
BrakeTorque = OutputBrake * MaxBrakeTorque;
LimitBrakeTorque = ( Abs(Wheels[i].SpinVel) * TransInertia ) / TheDeltaTime; // Size of torque needed to completely stop wheel spinning.
BrakeTorque = Clamp(BrakeTorque, -LimitBrakeTorque, LimitBrakeTorque); // Never apply more than this!
mBrakeTorque = BrakeTorque;
}
else
{
BrakeTorque = 0;
mBrakeTorque = BrakeTorque;
}
//_____________________________________________
if(bClutching)
{
WheelTorque = GripTorque;
//Log("GripTorque");
//Log(GripTorque);
}
else // we are in full gear
{
if(!Wheels[i].bPoweredWheel)
{
//Don't do this if 4 wheel drive!!!!
WheelTorque = GripTorque * 2;
}
else
{
// Resultant torque at wheel : torque applied from engine + brakes + equal-and-opposite from tire-road interaction.
WheelTorque = DriveTorque + BrakeTorque - GripTorque;
/* Log("WheelTorque");
Log(WheelTorque);
Log("GripTorque");
Log(GripTorque); */
}
}
//____________________________________________
// Resultant linear force applied to car. (GripTorque applied at road)
VehicleForce = GripTorque / (FTScale * Wheels[i].WheelRadius);
// If the wheel torque is opposing the direction of spin (ie braking) we use friction to apply it.
if( OutputBrake > 0 && bBraking)// || (DriveTorque + BrakeTorque) * Wheels[i].SpinVel < 0.0
{
Wheels[i].DriveForce = 0.0;
Wheels[i].LongFriction = Abs(VehicleForce) + (OutputBrake * MinBrakeFriction);
}
else if(bClutching)
{
Wheels[i].DriveForce = 0.0;
Wheels[i].LongFriction = 0.0;
}
else
{
if(!Wheels[i].bPoweredWheel)
{
Wheels[i].DriveForce = 0.0;
Wheels[i].LongFriction = 0.0;
}
else
{
Wheels[i].DriveForce = VehicleForce;
Wheels[i].LongFriction = 0.0;
//Wheels[i].LongFriction = 0.0;
}
}
if(Wheels[i].bPoweredWheel)
{
// Calculate torque applied back to chassis if wheel is on the ground
if (Wheels[i].bWheelOnGround)
Wheels[i].ChassisTorque = -1.0 * (DriveTorque + BrakeTorque) * ChassisTorqueScale;
else
Wheels[i].ChassisTorque = 0.0;
}
// Calculate new wheel speed.
// The lower the gear ratio, the harder it is to accelerate the engine.
if(bClutching)
{
TransAcc = WheelTorque / -1;
Wheels[i].SpinVel += TransAcc * DeltaTime;
}
else
{
if(!Wheels[i].bPoweredWheel)
{
TransAcc = WheelTorque / -1;
Wheels[i].SpinVel += TransAcc * DeltaTime;
}
else
{
TransAcc = WheelTorque / TransInertia;
Wheels[i].SpinVel += TransAcc * DeltaTime;
}
}
// -----this is were the engine needs to be slowly put into gear-----
if(bClutching)
{
WheelRPM += (Wheels[i].SpinVel / EngineWheelRatio);
//TorqueConverter();
}
else // we are in full gear
{
if(!Wheels[i].bPoweredWheel)
{
WheelRPM += (Wheels[i].SpinVel / EngineWheelRatio);
}
else
{
// Accumulate wheel spin speeds to find engine RPM.
// The lower the gear ratio, the faster the engine spins for a given wheel speed.
NewTotalSpinVel += Wheels[i].SpinVel;
if(!bInGear)
{
// EngineRPM += (Wheels[i].SpinVel / EngineWheelRatio);
WheelRPM += (Wheels[i].SpinVel / EngineWheelRatio);
TorqueConverter();
}
if(bInGear)
{
WheelRPM += (Wheels[i].SpinVel / EngineWheelRatio);
EngineRPM += (Wheels[i].SpinVel / EngineWheelRatio);
}
}
}
// ---------------------------------------------------------------
// Make sure the wheel can't spin in the wrong direction for the current gear.
if(bClutching)
{
}
else
{
if(Wheels[i].bPoweredWheel)
{
if(Gear == 0 && Wheels[i].SpinVel > 0.0)
Wheels[i].SpinVel = 0.0;
else if(Gear > 0 && Wheels[i].SpinVel < 0.0)
Wheels[i].SpinVel = 0.0;
}
}
/////////// LATERAL ///////////
if(!Wheels[i].bPoweredWheel)
{
Wheels[i].LatFriction = (WheelLatFrictionScale[0] * 1.5) * Wheels[i].TireLoad ;
Wheels[i].LatSlip = InterpCurveEval(Wheels[i].LatSlipFunc, Wheels[i].SlipAngle);
}
else
{
Wheels[i].LatFriction = WheelLatFrictionScale[0] * Wheels[i].TireLoad;
Wheels[i].LatSlip = InterpCurveEval(Wheels[i].LatSlipFunc, Wheels[i].SlipAngle);
}
if(OutputHandbrake && Wheels[i].bHandbrakeWheel)
{
Wheels[i].LatFriction *= Wheels[i].HandbrakeFrictionFactor;
Wheels[i].LatSlip *= Wheels[i].HandbrakeSlipFactor;
}
/////////// STEERING ///////////
// Pass on steering to wheels that want it.
if(Wheels[i].SteerType == VST_Steered)
Wheels[i].Steer = ActualSteering;
else if(Wheels[i].SteerType == VST_Inverted)
Wheels[i].Steer = -ActualSteering;
else
Wheels[i].Steer = 0.0;
}
}
if(bClutching)
{
}
else
{
// EngineRPM is in radians per second, want in revolutions per minute
EngineRPM /= 4;
// EngineRPM /= 2.0 * PI; // revs per sec
// EngineRPM *= 60;
EngineRPM = Max(EngineRPM, 0.00); // ensure always positive!
RPM /= NumPoweredWheels;
// RPM /= 2.0 * PI; // revs per sec
// RPM *= 60;
RPM = Max(EngineRPM, 0.00); // ensure always positive!
}
if(!bClutching)
{
// Update total wheel spin vel
TotalSpinVel = NewTotalSpinVel;
}
// Turn (yaw) damping.
carTM = CachedLocalToWorld; // was carTM = LocalToWorld()
//worldUp(carTM.M[2][0], carTM.M[2][1], carTM.M[2][2]);
//worldRight(carTM.M[1][0], carTM.M[1][1], carTM.M[1][2]);
//worldForward(carTM.M[0][0], carTM.M[0][1], carTM.M[0][2]);
// KGetRigidBodyState(rbState);
// AngVel = KRBVecToVector(rbState.AngVel);
AngVel.X = rbState.AngVel.X;
AngVel.Y = rbState.AngVel.Y;
AngVel.Z = rbState.AngVel.Z;
TurnAngVel = AngVel dot worldUp;
DampingScale = 1.0 - MinAirControlDamping;
if(bAllowAirControl && !bVehicleOnGround)
{
// Log("bVehicleOnGround");
// Log(bVehicleOnGround);
TurnDampingMag = (1.0 - DampingScale*Abs(Steering)) * TurnDamping * TurnAngVel;
//KAddImpulse( WForce, -TurnDampingMag * worldUp );
Force += (WForce + (-1 * TurnDampingMag) * worldUp);
}
else
{
TurnDampingMag = (1.0 - Abs(ActualSteering)) * TurnDamping * TurnAngVel;
//KAddImpulse( WForce, -TurnDampingMag * worldUp );
Force += (WForce + (-1 + TurnDampingMag) * worldUp);
}
// If vehicle is in the air and we are allowing air control...
if(!bVehicleOnGround)
{
PitchAngVel = AngVel dot worldRight;
RollAngVel = AngVel dot worldForward;
if(bAllowAirControl)
{
AirControlTorque = worldRight * OutputPitch * -AirPitchTorque;
if(bIsWalking)
{
// Log("bIsWalking");
// Log(bIsWalking);
AirControlTorque += (worldForward * Steering * -AirRollTorque);
}
else
{
AirControlTorque += (worldUp * Steering * -AirTurnTorque);
}
//KAddImpulse( WForce, AirControlTorque );
Force += AirControlTorque;
// Damping forces
PitchDampingMag = (1.0 - DampingScale*Abs(OutputPitch)) * AirPitchDamping * PitchAngVel;
RollDampingMag = (1.0 - DampingScale*Abs(Steering)) * AirRollDamping * RollAngVel;
//KAddImpulse( WForce, (-PitchDampingMag * worldRight) + (-RollDampingMag * worldForward) );
Force += (WForce + ((-1 + PitchDampingMag) * worldRight) + ((-1 + RollDampingMag) * worldForward));
UDForce = Force;
UDTorque = Torque;
}
else
{
PitchDampingMag = AirPitchDamping * PitchAngVel;
//KAddImpulse( WForce, -PitchDampingMag * worldRight );
Force += (WForce + (-1 + PitchDampingMag) * worldRight);
}
}
else
{
UDForce = Force;
UDTorque = Torque;
}
}
}
- Engine
function Engine()
{
Gas = Gas2;
RPM = InterpCurveEval(EngineS, Gas);
}
- StoptheCar
- This function allows for slower, more real life like breaking
function StoptheCar()
{
if(bBraking)
{
if(hBrake<=21 && hBrake>=0)
{
hBrake += 0.5;
}
if(hBrake>21)
hBrake = 21;
}
if(!bBraking)
{
if(hBrake<=21 && hBrake>=0)
{
hBrake -= 0.5;
}
if(hBrake<0)
hBrake = 0;
}
}
- TorqueConverter
- This function should prolly have a different name, I named it this thinking I would need to accually have a torque converter for the tranny but I turned out making it a function that eases into gear and fixes the rpm to equal that of the engine rpm when the clutch is pressed.
function TorqueConverter()
{
local float rpmDifference;
local float myRPM;
rpmDifference = 0.0;
myRPM = WheelRPM;
myRPM *= 6.28; // convert to radians per sec
myRPM /= 60;
if(EngineRPM == myRPM)
bInGear = True;
else if(EngineRPM > myRPM)
{
if(myRPM < 0)
rpmDifference = EngineRPM - (2 * Abs(myRPM));
else if(myRPM >= 0)
rpmDifference = EngineRPM - myRPM;
if(rpmDifference <= 200.0)
{
bInGear = True;
}
else if(rpmDifference > 200.0)
{
bInGear = False;
rpmDifference = rpmDifference * 0.2;
EngineRPM -= rpmDifference;
bRadians = True;
}
}
else if(EngineRPM < myRPM)
{
rpmDifference = myRPM - EngineRPM;
if(rpmDifference <= 200.0)
{
bInGear = True;
}
else if(rpmDifference > 200.0)
{
bInGear = False;
rpmDifference = rpmDifference * 0.2;
EngineRPM -= rpmDifference;
bRadians = True;
}
}
}
- KApplyForce
function KApplyForce(out vector Force, out vector Torque)
{
Super.KApplyForce(Force, Torque);
Force += UDForce;
Torque += UDTorque;
if (bBoosting == true && bVehicleOnGround == true)
Force += vector(Rotation) * SpeedBoost;
}
- KImpact
event KImpact(Actor Other, vector Pos, vector ImpactVel, vector ImpactNorm)
{
if (Role == ROLE_Authority)
{
ImpactInfo.Other = Other;
ImpactInfo.Pos = Pos;
ImpactInfo.ImpactVel = ImpactVel;
ImpactInfo.ImpactNorm = ImpactNorm;
ImpactInfo.ImpactAccel = KParams.KAcceleration;
ImpactTicksLeft = ImpactDamageTicks;
}
}
- DrivingStatusChanged
event DrivingStatusChanged()
{
local int i;
Super.DrivingStatusChanged();
if (bDriving && Level.NetMode != NM_DedicatedServer && !bDropDetail)
{
Dust.length = Wheels.length;
for(i=0; i<Wheels.Length; i++)
if (Dust[i] == None)
{
// Create wheel dust emitters.
WheelCoords = GetBoneCoords(Wheels[i].BoneName);
Dust[i] = spawn(class'ONSDirtSlipEffect', self,, WheelCoords.Origin + ((vect(0,0,-1) * Wheels[i].WheelRadius) >> Rotation));
Dust[i].SetBase(self);
Dust[i].SetDirtColor( Level.DustColor );
}
if(bMakeBrakeLights)
{
for(i=0; i<2; i++)
if (BrakeLight[i] == None)
{
BrakeLight[i] = spawn(class'ONSBrakelightCorona', self,, Location + (BrakeLightOffset[i] >> Rotation) );
BrakeLight[i].SetBase(self);
BrakeLight[i].SetRelativeRotation( rot(0,32768,0) ); // Point lights backwards.
BrakeLight[i].Skins[0] = BrakeLightMaterial;
}
}
}
else
{
if (Level.NetMode != NM_DedicatedServer)
{
for(i=0; i<Dust.Length; i++)
Dust[i].Destroy();
Dust.Length = 0;
if(bMakeBrakeLights)
{
for(i=0; i<2; i++)
if (BrakeLight[i] != None)
BrakeLight[i].Destroy();
}
}
TurnDamping = 0.0;
}
}
- KDriverEnter
function KDriverEnter(Pawn P)
{
Super.KDriverEnter(P);
}
- ClientKDriverLeave
simulated function ClientKDriverLeave(PlayerController PC)
{
Super.ClientKDriverLeave(PC);
bWeaponIsAltFiring = false;
PC.EndZoom();
}
- SpecialCalcFirstPersonView
simulated function SpecialCalcFirstPersonView(PlayerController PC, out actor ViewActor, out vector CameraLocation, out rotator CameraRotation )
{
ViewActor = self;
CameraLocation = Location + (FPCamPos >> Rotation);
}
- Fire
function Fire(optional float F)
{
VehicleFire(False);
}
- AltFire
function AltFire(optional float F)
{
VehicleFire(true);
}
- VehicleFire
function VehicleFire(bool bWasAltFire)
{
if(!bWasAltFire)
{
bGearUp = 1;
ChangeGear(bGearUp);
}
else if(bWasAltFire)
{
bGearDown = -1;
ChangeGear(bGearDown);
}
}
- VehicleCeaseFire
function VehicleCeaseFire(bool bWasAltFire)
{
if(!bWasAltFire)
{
bGearUp = 0;
ChangeGear(bGearUp);
}
else if(bWasAltFire)
{
bGearDown = 0;
ChangeGear(bGearDown);
}
Super.VehicleCeaseFire(bWasAltFire);
}
- ClientVehicleCeaseFire
simulated function ClientVehicleCeaseFire(bool bWasAltFire)
{
Super.ClientVehicleCeaseFire(bWasAltFire);
}
- Tick
function Tick(float DT)
{
local int i, x;
local bool lostTraction;
local KarmaParams KP;
local KRigidBodyState BodyState;
TheDeltaTime = DT;
Super.Tick(DT);
VRate = VSize(Velocity);
KP = KarmaParams(KParams);
KGetRigidBodyState(BodyState);
SteerBoneAxis = AXIS_Z;
KWake();
if(Role == ROLE_Authority)
{
if(bDriving)
{
// Update ForwardVel, CarMPH and bIsInverted on both server and client.
GetAxes(Rotation, worldForward, worldRight, worldUp);
ForwardVel = Velocity dot worldForward;
CarMPH = Abs((ForwardVel * 3600.0) / 140800.0); // Convert from units per sec to miles per hour.
bIsInverted = worldUp.Z < 0.2;
// Update engine sound pitch
EnginePitch = 255.0 * ((EngineRPM+IdleRPM)/EngineRPMSoundRange);
EnginePitch = Clamp(EnginePitch, 0.0, 255.0);
SoundPitch = EnginePitch;
SteerBoneAngle = (ActualSteering/InterpCurveEval(MaxSteerAngleCurve, VRate)) * SteerBoneMaxAngle * (65535.0/360.0);
one.X = 0;
one.Y = 0;
one.Z = SteerBoneAngle;
two.X = SteerBoneAngle;
two.Y = 0;
two.Z = 0;
three.X = 0;
three.Y = SteerBoneAngle;
three.Z = 0;
if(SteerBoneAxis == AXIS_X)
//SteerRot = Rotator(0, 0, SteerBoneAngle);
SteerRot = Rotator(one);
else if(SteerBoneAxis == AXIS_Y)
//SteerRot = Rotator(SteerBoneAngle, 0, 0);
SteerRot = Rotator(two);
else
//SteerRot = Rotator(0, SteerBoneAngle, 0);
SteerRot = Rotator(three);
SetBoneDirection(SteerBoneName, SteerRot, WForce, 1, 0);
}
}
// Update any stunt variables.
if(bDoStuntInfo)
{
ForwardsInOldPlane = Coords.XAxis - (Coords.XAxis dot OldCoords.ZAxis) * OldCoords.ZAxis;
//ForwardsInOldPlane = SafeNormal(ForwardsInOldPlane);
DeltaHeading = Acos( Clamp( ForwardsInOldPlane dot OldCoords.XAxis, -1.0, 1.0 ) );
if( (ForwardsInOldPlane dot OldCoords.YAxis) < 0.0)
DeltaHeading *= -1.0;
DeltaPitch = Asin( Clamp(Coords.XAxis dot OldCoords.ZAxis, -1.0f, 1.0) );
DeltaRoll = Asin( Clamp(Coords.YAxis dot OldCoords.ZAxis, -1.0f, 1.0) );
//debugf( TEXT("DR:%f DP:%f"), DeltaRoll, DeltaPitch );
bCurrentOnGround = (bVehicleOnGround || KP.bContactingLevel);
DaredevilPoints = 0;
if(bCurrentOnGround)
{
if(!bOldVehicleOnGround)
{
// We just landed - see if we should display Daredevil 'message'
InAirTime = Level.TimeSeconds - LastOnGroundTime;
Dist1 = Location;
Dist2 = LastOnGroundLocation;
Vect = Dist1 - Dist2;
Dist3 = Vect.X * Vect.X - Vect.Y *