Sunday, 20 February 2011

UDK Motorsport: acceleration and braking

The first thing we want to achieve with a car is make it go forward, and eventually make it stop. So let's have a look at how it (roughly) works in real life.

Basically, the engine spins at a certain speed, which makes the wheels (through the drive shaft) spin at the same speed (let's forget about the gear box for a moment). As the wheels spin, they make the car go forward (assuming there's enough grip to the road). Simple, isn't it? (yeah, I simplified a lot)

We're going to have a look at how acceleration works in Unreal, but first, let's talk about torque as I'll be using that term a lot.

A force pushes an object in a certain direction. A torque makes an object rotate around a certain axis. When a engine revs, it applies torque to the drive shaft, which will transmit that torque to the wheels, which makes them spin, setting the vehicle in motion.

In car engine specs, you may find something called torque curves, or dyno graphs. They tell you how much torque an engine is capable of generating depending on its spin velocity (usually stated in RPM).
Dyno Graph of a 2004 Porsche Boxster
In the graph above, we're looking at the blue curve. Torque should be stated in Newton-Metre, but some countries who like to do things their way use lb-ft. I reckon Unreal uses the former unit, but I can't say for sure.

Unreal uses such a curve, but there are some simplifications: it considers that the engine torque is directly transferred to the wheels (i.e. no loss, no gearbox). As a result, the variable used in the torque curve is not the engine's RPM, but the vehicle's speed (in UU/s). The max torque applied to a wheel is the value of the curve for the current speed, multiplied by Throttle. The maximum torque is not applied straight away but is increasing over time according to the value of UDKVehicleSimCar.ThrottleSpeed.

There is one very important thing to note: being a Pawn, a vehicle obeys to the GroundSpeed and AirSpeed variables. If the vehicle goes beyond those speeds it will simply stop applying torque (even braking torque) until the vehicle slows back down under that limit. Took me 2 days to figure this out, and I did not notice it at first because my previous test level was too small so I couldn't reach the limit speed.

Using this to actually limit the speed is a bad thing as the controls will appear (and to some extent will be) broken if the vehicle goes over this limit (slopes, boosts, etc). So in order to do it in a neater way, let's have a look at the Scorpion's torque curve (UDKVehicleSimCar.TorqueVSpeedCurve):

It's mostly flat, meaning constant acceleration, then drops beyond 1050 UU/s, effectively achieving a maximum speed.

Just change the curve if you want something more realistic, but don't forget to make it drop to 0 at some point.
My custom torque curve
Note that the red cross is stuck at that position as I reach my maximum speed, as (most likely) the provided torque is not enough to make the car go any faster.

Now let's talk about braking.

Remember that the speed of the wheels and speed of the engine are dependent on one another since they're linked together. As such, braking can be done in two ways: either you slow the engine down (engine brake) or you slow the wheels down (wheel brake).

This is where the first hurdle comes in UDK (yeah, so soon): wheel brake is handled internally in UDKVehicleSimCar. It only happens when you apply reverse throttle while moving. As a result, you can't really accelerate and (wheel) brake at the same time (something you may find yourself doing if you want good lap times).

Also, as we're applying reverse torque to the wheel rather than actually reducing their speed, it seems like it's impossible to lock the wheels (at the very least, not from Unrealscript). We're stuck with an ABS-like braking, not very handy for drifting.

Wheel and engine brake are achieved by applying reverse torque to the wheels, in order to slow them down. Braking power is controlled by UDKVehicleSimCar.MaxBraqueTorque for wheel brake, and UDKVehicleSimCar.EngineBrakeFactor for engine brake.

I've managed to put acceleration and braking on different buttons by doing some arbitrary combination:

state PlayerDriving
ignores SeePlayer, HearNoise, Bump;

 function ProcessMove(float DeltaTime, Vector NewAccel, eDoubleClickDir DoubleClickMove, rotator DeltaRot);

 function PlayerMove( float DeltaTime )
  local float throttle, temp_right, temp_left;

  temp_right = PlayerInput.aRightAnalogTrigger;
  if (temp_right >= 1)
   temp_right -= 1; //Remember that triggers return a value in the [1,2] range

  temp_left = PlayerInput.aLeftAnalogTrigger;
  if (temp_left >= 1)
   temp_left -= 1;
  //NOTE: you might need to take into account the potential scaling factors in the input.

  throttle = temp_right - 2* temp_left; //give priority to braking

  // update 'looking' rotation

  // TODO: Don't send things like aForward and aStrafe for gunners who don't need it
  // Only servers can actually do the driving logic.
  ProcessDrive(throttle, PlayerInput.RawJoyRight, PlayerInput.aUp, bPressedJump);
  if (Role < ROLE_Authority)
   ServerDrive(throttle , PlayerInput.RawJoyRight, PlayerInput.aUp, bPressedJump, ((Rotation.Yaw & 65535) << 16) + (Rotation.Pitch & 65535));

  bPressedJump = false;