| Home Page | Recent Changes

Manual Shift Car

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 * Vect.Y;
                                Dist3 = Sqrt(Dist3);

                DaredevilPoints += Max( Ceil( Abs(InAirSpin)/(0.5*DaredevilThreshInAirSpin) ) - 1, 0 );
                DaredevilPoints += Max( Ceil( Abs(InAirPitch)/(0.5*DaredevilThreshInAirPitch) ) - 1, 0 );
                DaredevilPoints += Max( Ceil( Abs(InAirRoll)/(0.5*DaredevilThreshInAirRoll) ) - 1, 0 );
                DaredevilPoints += Max( Ceil( InAirTime/(0.5*DaredevilThreshInAirTime) ) - 1, 0 );
                DaredevilPoints += Max( Ceil( InAirDistance/(0.5f*DaredevilThreshInAirDistance) ) - 1, 0 );
                DaredevilPoints *= 10;

                // A wheel must be touching the ground on landing to get a daredevil
                if( bVehicleOnGround && DaredevilPoints > 0 )
                {
                    OnDaredevil();
                }
            }


            LastOnGroundLocation = Location;
            LastOnGroundTime = Level.TimeSeconds;
            InAirSpin = 0.0;
            InAirPitch = 0.0;
            InAirRoll = 0.0;
        }
        else
        {
            InAirSpin += (180.0/PI) * DeltaHeading;
            InAirPitch += (180.0/PI) * DeltaPitch;
            InAirRoll += (180.0/PI) * DeltaRoll;
        }

        OldRotation = Rotation;
        bOldVehicleOnGround = bCurrentOnGround;
    }

    if(!bDriving)
    {
        bOldVehicleOnGround = true; // If no-one is in the vehicle, dont consider it for daredevil status.
    }

    if(bAllowChargingJump)
    {
        // Check if all wheels are on the ground.
        if(bAllWheelsOnGround == true)
        {
             for(x = 0; x<Wheels.Length; x++)
             {
             if (Wheels[x].bWheelOnGround != false)
                bAllWheelsOnGround = false;
             }
        }
        else
        {
             // If any wheels are off the ground - we can just reset everything.
             if(!bVehicleOnGround)
             {
                  JumpForce = 0.0;
                  JumpSpin = 0.0;
//                          bPushDown = false;
             }

        }
    }
      //Engine(Throttle);
     // If on the server - we work out OutputGas, OutputBrake etc, and pack them to be sent to the client.
     if(!bClutching)
           Gas2 = InterpCurveEval(RPMtoGas, EngineRPM);
     ProcessCarInput();
     PackState();
}
ProcessCarInput
function ProcessCarInput()
{
        local bool     bReverse;

    bReverse = false;
    if(Driver == none)
    {
        OutputBrake = 1.0;
        OutputGas = 0.0;
    }
    else
    {
        if(Throttle >= 0) // pressing forwards
        {
                        OutputGas = Abs(Throttle);
                        OutputBrake = 0.0;
                        if(bClutching)
                        {
                             if(Throttle == 0)
                             {
                                  if(Gas2<=21 && Gas2>=0)
                                  {
                                        Gas2 -= 0.5;
                                  }
                                  if(Gas2<0)
                                        Gas2 = 0;
                             }
                             else
                             {
                                  if(Gas2<=21 && Gas2>=0)
                                  {
                                         Gas2 += 0.5;
                                  }
                                  if(Gas2>21)
                                         Gas2 = 21;
                             }
                             if(Gas2 >=21)
                                  Gas2 = 21;
                         }
                         if(!bClutching)

                 }

       StoptheCar();
       OutputBrake = Abs(InterpCurveEval(BrakeCurve, hBrake));

    OutputPitch = Rise;
    }
    Engine();
    KWake();
}
Clutch
exec function Clutch(bool bEnabled)
{
       if(!bEnabled)
       {
           bClutching = False;
           bInGear = False;
       }

       else if(bEnabled)
       {
           bClutching = True;

           RPM = EngineRPM;
       }
}
Brake
exec function Brake(bool bEnabled)
{
      if(bEnabled)
      {
           bBraking = True;
           OutputHandbrake = true;
      }
      if(!bEnabled)
      {
           bBraking = false;
           OutputHandbrake = false;
      }
}
ChangeGear
function ChangeGear(int GearChoice)
{
      if(bClutching){
        if(Gear >= 1 && Gear <= 7)
        {
             if(GearChoice == 1)
             {
                  Gear++;
                  bGearUp = 0;
             }
             else if(GearChoice == -1)
             {
                  Gear--;
                  bGearDown = 0;
             }
             else if(GearChoice == 0)
             {

             }
        }
        else if(Gear == 0 && GearChoice == 1)
        {
              Gear++;
              bGearUp = 0;
        }
        else if(Gear == 8 && GearChoice == -1)
        {
              Gear--;
              bGearDown = 0;
        }
        else
        {
             bGearUp = 0;
             bGearDown = 0;
        }
      }
}
PackState
function PackState()
{
    local KRigidBodyState   RBState;
    local rotator           ViewRot;
    if(!KIsAwake())
        return;
    KGetRigidBodyState(RBState);
        CActiveWeapon=SActiveWeapon;
    CarState.ChassisPosition.X = RBState.Position.X;
    CarState.ChassisPosition.Y = RBState.Position.Y;
    CarState.ChassisPosition.Z = RBState.Position.Z;
    CarState.ChassisQuaternion = RBState.Quaternion;
    CarState.ChassisLinVel.X = 10.0 * RBState.LinVel.X;
    CarState.ChassisLinVel.Y = 10.0 * RBState.LinVel.Y;
    CarState.ChassisLinVel.Z = 10.0 * RBState.LinVel.Z;
    CarState.ChassisAngVel.X = 1000.0 * RBState.AngVel.X;
    CarState.ChassisAngVel.Y = 1000.0 * RBState.AngVel.Y;
    CarState.ChassisAngVel.Z = 1000.0 * RBState.AngVel.Z;
        CarState.ServerHandbrake = FloatToRangeByte(OutputPitch);
    CarState.ServerBrake = FloatToRangeByte(OutputBrake);
    CarState.ServerGas = FloatToRangeByte(OutputGas);
    CarState.ServerGear = Gear;
    CarState.ServerSteering = FloatToRangeByte(Steering);
    if(Controller != None)
    {
        if(IsHumanControlled())
        {
            DriverViewPitch = Controller.Rotation.Pitch;
            DriverViewYaw = Controller.Rotation.Yaw;
        }
        else
        {
            ViewRot = rotator(Controller.FocalPoint - Location);
            DriverViewPitch = ViewRot.Pitch;
            DriverViewYaw = ViewRot.Yaw;
        }
    }
    else
    {
        DriverViewPitch = Rotation.Pitch;
        DriverViewYaw = Rotation.Yaw;
    }
    CarState.ServerViewPitch = DriverViewPitch;
    CarState.ServerViewYaw = DriverViewYaw;
    FudgeByte++;
}
Motion Blur (still in the works)
function addblur()
{
      local PlayerController PC;
      
      PC = PlayerController(Controller);
      
      PC.AddCameraEffect(new myBlur);
}
LimitPitch
function int LimitPitch(int pitch)
{
    if (bAllowAirControl && !bVehicleOnGround)
        return pitch;
    return Super.LimitPitch(pitch);
}
Destroyed
simulated function Destroyed()
{
    if (LeftTrail != None)
        LeftTrail.Destroy();

    if (RightTrail != None)
        RightTrail.Destroy();

    Super.Destroyed();
}
TakeImpactDamage
event TakeImpactDamage(float AccelMag)
{
    local int Damage;
        Damage = int(VSize(ImpactInfo.Other.Velocity) * 20 * ImpactDamageModifier());
    if (Vehicle(ImpactInfo.Other) != None)
        TakeDamage(Damage, Vehicle(ImpactInfo.Other), ImpactInfo.Pos, vect(0,0,0), class'DamType');
    else
        TakeDamage(int(AccelMag * ImpactDamageModifier())/ WallCollisionResistance , Self, ImpactInfo.Pos, vect(0,0,0), class'DamType');

    //FIXME - scale sound volume to damage amount
    if (ImpactDamageSounds.Length > 0)        PlaySound(ImpactDamageSounds[Rand(ImpactDamageSounds.Length-1)],,TransientSoundVolume*2.5);
    if (Health < 0 && (Level.TimeSeconds - LastImpactExplosionTime) > TimeBetweenImpactExplosions)
    {
        VehicleExplosion(Normal(ImpactInfo.ImpactNorm), 0.5);
        LastImpactExplosionTime = Level.TimeSeconds;
    }
}
Died
function Died(Controller Killer, class<DamageType> damageType, vector HitLocation)
{
    if (damageType == none)
       return;
    if (Killer != None)        PlayerController(Killer).ReceiveLocalizedMessage(class'VehicleKillMessage', 7);
    Super.Died(Killer, damageType, HitLocation);
}
FloatToRangeByte
simulated function byte FloatToRangeByte(float f)
{
    f= FClamp(f, 0, 1);
    f = Round(f * 255);
    return byte(f);
}
RangeByteToFloat
//The opposite of the above function.
simulated function float RangeByteToFloat(byte b)
{
    local float f;
    f = b;
    f /= 255;
    return f;
}

