Monday, 12 April 2010

Simple 2D car steering physics in games




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:
  1. Find the world position of the front and back imaginary wheels
  2. Move each wheel forward in the direction it is pointing
  3. Calculate the new car location and heading from the new wheel positions
Step 1
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:

And the code:
Vector2 frontWheel = carLocation + wheelBase/2 * new Vector2( cos(carHeading) , sin(carHeading) );
Vector2 backWheel = carLocation - wheelBase/2 * new Vector2( cos(carHeading) , sin(carHeading) );

Step 2
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));

Step 3
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:

In code:
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.


  1. Hello,

    Thanks for your code. It was helpful, but i'm still having problems to make the car move right.

    My car looks like in a soap road...sliding or spinning like a fan.

    Can you give some example using the structure of a XNA Game? Like...putting the code into the right methods?

    Best regards.

    1. you have to add friction you dumb dumb

    2. Any Idea how to implement this

  2. Assuming you have a Car class, you would need to put the 5 fields I mention at the top in this Car class. The Car's Update method would then include the 6 lines I mentioned. You probably also want to set the carSpeed and steerAngle variables based on some keyboard input.

    In the Car's Draw method you would then use carLocation and carHeading to actually draw the car.

    1. Hello engineer, I am doing a project on opengl to drive a tank, the tank was given to us the issue is to move the tank, could you explain your code a bit more. I am not also aable to add carLocation with wheelBase for some reason. any pointers would help

    2. Vector2 is the XNA class for a 2D vector. You cannot add a scalar (like wheelBase) to a 2D vector. The first line of code multiplies wheelBase by a 2D direction vector (which results in a new 2D vector scaled by the value of wheelBase) and then adds that to carLocation.

  3. Sweet.. Breaking down into two steps is something i had never considered.. thnx :)

  4. Christopher Night1 September 2011 at 18:34

    Hey, I think there's a problem with this algorithm. It doesn't conserve the baseline, that is, the distance between the front and rear tire. Over time, if you move forward while turning, the baseline will shrink down to carSpeed * dt, and the motion will become completely unrealistic. I tested it out with an initial baseline of 50, carSpeed = 50, dt = 1/60, and steerAngle = 20 degrees. By frame 500 the baseline had shrunk down to 26.2.

    Maybe the most realistic way to rectify this is to have the front tire move extra in order to maintain the baseline. I get that the front tire must move a distance of sqrt(B^2+C)-B, where:

    B = (b-r) cos(steerAngle)
    C = r(2b-r)
    b is the size of the baseline
    r = carSpeed * dt is the distance the rear wheel moves this frame

    If you use this quantity in place of r in your update of frontWheel, the baseline should be conserved.

    1. I noticed this too. The change you added seemed to work.

  5. Thanks for the reply Christopher, however you need to re-calculate all 6 lines every frame. This will recalculate the front and rear wheel positions from scratch based on the car center point and heading every frame. You'll then find the distance between the two wheels is always equal to wheelBase.

  6. Christopher Night2 September 2011 at 16:50

    Ah, I understand. Yes, that definitely means the problem I was seeing was my mistake. It looks good!

    I think that my modification makes it slightly more realistic, but it's probably not worth the effort. Thanks for the response!

  7. Yes your solution is definitely more accurate, and at very sharp steering angles (nearer to 90 degrees rather than straight ahead) it should make a visible difference. I think we also have to modify the rear wheel distance, as at a 90 degree steer angle the rear wheel should stay stationary - also how to define what carSpeed means in this situation?

    But whichever way you use, it's not a good idea to keep track of each wheel totally separately, they will drift apart due to numerical precision errors no matter how good the maths is.

  8. how you work with floats and int conversion?

    cos(angle)* speed; 2.40232;
    posx and posy are natural numbers 1,2,3,4 ...
    i have troubles with that.. the "car" is moving to the left and up the screen.

    (i use SDL to simulation)

  9. Here I'm just using floats, but I understand the reasons for wanting to avoid them for posx and posy (accuracy loss with large values).

    If cos(angle)*speed*dt is coming out around 2.4, then you shouldn't use ints alone for the position, there isn't enough resolution to make the car move the direction it is facing.

    If you need to avoid doubles/floats then try fixed point, it will work if you use enough bits in the fractional part, but the math become a little more complex. You might even need to use a separate variable to store the fractional part of the result (and then posx and posy can remain as integers). This should solve your problem.

  10. hello. i hope this isn't too much to ask, but could you do the same example, but without the vectors. this is like the only example ive found on the net i can understand, all the others are waay to complicated. the problem im having is that i write in blitz basic. and it doesn't use vectors. ive tried converting your code but it doesnt seem to work for me. so could you try to write the code again, except without all the vector operations?

  11. Hi Anonymous, it will work if you just do the x and y component separately for each line.

    In your case you will need to keep track of carLocationX and carLocationY as separate variables from frame to frame. Within each frame you then need to do the steps I mentioned, but split into X and Y components. Something like this:

    frontWheelX = carLocationX + wheelBase/2 * cos(carHeading);
    frontWheelY = carLocationY + wheelBase/2 * sin(carHeading);

    backWheelX = carLocationX - wheelBase/2 * cos(carHeading);
    backWheelY = carLocationY - wheelBase/2 * sin(carHeading);

    backWheelX += carSpeed * dt * cos(carHeading);
    backWheelY += carSpeed * dt * sin(carHeading);

    frontWheelX += carSpeed * dt * cos(carHeading+steerAngle);
    frontWheelY += carSpeed * dt * sin(carHeading+steerAngle);

    carLocationX = (frontWheelX + backWheelX) / 2;
    carLocationY = (frontWheelY + backWheelY) / 2;

    carHeading = atan2( frontWheelY - backWheelY , frontWheelX - backWheelX );

    Let me know if you're still having problems.

  12. thank you so much it works perfectly now. i see what i did wrong, i had the variables aligned in the wrong order. lol now have to learn how to make the car drift. *prepares self for difficult journey into car physics*

  13. If you want physically correct drifting with realistic control, then you want to totally ignore this tutorial and start again considering forces and accelerations on the tyres. However there is a quick hack I found that you can add to this tutorial that gives a rough impression of drifting for very little effort.

    The trick is to create a delay on the carHeading value, so that the value used in the sin and cos terms when moving the wheels forwards, is the value from a few frames earlier. But still use the instantaneous value for drawing the car, so this gives the impression that the car is sliding sideways a bit (the length of the delay controls how much the car will slide). It might take some tweaking to get it to work, because after all this is just a pure hack to give a very rough approximation of drifting.

  14. Hi

    Firstly thanks for the great tutorial. I'm having a bit of a problem though (using C#). My car heading value keeps getting stuck between two values and my car just rotates a little to the left and then back to the right, even when I am holding just one key (eg. 'a' key).

    I'm using this atm

    carHeading = (float)Math.Atan2(Convert.ToDouble(frontWheel.Y - backWheel.Y), Convert.ToDouble(frontWheel.X - backWheel.X));

    I have to convert to double because the functions take doubles, but then i have to change back to float because Vectors use float.

    Thanks if you can help me any.

  15. Hi James, that line looks fine to me, the mistake must be in the rest of your code. Are you running all 6 lines of code every frame? What if you fix steerAngle and carSpeed to be constant - does the car drive around in a circle?

  16. Hey TheEngineer

    I think i may have fixed it! And it is all thanks to you suggesting fixing the steerAngle and carSpeed to a constant. That really helped me find out what was really going on. Good call :)
    It seems to be working as expected right now.

    What i'd like to try now is to get the steer angle to sort of square up with the car heading while not holding down the left or right (a or d) keys. If you know what i mean? At the moment. Sort of how when you let go of the steering wheel in a real car the wheels will go back straight. I think this might just be as easy as adding or subtracting to the steering angle until it is the same as the car heading?

    Once again thanks!

    - James

  17. Hi James, glad you got it working. To return the steering angle back to zero, I usually just subtract a fixed amount back towards zero when neither left nor right are being pressed. Watch out not to overshoot zero though. Something like:

    const float returnRate = 0.01 * dt;
    if( !A_Pressed && !D_Pressed)
    float delta = steerAngle;
    if( delta > returnRate ) delta = returnRate;
    if( delta <-returnRate ) delta =-returnRate;
    steerAngle -= delta;

    You can adjust returnRate to set how quickly the wheels return back to centre. I prefer it to be a bit faster than the rate at which the wheels turn when holding down the key - you then let the player get some quite smooth turning by just tapping the a or d keys.

    You could even make returnRate proportional to the car speed, that might be more realistic (when you are stopped the wheels then wouldn't return back, which is correct).

  18. Thanks for the simple and easy to follow tut on steering.

    For those interested in drifting, another option/hack is to create a 'carDriftHeading' variable that is calculated something like this;
    carDriftHeading = carHeading + (steerAngle * (carSpeed/carSpeedMax) * driftDampening)

    This doesn't actually produce any real drift, rather the illusion of drift by over rotating the car at high speeds so may not suit some situations but it will give almost no drift at low speeds and a lot of drift at high speeds so it is somewhat realistic and when tunned right, provides a fun driving experience.

    I suspect combining a little of this approach and a little of the approach
    'The Engineer' mentions above might be enough to get a lo-fi game up and running.

  19. Hey TheEngineer,

    Thanks for the tutorial. I have been looking for days for a simple car driving algorithm for days!

    That said, I'm having some problem with my code. For some reason my car simply jitters in place, maybe moving like 2 pixels back and forth not sure why. I think the problem might be converting to and from Radians to Degrees. The SteerAngle is set to 45 degrees and the CarSpeed is 1.5f.

    float controlAngle = 45f;
    float carFacingAngle = MathUtils.radiansToDegrees * mBody.getAngle();
    steerAngle = controlAngle - carFacingAngle; // angle from Car to Control

    carSpeed = 1.5f;

    frontWheel.x = carLocation.x + wheelBase/2 * MathUtils.sin(carHeading);
    frontWheel.y = carLocation.y + wheelBase/2 * MathUtils.cos(carHeading);

    backWheel.x = carLocation.x - wheelBase/2 * MathUtils.sin(carHeading);
    backWheel.y = carLocation.y - wheelBase/2 * MathUtils.cos(carHeading);
    backWheel.x += carSpeed * deltaTime * MathUtils.sin(carHeading);
    backWheel.y += carSpeed * deltaTime * MathUtils.cos(carHeading);

    frontWheel.x += carSpeed * deltaTime * MathUtils.sin(carHeading+steerAngle);
    frontWheel.y += carSpeed * deltaTime * MathUtils.cos(carHeading+steerAngle);

    carLocation.x = (frontWheel.x + backWheel.x) / 2;
    carLocation.y = (frontWheel.y + backWheel.y) / 2;

    carHeading = MathUtils.atan2( frontWheel.y - backWheel.y , frontWheel.x - backWheel.x );

    I swapped the Sin and Cos because in LibGDX angles are backwards I think, so 90 degrees faces left instead of up.

    1. Hi Abdullah,

      I'm not sure how you are calculating steerAngle, but this is meant to be the angle of the front wheels relative to straight ahead. Try setting this to 0 and check the car goes straight. If not I would simplify the whole thing down to just one line like:

      carLocation.x += carSpeed * deltaTime;

      Then at least the car should move horizontally at a constant speed (although it might not face the right way). If even that doesn't work then you've got an error somewhere else in your code.

      Let me know how it goes.

    2. So I found out what the problem is. Apparently in LibGDX Sprites and Box2d Bodies have their positions at the bottom left corner of the object. So the carLocation would be (0,0) instead of say (3,3) for an object that has all sides the length of 6.

      I guess what I need to do is figure out a way to either move the origin of the position to the middle of the sprite or reset the calculations to take that into account.

      I think I'll try to fix the positioning so that the return position is the center not the bottom left.

  20. Thanks for this article, i was loocking for a simple steering physics and it helps me a lot.
    As the rear wheel should follow the front wheel, may i propose some modifications to make it slightly more realistic :

    // Old front wheel position

    // New front wheel position

    // Old back wheel position

    // new car heading

    // new car location
    carLocation = frontWheel - wheelBase/2 * new Vector2( cos(carHeading) , sin(carHeading) );

    1. Thanks for your alternative algorithm idea - it got me thinking! It's easy to test the accuracy of your idea by using a specific input to the model with a very small timestep and then seeing how inaccurate it gets with larger timesteps.

      So you can hopefully replicate the results I used the following settings: Car starting with centre point at (0,0) and heading 0 degrees, wheel base of 2 units, a speed of 1 unit per second and a steer angle of 5 degrees. I ran this setup for 1 second simulation time and then output the final car heading.

      First off I used a dt of 0.001 and got 2.497 degrees with my algorithm described above and 2.496 degrees with your algorithm - so it seems pretty close (I assume using a smaller dt will get even closer results). Then I adjusted the timestep to 0.01 and got 2.497 again with my algorithm, but yours gave 2.484. With a dt of 0.1 mine still gives 2.497 and yours is down to 2.378.

      I think the source of the error in your algorithm is that you are trying to calculate the new angle of the car based on the old rear wheel position and the new front wheel position, so the result will obviously depend heavily on the value of the time-step (which it shouldn't). Mine uses the new updated position of both wheels so does not depend as much on the time-step value.

      The thing that initially rang alarm bells with your idea was that there is no physical reason why the front wheel should be treated any differently to the rear wheel - both should be moved forwards the direction they are pointing each time step, and then it doesn't matter if the car is going forwards or backwards, or if the front or rear wheels (or both) are being steered.

  21. Thank you so much for this! :o)

    Could I just ask what the draw code would be, as I am getting it all wrong, with regards to position and rotation of the sprite itself.

    1. Hi AWP, I'm using XNA in 3D for this, so my draw code simply builds a world matrix from the position and rotation of the car:

      Matrix mWorld = Matrix.CreateRotationY( heading ) * Matrix.CreateTranslation( location.X , -axleHeight + axles[0].wheelRadius, location.Z );

      This is then set in the normal way before drawing the mesh.

      In 2D you need to set the rotation of the sprite to "heading" and then translate it according to "location".

  22. Thanks for the reply! Will try what you suggested.

  23. Hello man!
    First of all, good tutorial! You have solved many questions for me with this.
    Now, my problem: i'm trying to adapt these code to Python, using Pygame.
    But, this isn't working good: the parts of my car are going in strange directions when I try to change the angle. But works perfectly using direction = PI, angle = 0.
    Can you help me with this?

    The code:
    distance = v*dt

    frontX = centerX + (math.cos(angle)*base/2) + (math.cos(angle+direction)*distance)
    frontY = centerY + (math.sin(angle)*base/2) + (math.sin(angle+direction)*distance)

    backX = centerX - (math.cos(angle)*base/2) + (math.cos(angle)*distance)
    backY = centerY - (math.sin(angle)*base/2) + (math.sin(angle)*distance)

    meioX = (frontX + backX)/2
    meioY = (frontY + backY)/2

    direction = math.atan((frontY- backY)/(frontX - backX))

    1. I forgot to wish you happy holidays new year! Happy new year!

    2. Carlos, your last line of code "direction=..." should be "angle=...". Using more descriptive variable names helps to avoid mistakes like this.
      Merry Christmas!

  24. Hi, nice code up in there, but I think there should be some correction in rear wheel movement, don't you think? I agree that each wheel can only move in its pointing direction and that makes the front wheels to move correclty. But when you are making a turn, the very next car position has always a new carHeading. That makes the rear wheels to have a new direction as well.

    With that in mind, your algorithm can only work with small dt in time increment. Or rather, your accuracy will be better the smaller your dt is.

    Think about a very tight turn with a large time step. The front wheels will move only a few to the front direction and a lot to the side direction.With this large time step, the next position of the rear wheels will not only be in the old carHeading direction but also will drift a little bit to the side of the turn.

    I'm not very good in expressing myselft, but can you understand what I am saying? If so, can you think of a good solution for this?

    1. Take a look at this image I've just made:

      it represents what I'm trying to say. This still may not be the perfect solution but may be a good approximation making both green angles to be equal.

    2. Hi Felipe, yes I understand your concern about the errors with large values of dt. I wrote the code with a frame rate of 30 or 60 fps in mind, and with this value of dt and "normal" car turning speeds the error is very small (less than 1%). The idea was to show that with only a few lines of code you can get a much better handling car than many web-games out there.

      If you did want to do it 100% accurately (eg you have a much bigger value of dt) it would be quite straightforward (but more than 6 lines of code!). First you need to find the instantaneous centre of rotation of the car, do this by drawing two lines out perpendicular from each wheel and find where they cross. Each wheel will then follow an arc around this centre point. Then for your value of dt you can calculate how far along the arc the wheel will have moved, and use this as the new positions. You value of dt can be as large as you want then and it will still be accurate, so long as the steering angle is constant throughout the time period.

    3. Yes, that is it! And I agree that for small dt your code is very good indeed. This arc method is also very interesting and quite simple. I did not think about that. Thanks anyway and good luck with your blog!

  25. This comment has been removed by a blog administrator.

  26. Steering goes bananas when the car reverses, has anyone else found this, or have I got something wrong?

  27. I fixed the steering problem I had, but I can't remember exactly what I did, it wasn't anything to do with your code.


    click Here

    شركة نقل اثاث بخميس مشيط
    شركة تنظيف فلل بخميس مشيط
    شركة مكافحة حشرات بخميس مشيط
    شركة كشف تسربات المياه بخميس مشيط
    شركة تنظيف بخميس مشيط
    شركة تنظيف بالرياض
    شركة نظافة بالرياض
    شركة تنظيف منازل بالرياض
    شركة تنظيف فلل بالرياض
    شركة كشف تسربات المياه بالرياض
    شركة نقل اثاث بالرياض
    شركة تخزين اثاث بالرياض
    شركة مكافحة حشرات بالرياض
    شركة مكافحة النمل الابيض بالرياض

  30. Thanks for sharing this excellent data. Thank you!

  31. Can you please help with a force diagram for a basic car steering? Anyone ? Please?

  32. This is exactly what I was looking for. Thanks a lot!

  33. Great tutorial - I used Carlos Jr's implementation as the engine I'm using doesn't have vectors. :)

    Has anyonѥ tried implementing a full Ackermann in 2d?

  34. Using Unity 2D, C# and physics, how does one take the rotation of the main body of a top down car and add the maximum steering angle to tell the wheels which way to point. It seems that .rotation doesn't work past 360 degrees and .Rotate just spins the wheel.
    Thank you

  35. Hi. really appreciated post. please continue to share more posts. very informative. you can play more online and amazing games at slope