| Home Page | Recent Changes

Manual Shift Car/CPlusPlus Version

UpdateVehicle

  • This is so you can see what I started with in hopes you can derive something better someday :)
void AONSWheeledCraft::UpdateVehicle(FLOAT DeltaTime)
{
    guard(AONSWheeledCraft::UpdateVehicle);

    /////////// STEERING ///////////

    FLOAT maxSteerAngle = MaxSteerAngleCurve.Eval(Velocity.Size());

#if 0
    FLOAT maxSteer = DeltaTime * SteerSpeed * maxSteerAngle;
#else
    FLOAT maxSteer = DeltaTime * SteerSpeed;
#endif

    FLOAT deltaSteer;
    
    // When using Jumping Vehicle mutator and holding down to jump, always steer straight.
    if(!bPushDown)
        deltaSteer = (-Steering * maxSteerAngle) - ActualSteering; // Amount we want to move (target - current)
    else
        deltaSteer = -ActualSteering; 

    deltaSteer = Clamp<FLOAT>(deltaSteer, -maxSteer, maxSteer);
    ActualSteering += deltaSteer;

    /////////// ENGINE ///////////

    // Calculate torque at output of engine. Combination of throttle, current RPM and engine braaking.
    FLOAT EngineTorque = OutputGas * TorqueCurve.Eval( EngineRPM );
    FLOAT EngineBraking = (1.0f - OutputGas) * (EngineBrakeRPMScale*EngineRPM * EngineBrakeRPMScale*EngineRPM * EngineBrakeFactor);

    EngineTorque -= EngineBraking;

    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.
    FLOAT EngineWheelRatio = GearRatios[Gear] * TransRatio;

    // Reset engine RPM. We calculate this by adding the component of each wheel spinning.
    FLOAT NewTotalSpinVel=0.0f;
    EngineRPM = 0.0f;

    // Do model for each wheel.
    for(INT i=0; i<Wheels.Num(); i++)
    {
        USVehicleWheel* vw = 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?
        FLOAT LSDSplit, EvenSplit, UseSplit;

        EvenSplit = 1/NumPoweredWheels;

        // If no wheels are spinning, just do an even split.
        if(TotalSpinVel > 0.1f)
            LSDSplit = (TotalSpinVel - vw->SpinVel)/((NumPoweredWheels-1) * TotalSpinVel);
        else
            LSDSplit = EvenSplit;

        UseSplit = ((1-LSDFactor) * EvenSplit) + (LSDFactor * LSDSplit);

        // Calculate Drive Torque : applied at wheels (ie after gearbox and differential)
        // This is an 'open differential' ie. equal torque to each wheel
        FLOAT 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
        FLOAT GripTorque = FTScale * vw->WheelRadius * vw->TireLoad * WheelLongFrictionScale * vw->LongFrictionFunc.Eval( Abs(vw->SlipVel) );
        if(vw->SlipVel < 0.0f)
            GripTorque *= -1.0f;

        // GripTorque can't be more than the torque needed to invert slip ratio.
        FLOAT TransInertia = (EngineInertia / Abs(GearRatios[Gear] * TransRatio)) + vw->WheelInertia;
        //FLOAT SlipAngVel = vw->SlipVel/vw->WheelRadius;

        // Brake torque acts to stop wheels (ie against direction of motion)
        FLOAT BrakeTorque = 0.0f;

        if(vw->SpinVel > 0.0f)
            BrakeTorque = -OutputBrake * MaxBrakeTorque;
        else
            BrakeTorque = OutputBrake * MaxBrakeTorque;

        FLOAT LimitBrakeTorque = ( Abs(vw->SpinVel) * TransInertia ) / DeltaTime; // Size of torque needed to completely stop wheel spinning.
        BrakeTorque = Clamp(BrakeTorque, -LimitBrakeTorque, LimitBrakeTorque); // Never apply more than this!

        // Resultant torque at wheel : torque applied from engine + brakes + equal-and-opposite from tire-road interaction.
        FLOAT WheelTorque = DriveTorque + BrakeTorque - GripTorque;
    
        // Resultant linear force applied to car. (GripTorque applied at road)
        FLOAT VehicleForce = GripTorque / (FTScale * vw->WheelRadius);

        // If the wheel torque is opposing the direction of spin (ie braking) we use friction to apply it.
        if( OutputBrake > 0.0f ||  (DriveTorque + BrakeTorque) * vw->SpinVel < 0.0f)
        {
            vw->DriveForce = 0.0f;
            vw->LongFriction = Abs(VehicleForce) + (OutputBrake * MinBrakeFriction);
        }
        else
        {
            vw->DriveForce = VehicleForce;
            vw->LongFriction = 0.0f;
        }

        // Calculate torque applied back to chassis if wheel is on the ground
        if (vw->bWheelOnGround)
            vw->ChassisTorque = -1.0f * (DriveTorque + BrakeTorque) * ChassisTorqueScale;
        else
            vw->ChassisTorque = 0.0f;

        // Calculate new wheel speed. 
        // The lower the gear ratio, the harder it is to accelerate the engine.
        FLOAT TransAcc = WheelTorque / TransInertia;
        vw->SpinVel += TransAcc * DeltaTime;

        // Make sure the wheel can't spin in the wrong direction for the current gear.
        if(Gear == 0 && vw->SpinVel > 0.0f)
            vw->SpinVel = 0.0f;
        else if(Gear > 0 && vw->SpinVel < 0.0f)
            vw->SpinVel = 0.0f;

        // Accumulate wheel spin speeds to find engine RPM. 
        // The lower the gear ratio, the faster the engine spins for a given wheel speed.
        NewTotalSpinVel += vw->SpinVel;
        EngineRPM += vw->SpinVel / EngineWheelRatio;

        /////////// LATERAL ///////////

        vw->LatFriction = WheelLatFrictionScale * vw->TireLoad;
        vw->LatSlip = vw->LatSlipFunc.Eval(vw->SlipAngle);

        if(OutputHandbrake && vw->bHandbrakeWheel)
        {
            vw->LatFriction *= vw->HandbrakeFrictionFactor;
            vw->LatSlip *= vw->HandbrakeSlipFactor;
        }

        /////////// STEERING  ///////////

        // Pass on steering to wheels that want it.
        if(vw->SteerType == VST_Steered)
            vw->Steer = ActualSteering;
        else if(vw->SteerType == VST_Inverted)
            vw->Steer = -ActualSteering;
        else
            vw->Steer = 0.0f;
    }

    // EngineRPM is in radians per second, want in revolutions per minute
    EngineRPM /= NumPoweredWheels;
    EngineRPM /= 2.0f * (FLOAT)PI; // revs per sec
    EngineRPM *= 60;
    EngineRPM = Max( EngineRPM, 0.01f ); // ensure always positive!

    // Update total wheel spin vel
    TotalSpinVel = NewTotalSpinVel;

    // Turn (yaw) damping.
    FMatrix carTM = LocalToWorld();
    FVector worldUp(carTM.M[2][0], carTM.M[2][1], carTM.M[2][2]);
    FVector worldRight(carTM.M[1][0], carTM.M[1][1], carTM.M[1][2]);
    FVector worldForward(carTM.M[0][0], carTM.M[0][1], carTM.M[0][2]);

    FKRigidBodyState rbState;
    KGetRigidBodyState(&rbState);
    FVector AngVel(rbState.AngVel.X, rbState.AngVel.Y, rbState.AngVel.Z);
    FLOAT TurnAngVel = AngVel | worldUp;

    FLOAT DampingScale = 1.0f - MinAirControlDamping;

    if(bAllowAirControl && !bVehicleOnGround)
    {
        FLOAT TurnDampingMag = (1.0f - DampingScale*Abs(Steering)) * TurnDamping * TurnAngVel;
        KAddForces( FVector(0,0,0), -TurnDampingMag * worldUp );
    }
    else
    {
        FLOAT TurnDampingMag = (1.0f - Abs(Steering)) * TurnDamping * TurnAngVel;
        KAddForces( FVector(0,0,0), -TurnDampingMag * worldUp );
    }

    // If vehicle is in the air and we are allowing air control...
    if(!bVehicleOnGround)
    {
        FLOAT PitchAngVel = AngVel | worldRight;
        FLOAT RollAngVel = AngVel | worldForward;

        if(bAllowAirControl)
        {
            FVector AirControlTorque = worldRight * OutputPitch * -AirPitchTorque;

            if(bIsWalking)
                AirControlTorque += (worldForward * Steering * -AirRollTorque);
            else
                AirControlTorque += (worldUp * Steering * -AirTurnTorque);

            KAddForces( FVector(0,0,0), AirControlTorque );

            // Damping forces
            FLOAT PitchDampingMag = (1.0f - DampingScale*Abs(OutputPitch)) * AirPitchDamping * PitchAngVel;
            FLOAT RollDampingMag = (1.0f - DampingScale*Abs(Steering)) * AirRollDamping * RollAngVel;

            KAddForces( FVector(0,0,0), (-PitchDampingMag * worldRight) + (-RollDampingMag * worldForward) );
        }
        else
        {
            FLOAT PitchDampingMag = AirPitchDamping * PitchAngVel;
            KAddForces( FVector(0,0,0), -PitchDampingMag * worldRight );
        }

    }

    unguard;
}