Defaults

defaultproperties
{
     myBlur=Class'WhatEverPackage.myMotionBlur'

     NumForwardGears=7

     DustSlipRate=2.800000
     DustSlipThresh=50.000000
     DaredevilThreshInAirSpin=100.000000
     DaredevilThreshInAirPitch=300.000000
     DaredevilThreshInAirRoll=300.000000
     DaredevilThreshInAirTime=1.500000
     DaredevilThreshInAirDistance=17.000000
     DaredevilMessageClass=Class'Onslaught.ONSDaredevilMessage'
     bOldVehicleOnGround=True
     MaxJumpSpin=30000.000000
     JumpChargeTime=1.000000
     JumpFeedbackForce="HoverBikeJump"
     JumpSound=Sound'ONSVehicleSounds-S.Hydraulics.Hydraulic10'
     JumpMeterOriginX=0.275000
     JumpMeterOriginY=0.943000
     JumpMeterWidth=0.136000
     JumpMeterHeight=0.057000
     JumpMeterSpacing=0.010000
     JumpMeterColor=(R=215,A=255)
     SpinMeterColor=(B=215,G=100,A=255)
     JumpMeterTexture=Texture'Engine.WhiteTexture'
     MinAirControlDamping=0.100000
     bBrakeFrontWheelsOnly=False
     ChangeUpPoint=2800
     StopThreshold=0.2
     CrosshairHitFeedbackTex=Texture'ONSInterface-TX.tankBarrelAligned'
     DefaultCrosshair=Material'InterfaceContent.Hud.fbBombFocus'
     CrosshairScale=0.125
     bTeamLocked=false
     bGearUp=True
     bInGear=True
     bEjectPassengersWhenFlipped=False
     bCanFlip=true
     DisintegrationHealth=0.0
     bShowChargingBar = false;
     // Damages
     WallCollisionResistance = 5 // Collision resistance: the bigger it is, the less damage taken
     ExplosionDamage=0.0
     DriverDamageMult=0
     CenterSpringRangePitch=5000
     CenterSpringRangeRoll=5000
     StolenAnnouncement="Play"

     bNetNotify=true
}
  • Global variables that would need to be added to the above uscript:
