I find nothing more frustrating in car games than when the developer has implemented some ridiculously unrealistic model for how the car moves. For most games, especially flash-based mini-games, simulation grade 3D physics are not required, but still the car should move and turn roughly like you'd expect a real car to.
Have a quick go at this car parking game, or this one, try to turn into a parking space, something doesn't feel right. More specifically the rear wheels are sliding sideways whenever you turn - this does not (normally) happen in a real car, and it makes this game very hard for all of us used to controlling a real car. I'll show you here how to get better physics than this in just half a dozen lines of code.
The very simple assumption that we will start with is that each wheel can only move in the direction it is pointing. This is actually a very good approximation for normal driving, and exactly appropriate for these kinds of parking games. The other simplification we will make is to use what is called a bicycle model, we imagine that the car has just two wheels; one at the front in the middle to steer, and one at the back in the middle that cannot steer. Of course we can draw a real car with four wheels, but the physics will only be considering two wheels at the centre of each axle.
To keep track of the state of our car in the game we probably have something like:
Vector2 carLocation; float carHeading; float carSpeed; float steerAngle; float wheelBase; // the distance between the two axles
Our physics algorithm should update the carLocation and carHeading each frame according to the above assumptions, and the input part of our game can update carSpeed and steerAngle based on the user input. So how to actually calculate the next position and heading of the car? It can be broken down into three steps:
- Find the world position of the front and back imaginary wheels
- Move each wheel forward in the direction it is pointing
- Calculate the new car location and heading from the new wheel positions
We can use geometry calculations to find the actual positions of the wheels (which are at the centre of each axle). The gap between the axles is given by the wheelBase, so each wheel is half that distance from the car centre, in the direction that the car is facing. The following diagram shows the geometry:
Vector2 frontWheel = carLocation + wheelBase/2 * new Vector2( cos(carHeading) , sin(carHeading) ); Vector2 backWheel = carLocation - wheelBase/2 * new Vector2( cos(carHeading) , sin(carHeading) );
Each wheel should move forward by a certain amount in the direction it is pointing. The distance it needs to move depends on the car speed, and the time between frames (I'll call it dt here). The rear wheel is easy, it moves in the same direction the car is heading. For the front wheel, we have to add the steer angle to the car heading as the diagram shows:
And the code:
backWheel += carSpeed * dt * new Vector2(cos(carHeading) , sin(carHeading)); frontWheel += carSpeed * dt * new Vector2(cos(carHeading+steerAngle) , sin(carHeading+steerAngle));
The new car position can be calculated by averaging the two new wheel positions. The new car heading can be found by calculating the angle of the line between the two new wheel positions:
carLocation = (frontWheel + backWheel) / 2; carHeading = atan2( frontWheel.Y - backWheel.Y , frontWheel.X - backWheel.X );
And that's it - a total of 6 lines of code to get a simple car to move realistically.