Foxpaw: The title of this page is a little misleading. It should perhaps be a subpage of your manual shifting vehicle page.

Also I'm a bit confused. You made a C++ version before making an Unrealscript version? Does this mean you have the native headers for UT2004?

CIpen: Sorry about that one :). No, the code here is from http://www.psyonix.com/ and no, I do not have the native headers. Even if I did have the headers, would it be good to have everything in script? No, I don't think you want to be doing second order derivatives in script to calculate suspension/chassy movement.

Foxpaw: I don't see any reason why UScript couldn't handle that, as it is quite fast for an interpreted language. I also don't see any derivatives being calculated at all in that source.

Anyways, that's slightly besides the point. I see now that this is the C++ code for an onslaught wheeled vehicle, not the C++ code for the "manual shift car" that you created. The name somewhat implied that this was a C++ version of the UScript car detailed on it's parent page, which would have required the native headers to get into the game. (If you had made the "manual shift car" in C++, that is.)

CIpen: Hey Foxpaw, that's nice you are pointing this out. I'll remember you for the next time I need someone to show me that I'm not half way through writing my page(the obvious).

Ya, double briliant, unreal script can not even handle simple rotations per min to radians per min convertion and you are claiming it can calculate second order derivatives on the fly? Where is this script at? Foxpaw show me were your unreal script is that will calculate the spring motion of a car tire + spring + shock + engine torque in unreal script on the fly.