enum ETextAnchor
{
    TA_Left,
    TA_Right,
    TA_Middle
};
var   float                         ScaleX, ScaleY;

CIpen: Here is some simple code you can add to print data on the screen to avoid logging 24/7. This is what I used to check numbers such as RPM, Gas, EngineRPM, and whatever else I could fit on 1024x768 resolution. I've found it to be most handy. I'm sure you can find this elsewhere but I'm saving you the effort of looking.

  • And here is the DrawHUD function that we must have to draw on the screen when a player has possesion of the vehicle:
simulated function DrawHUD(Canvas C)
{
    local PlayerController PC;


    PC = PlayerController(Controller);

    ScaleX = C.ClipX/1024;
    ScaleY = C.ClipY/768;

    C.Style = ERenderStyle.STY_Additive;
    C.Font = PC.MyHUD.GetFontSizeIndex(C, -2);
    C.DrawColor = C.MakeColor(255,255,255);

    DrawEngineStuff(C);
}
  • Here is the DrawEngineStuff(C) which is isn't a required function but is there to organize:
simulated function DrawEngineStuff(Canvas C)
{
    C.Font = C.TinyFont;

    C.SetPos(40*ScaleX, 150*ScaleY);
    C.DrawText("Engine stuff");

    C.SetPos(40*ScaleX, 160*ScaleY);
    C.DrawText("Engine RPM: " @ EngineRPM);
}

40*ScaleX sets the vertical starting position, and 150*ScaleY sets the horizontal starting position.

Notes

If you are to subclass, you need to know that the engine rpm is in radians per min. This means that if you were to create a torque curve and wanted the rpm range to be from 500 to 7000, you would need to convert all the points from revolutions per min to radians per sec. Take the rpm(real life engine rpm) of 7000, divide by 60, then multiply by 2 PI. Like so:

(7000/60)*6.2831853

this yields 733.038285 as the correct rpm

bNetNotify=true is prolly the most critical thing needed for this class to work online/lan

