Top Down 2D Car Physics

What I want to do is to make a top down game implementing the Unity3d 2d system. When I say top down I mean from the roof of the car. I want to be able to steer the car realistically like what the wheel collides do in car demo.

Before anyone says it: I have spent a great deal of time searching already.

I know it’s far too late, but I used the hints above to create the following script, please excuse the verbose syntax. I have the public variables set to 5 (acceleration) and 3 (steering). I’m posting because someone may still find this useful:

using UnityEngine;
using System.Collections;

public class 2dCarController : MonoBehaviour {

	public float acceleration;
	public float steering;
	private Rigidbody2D rb;

	void Start () {
		rb = GetComponent<Rigidbody2D>();
	}

	void FixedUpdate () {
		float h = -Input.GetAxis("Horizontal");
		float v = Input.GetAxis("Vertical");

		Vector2 speed = transform.up * (v * acceleration);
		rb.AddForce(speed);

		float direction = Vector2.Dot(rb.velocity, rb.GetRelativeVector(Vector2.up));
		if(direction >= 0.0f) {
			rb.rotation += h * steering * (rb.velocity.magnitude / 5.0f);
			//rb.AddTorque((h * steering) * (rb.velocity.magnitude / 10.0f));
		} else {
			rb.rotation -= h * steering * (rb.velocity.magnitude / 5.0f);
			//rb.AddTorque((-h * steering) * (rb.velocity.magnitude / 10.0f));
		}

		Vector2 forward = new Vector2(0.0f, 0.5f);
		float steeringRightAngle;
		if(rb.angularVelocity > 0) {
			steeringRightAngle = -90;
		} else {
			steeringRightAngle = 90;
		}

		Vector2 rightAngleFromForward = Quaternion.AngleAxis(steeringRightAngle, Vector3.forward) * forward;
		Debug.DrawLine((Vector3)rb.position, (Vector3)rb.GetRelativePoint(rightAngleFromForward), Color.green);

		float driftForce = Vector2.Dot(rb.velocity, rb.GetRelativeVector(rightAngleFromForward.normalized));

		Vector2 relativeForce = (rightAngleFromForward.normalized * -1.0f) * (driftForce * 10.0f);


		Debug.DrawLine((Vector3)rb.position, (Vector3)rb.GetRelativePoint(relativeForce), Color.red);

		rb.AddForce(rb.GetRelativeVector(relativeForce));
	}
}

Obviously this is just a model of 2D topdown car physics (no tyre model). It calculates the drift and counteracts this with an opposing force, shown with the debug.draw red line. The steering is reduced to zero when stationary to prevent the car spinning on the spot. Rigidbody drag and angularDrag are both set to 1.

The most interesting calculation is the dirftForce, which I got the concept from: http://docs.unity3d.com/Manual/AmountVectorMagnitudeInAnotherDirection.html

Note that I modify RigidBody2D rotation directly, but you can try AddTorque as shown (commented out).

I’m not terribly good with maths, but I hacked away at this until I got it roughly working as intended.

A bit of an old question but, thought I would pitch in, in case anyone else comes across the question, looking for an answer. Jeff and Major have pretty much worked it out but, to provide a little more fleshed out answer in C#
I found that playing around with the rigidbody 2d drags helped with getting more realistic feeling car movements.
You could modify or remove the noGas() method, and move it into the FixedUpdate() if you wanted. I found that this helped with braking and a more realistic stop, when not accelerating.

1.) Add a Rigidbody 2D component to your Car object (turn the Gravity Scale to 0) and then implement a script along the lines of the following:

    public float power = 3;
    public float maxspeed = 5;
    public float turnpower = 2;
    public float friction = 3;
    public Vector2 curspeed ;
    Rigidbody2D rigidbody2D;

    // Use this for initialization
    void Start () {
        rigidbody2D = GetComponent<Rigidbody2D>();
    }
	

    void FixedUpdate()
    {
        curspeed = new Vector2(rigidbody2D.velocity.x,    rigidbody2D.velocity.y);

        if (curspeed.magnitude > maxspeed)
        {
            curspeed = curspeed.normalized;
            curspeed *= maxspeed;
        }

        if (Input.GetKey(KeyCode.W))
        {
            rigidbody2D.AddForce(transform.up * power);
             rigidbody2D.drag = friction;
        }
        if (Input.GetKey(KeyCode.S))
        {
            rigidbody2D.AddForce(-(transform.up) * (power/2));
            rigidbody2D.drag = friction;
        }
         if (Input.GetKey(KeyCode.A))
        {
            transform.Rotate(Vector3.forward * turnpower);
        }
         if (Input.GetKey(KeyCode.D))
        {
            transform.Rotate(Vector3.forward * -turnpower);
        }

        noGas();

    }

    void noGas()
    {
        bool gas;
        if(Input.GetKey(KeyCode.W) || Input.GetKey(KeyCode.S))
        {
            gas = true;
        }
        else
        {
            gas = false;
        }

        if (!gas)
        {
            rigidbody2D.drag = friction * 2;
        }
    }

So, you can work this out yourself if you know what a force is and what acceleration is.

(1) Start with a basic model: Track velocity in X and Y and add it times delta time into your position on update
Try that and see how it behaves like an object in space.

(2) When you add velocity, add it in the “steering direction”. See how it behaves like a car with no traction

(3) Add an opposing force at right angles to the steering direction and proportional to the velocity on that angle, this adds traction.

ok i am 5 years late but just in case somone stumbles on this. i have made a simple script which works but has no friction so you cant drift with this one, but its shorter than others.

using System.Collections;
using System.Collections.Generic;
using UnityEngine;

public class drive : MonoBehaviour
{
    Rigidbody2D rb;
    float rotation;
    float x;
    float y;
    public float speed=1;
    public float turnspeed = 1;
    int temp=0;
    // Start is called before the first frame update
    void Start()
    {
        rb = GetComponent<Rigidbody2D>();
    }

    // Update is called once per frame
    void Update()
    {
        rotation = rb.rotation;
        rb.rotation = rotation - Input.GetAxis("Horizontal")*turnspeed*Time.deltaTime*10;
        rotation=Mathf.Deg2Rad* rotation;
        x = Mathf.Sin(rotation) * speed * Time.deltaTime*10;
        y = Mathf.Cos(rotation) * speed * Time.deltaTime*10;

        rb.velocity=new Vector2(-x,y);
    }
}