Upcoming Events
Southwest Gaming Expo
11/20 - 11/22 @ Dallas, TX

Workshop on Network and Systems Support for Games (NetGames 2009)
11/23 - 11/25 @ Paris, France

ICIDS 2009 Interactive Storytelling
12/9 - 12/11 @ Guimarăes, Portugal

Global Game Jam
1/29 - 1/31  

More events...


Quick Stats
1277 people currently visiting GDNet.
2341 articles in the reference section.

Help us fight cancer!
Join SETI Team GDNet!



Link to us

Link to us

  Intel sponsors gamedev.net search:   

2D Car Physics


Phase Three - The Vehicle

Assuming everything has gone well above, you should have a rigid body actor in your scene that you can apply forces to and watch move around. Now all that’s left is to calculate these forces in a way that will simulate a vehicle. For that we are going to need a vehicle object. I recommend deriving directly from you rigid body object since the chassis is essentially a rigid body. In addition to that we will need to construct a “wheel" object. This wheel will handle the steering direction of each wheel, the velocity the wheel is spinning, and calculate the forces that that particular wheel applies to the chassis (all in vehicle space). Since our wheel is known to be constrained to the vehicle, we don’t need to simulate it as another rigid body (though you could, but not in the 2D case.) We will simply duplicate the angular properties of the rigid body in the wheel object.

So we’ll need: Wheel Velocity, Wheel Inertia, and and Wheel Torque. We’ll also need the relative offset of the wheel in the vehicle space, and the angle the wheel is facing (this is constant for the back wheels, unless you want 4 wheel steering.) Just like the rigid body, the wheel's torque function acts as an accumulator, we add torques to it and after it gets integrated the torque is zeroed out. The AddTorque function is where you will apply a wheel torque from either the transmission (to make you go) or from the brakes (to make you stop). Internally the wheel will generate a torque caused by the friction on the road.

The wheel object also needs a SetSteering function. This function calculates two vectors: an effective Side Direction, and an effective Forward Direction (both in vehicle space) that the tire patch will act on. The force applied on the tire by the ground acting in the side direction will directly translate into the chassis. Meanwhile the force acting in the forward direction will not only act on the chassis, but it will induce a rotation of the tire. Here is the SetSteering function; you will see I used the Drawing2D.Matrix to transform the initial forward and side vectors by the steering angle (I had to convert the vectors to “points" in order to transform them by the matrix.)

public void SetSteeringAngle(float newAngle)
{
  Matrix mat = new Matrix();
  PointF[] vectors = new PointF[2];

  //foward vector
  vectors[0].X = 0;
  vectors[0].Y = 1;
  //side vector
  vectors[1].X = -1;
  vectors[1].Y = 0;

  mat.Rotate(newAngle / (float)Math.PI * 180.0f);
  mat.TransformVectors(vectors);

  m_forwardAxis = new Vector(vectors[0].X, vectors[0].Y);
  m_sideAxis = new Vector(vectors[1].X, vectors[1].Y);
}

Force Calculation

So, if the vehicle is sitting there not moving with its front wheels turned, and you push it, a force will be generated in the opposite direction you push. This force gets projected onto these two directions. If the wheels were straight there would be no side force. So the vehicle would simply roll forward. But since the wheels are turned, there is a bit of the force that acts in the “effective side direction” so we apply an opposite force to the chassis. This is what causes you to turn when you steer the wheels. To get this force that gets projected onto the two directions, we need to first determine the velocity difference between the tire patch and the road. If the wheel is spinning at the same speed the ground is wizzing by, then there is effectively no force acting on the vehicle. But as soon as you slam on the brakes and stop the wheel, there is a huge velocity difference and this is what causes the force that stops your car.

So here is the process broken down into 6 steps, for each wheel.

Step 1, calculate the effective direction vectors (with steering function).

Step 2, calculate velocity difference. The ground speed is determined via the “PointVel” function on the rigidbody, given the current wheel’s world offset.

Step 3, project this velocity onto the two effective directions.

Step 4, generate an equal and opposite force for the two direction and call this the “response force”. This is what gets added to the chassis for each wheel.

Step 5, calculate the torque that the forward response force created on the wheel, and add this to the wheel torque.

Step 6, integrate the wheel torques into the wheel velocity.

That bit of code looks like this:

public Vector CalculateForce(Vector relativeGroundSpeed, float timeStep)
{
  //calculate speed of tire patch at ground
  Vector patchSpeed = -m_forwardAxis * m_wheelSpeed *
    m_wheelRadius;

  //get velocity difference between ground and patch
  Vector velDifference = relativeGroundSpeed + patchSpeed;

  //project ground speed onto side axis
  float forwardMag = 0;
  Vector sideVel = velDifference.Project(m_sideAxis);
  Vector forwardVel = velDifference.Project(m_forwardAxis, out forwardMag);

  //calculate super fake friction forces
  //calculate response force
  Vector responseForce = -sideVel * 2.0f;
  responseForce -= forwardVel;

  //calculate torque on wheel
  m_wheelTorque += forwardMag * m_wheelRadius;

  //integrate total torque into wheel
  m_wheelSpeed += m_wheelTorque / m_wheelInertia * timeStep;

  //clear our transmission torque accumulator
  m_wheelTorque = 0;

  //return force acting on body
  return responseForce;
}

Almost Done!

We’re in the home stretch here now. Now we have a way to calculate the force each wheel generates on the chassis. Every frame, all we have to do is set our transmission and brake torques, our steering angle, calculate each wheel force, add these to the chassis, and integrate the rigid body. Badaboom badabing, vehicle done! :)



Conclusion

Contents
  Introduction
  Phase One
  Phase Two
  Phase Three
  Conclusion

  Source code
  Printable version
  Discuss this article