I've got a lot of changes that need to be made to this class, so be patient. Additions include, properly calculating wheel torque if the wheel is powered or not, better collisions with walls(currently if you hit a wall flat at 300 mph the vehicle just stops and takes no damage), available auto transmision, ...

Related Topics

Discussion

Graphik: Very nice idea, but a bit unsuited for an FPS, more for a racing game like UnWheel. It seems to me that Epic made sure that the vehicles were as "dumbed down" as possible to keep the focus on fragging people with a vehicle rather than the minutia of driving one. I recommend sending this in to [UnWheel] when you're done. If they don't actually use it I'm sure they'd at the very least find it interesting.

CIpen: Accually I got a thumbs up on that at the same time I created this page. The bounds of this script is almost limitless, as it would be easy to have say the tire blow out when shot. As soon as I get a free day I'll update this page with all the new additions (infact that might be something good to do tonight). The big change is that I added the option for auto or stick control and the the power is ditributed over the wheels that are powered.

Foxpaw: Why do you divide by 60 when converting rotations per minute to radians per minute? The radians per minute should be greater than the roatations per minute, as there's more than one radian per rotation. Or is that supposed to say, radians per second?

CIpen: Yes, you pointed out a typo :D. Yes, it should say radians per second. Yet your own message has typos, it's not rotations per minute, but revolutions per minute. :)

Foxpaw: Err, ehm, well, I guess that depends on where you measure it from. I'd measure it from the flywheel which definately rotates. I suppose some parts of the crankshaft do revolve though, so either could be correct.


Zxanphorian: WOW, that is a lot of scriptage there! I have an idea, to make it randomly stall :B


CIpen: Ya, there are a lot of cool possibilities with this class. You could attach a model to the wheel bone, add another bone for disk brakes (to attach a disk brake mesh to that turns when the wheels turn), make the engine blow up if you downgear and the engine rpm goes to high, have an axle break, have more wheel slip functions for different traction of different surfaces... any number of things....

OlympusMons: I would have to agree CIpen. Has anyone given any thought to making a proper automatic car you know the manual automatic ones. :S Like what mafia did. I havent really had time to look through all this code although I'd like to, I was just wondering if all of this is actually needed to make a manual shift car or if there are other features which have been added through all of this script. Actually another cool thing to do would be to make a proper 4wd manual shift. Hmm!

CIpen: Good! Lets get to the meat! The first thing you need to know is that the function UpdateVehicle() is somehow tied to native code. To see this all you need to do is log the DeltaTime from inside the Tick() function and from inside UpdateVehicle() function. You should see that UpdateVehicle() always has the same DeltaTime so matter what your FPS is (at least this is what I remember it being when I tested it).

The next serious thing is that, unless you do your own wheel calculations, you'll have to build around the SVehicleWheel variables that are calculated each frame. It's both good and bad I guess. Take a minute to thing about this: below is the line that calculates the GripTorque.

GripTorque = FTScale * Wheels[i].WheelRadius * Wheels[i].TireLoad * WheelLongFrictionScale[0] * InterpCurveEval(Wheels[i].LongFrictionFunc, Abs(Wheels[i].SlipVel));

Now, lets say we want to be the biggest uscript hot shot around, you might change this line to look like this:

if(bPlayerIsOnHood)
     GripTorque = FTScale * Wheels[i].WheelRadius * (Wheels[i].TireLoad + PlayerMass) * WheelLongFrictionScale[0] * InterpCurveEval(Wheels[i].LongFrictionFunc, Abs(Wheels[i].SlipVel));
else
     GripTorque = FTScale * Wheels[i].WheelRadius * Wheels[i].TireLoad * WheelLongFrictionScale[0] * InterpCurveEval(Wheels[i].LongFrictionFunc, Abs(Wheels[i].SlipVel));

Thus simulating player weight on the car(more or less). That is after you do some stuff to get if the player is on the vehicle or not.

btw I just updated it UpdateVehicle() because it was far behind.

Tarquin: I moved this back to its original name, as the class name didn't seem very descriptive.

WinterHummer: Can someong give me a good scorpion subclass of this for me and other lazy people?


Category Tutorial

The Unreal Engine Documentation Site

Wiki Community

Topic Categories

Recent Changes

Offline Wiki

Unreal Engine

Console Commands

Terminology

FAQs

Help Desk

Mapping Topics

Mapping Lessons

UnrealEd Interface

UnrealScript Topics

UnrealScript Lessons

Making Mods

Class Tree

Modeling Topics

Chongqing Page

Log In