Foxpaw: Err, that was a bit rude, but anyway, I'm not sure where you got your ideas about what Unrealscript can handle. RPM can be easily converted to radians per minute with one line: RadiansPerMinute = RPM * 2 * Pi; Unrealscript is a more-or-less full featured programming language: except for a few things that are restricted for security purposes, Unrealscript can do everything that C++ can do.

I don't have any code to calculate motion of tires since I haven't coded any land vehicles, but my mod handles hundreds, if not thousands of coordinate rotation and other matrix operations every tick, and still runs at over 200 fps - so I'm sure that it has the capability to handle calculating a few derivatives at acceptable speed.

CIpen: I made this page to show the native function UpdateState(). Foxpaw, did you have a question about the function or did you come to boast about your mod? Please Foxpaw, if it isn't about the code above please discuss it in the forums.

Foxpaw: Errr, I made a comment regarding the title of this page, and asked what this was since there was very little other than the code and a short note at the top stating that this is what you started with. You responded saying that you got the code from Psyonix at which point I realized what this was. You also said that you wouldn't want to do second order derivatives in Unrealscript. I said that I now realized what this was but said that I saw no reason why you couldn't do derivatives in UnrealScript. Then you responded defensively and made a remark about the capability (or lack thereof) of Unrealscript to perform even a simple calculation, let alone a complex one. I refuted that statement with an example of complex calculations being performed in Unrealscript. (my mod)

So, I don't see why "now I see what this page is about" prompted the rude response: " Hey Foxpaw, that's nice you are pointing this out. I'll remember you for the next time I need someone to show me that I'm not half way through writing my page(the obvious)." I also don't see why me showing you that you can in fact perform an operation you claimed impossible offends you.

CIpen: "I'm not half way through writing my page", did you miss this part of the sentence? :D

Okay, yes, you can do complex calculations. You win. I do not have the time to answer this, so, you're right. And when you're right, you're right. But why even convert rotations per min to radians per min if you don't have to? And what if there is already a lot eye candy? Yes you're right, it is optional to optimize your code/map/whatever.

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