Hill Climb Racing Like 2D Car Physics 2 - Add Speed Through Script

It has been some time that I did the first part of this series Hill Climb Racing Like 2D Car Physics 1 - WheelJoint2D, and people were asking me why is it taking so much time to post the second part. Well, it is not easy being an Indie Game Developer you know and it becomes that difficult when you are alone working on writing articles on the blog as well. Never mind, here I am back with the second part as promised.

In this part of the series on How to build a 2D based physics car just like the one which you see in Hill Climb Racing, we will add the capability to apply speed to the wheels via script. Basically it is a post on 2D Car Movement via Script. This post might end with just a single script that we would be adding to the car, but it involves whole sort of Physics understanding. Sit back tight and ride the car!

The Result: Hill Climb Racing Like 2D Car Physics 2 - Add Speed Through Script




If my memory serves me good, we, as of now have a car capable of moving using the motors of the wheelJoints.

Add Friction to the Wheels

The first thing that we would add to the wheels is the friction so that the movement is more crisp and more gripping.

Create a New Folder under the assets in the Project panel and name it as Materials. Right click on the Materials folder and select Physics2DMaterial. Name this as Tires. Set the Friction to 3 and Bounciness to 0.05. Though these are not what it would be in real but, I found these values work quite well to give it a real car ride feel.

Tire Physics2DMaterial
Once you have created the material, apply this material to the CircleCollider2D's Material property of both the wheels.

Add Physics2DMaterial to the CircleCollider2D
Add Drag to the 2D Car

To give the car some realistic effect while moving, it is necessary to add some Linear Drag. You can add the Linear Drag to the Car's RigidBody2D by simply setting the Linear Drag property to 0.1 and set the Angular Drag property to 0.05. You have to do this to the CarBody, RearWheel and FrontWheel.

Add Linear Drag and Angular Drag
WheelJoint2D Motor Configuration

Uncheck the Use Motor property of the WheelJoint2D component connected to the FrontWheel which is just what a real car would work like.

Add Script

Now we will add a script which would add movement to the 2D Car. Create a New Folder named Scripts and add a New C# Script inside this folder. Name the script as WheelJointCarMovement. Attach the script to the CarBody object. Open the script and add the below code to it

using UnityEngine; 
using System.Collections; 


public class WheelJointCarMovement : MonoBehaviour { 
 //reference to the wheel joints
 WheelJoint2D[] wheelJoints; 
 //center of mass of the car
 public Transform centerOfMass;
 //reference tot he motor joint
 JointMotor2D motorBack;  
 //horizontal movement keyboard input
 float dir = 0f; 
 //input for rotation of the car
 float torqueDir = 0f;
 //max fwd speed which the car can move at
 float maxFwdSpeed = -5000;
 //max bwd speed
 float maxBwdSpeed = 2000f;
 //the rate at which the car accelerates
 float accelerationRate = 500;
 //the rate at which car decelerates
 float decelerationRate = -100;
 //how soon the car stops on braking
 float brakeSpeed = 2500f;
 //acceleration due to gravity
 float gravity = 9.81f;
 //angle in which the car is at wrt the ground
 float slope = 0;
 //reference to the wheels
 public Transform rearWheel;
 public Transform frontWheel;

 // Use this for initialization 
 void Start () { 
  //set the center of mass of the car
  rigidbody2D.centerOfMass = centerOfMass.transform.localPosition;
  //get the wheeljoint components
  wheelJoints = gameObject.GetComponents<WheelJoint2D>(); 
  //get the reference to the motor of rear wheels joint
  motorBack = wheelJoints[0].motor; 
 }  
 
 //all physics based assignment done here
 void FixedUpdate(){
  //add ability to rotate the car around its axis
  torqueDir = Input.GetAxis("Horizontal"); 
  if(torqueDir!=0){ 
   rigidbody2D.AddTorque(3*Mathf.PI*torqueDir, ForceMode2D.Force);
  } 
  else{
   rigidbody2D.AddTorque(0);
  }

  //determine the cars angle wrt the horizontal ground
  slope = transform.localEulerAngles.z;

  //convert the slope values greater than 180 to a negative value so as to add motor speed 
  //based on the slope angle
  if(slope>=180)
   slope = slope - 360;
  //horizontal movement input. same as torqueDir. Could have avoided it, but decided to 
  //use it since some of you might want to use the Vertical axis for the torqueDir
  dir = Input.GetAxis("Horizontal"); 

  //explained in the post in detail
  //check if there is any input from the user
  if(dir!=0)
   //add speed accordingly
   motorBack.motorSpeed = Mathf.Clamp(motorBack.motorSpeed -(dir*accelerationRate - gravity*Mathf.Sin((slope * Mathf.PI)/180)*80 )*Time.deltaTime, maxFwdSpeed, maxBwdSpeed);
  //if no input and car is moving forward or no input and car is stagnant and is on an inclined plane with negative slope
  if((dir==0 && motorBack.motorSpeed < 0 ) ||(dir==0 && motorBack.motorSpeed==0 && slope < 0)){
   //decelerate the car while adding the speed if the car is on an inclined plane
   motorBack.motorSpeed = Mathf.Clamp(motorBack.motorSpeed - (decelerationRate - gravity*Mathf.Sin((slope * Mathf.PI)/180)*80)*Time.deltaTime, maxFwdSpeed, 0);
  }
  //if no input and car is moving backward or no input and car is stagnant and is on an inclined plane with positive slope
  else if((dir==0 && motorBack.motorSpeed > 0 )||(dir==0 && motorBack.motorSpeed==0 && slope > 0)){
   //decelerate the car while adding the speed if the car is on an inclined plane
   motorBack.motorSpeed = Mathf.Clamp(motorBack.motorSpeed -(-decelerationRate - gravity*Mathf.Sin((slope * Mathf.PI)/180)*80)*Time.deltaTime, 0, maxBwdSpeed);
  }
  
  
  
  //apply brakes to the car
  if (Input.GetKey(KeyCode.Space) && motorBack.motorSpeed > 0){
   motorBack.motorSpeed = Mathf.Clamp(motorBack.motorSpeed - brakeSpeed*Time.deltaTime, 0, maxBwdSpeed); 
  }
  else if(Input.GetKey(KeyCode.Space) && motorBack.motorSpeed < 0){ 
   motorBack.motorSpeed = Mathf.Clamp(motorBack.motorSpeed + brakeSpeed*Time.deltaTime, maxFwdSpeed, 0);
  }
  //connect the motor to the joint
  wheelJoints[0].motor = motorBack; 

 }

}


This script has been thoroughly commented for your understanding. However, there is one thing which I think I should touch on - The applying of the speed to the motor. There are various conditions that I have considered here while applying the speed to get the desired result, which are listed below:

Line 67 to 75 of the Script

- Check whether there is any input from the user.
- If yes, add the speed to the motor in the range (maxFwdSpeed, maxBwdSpeed). It is opposite to what you expect since the negative value of motor speed moves the car forward.

NOTE: The usage of WheelJoint2D is not the way in which we are using. Actually, the wheelJoints are to be attached to the Wheels themselves and not the car body. However, I find that using this way makes the car more stable. If you want to use the WheelJoint2D's the right way, maybe, you might want to set the Frequency to a very high value of, say, 50+ to make the Car stable. Give it a try. Also, note you might want to change this script accordingly.

- The formula that we are using here apply speed to the car is similar to

vf=vi+at

i.e., Final Velocity = Initial Velocity + (Acceleration*time)

Now,

a=gsinθ

Hence, we would have an equation which would look like

vf=vi+gsinθt

Where, g is obviously the acceleration due to gravity.
The reason why we have considered the angles and all is, because, we do not want the car to be stagnant on, say, an inclined road where in no input is given by the user and the motor speed would eventually become zero.
You might perhaps say that the Unity's built in Physics should make this work by default. Well. No. It does not because, the WheelJoint2D's motor speed would block the movement of the car. As the speed would be zero, the wheels wouldn't move and hence the car.
I have used this equation and modified it as per requirements and considering the fact that negative speed moves our car forward.
- We have multiplied by speed by certain constant to get a desired acceleration rate so that the car wouldn't look impractical.

Save the Script and return to Unity. Attach this script to the CarBody component. You would notice that the script expects three components. Drag and drop them as in the image below

2D Car Movement Script - Unity
Now, you should be able to test this Physics Based 2D Car capable of moving based on the Users input and doing all sort of things, just like the Car in Hill Climb Racing does.

Check out the third part from the below link,
Hill Climb Racing Like 2D Car Physics 3 - Add Engine Sound

See you around.
Share on Google+

About Sujit Horakeri

Sujit Horakeri is a game freak just like any other next door guy you would come across. He is a Web Developer by Profession, Game Developer by Choice.
Connect with him on:
    Blogger
    Facebook

12 comments:

  1. Thanks for this awesome post on 2D Car Physics. I just loved it

    ReplyDelete
  2. Hello, for movement in android ?

    ReplyDelete
  3. Hello , Thanks for this Tutorials !!
    I do this post to end , but i can not Attach this script to the CarBody component .
    the Unity 5 error is :`UnityEngine.Component.rigidbody2D' is obsolete: `Property rigidbody2D has been deprecated. Use GetComponent() instead. (UnityUpgradable)'

    Please Help Me

    ReplyDelete
    Replies
    1. I've just started programming too, I realised that obsolete just means that the unity API has been updated. In this case they removed the short for getcomponent. What I did was basically create a public variable for Rigidbody2D rb then replace all the rigidbody2D with rb.

      Delete
  4. my wheel are become disjoint when car jump and come again to path ..and wheels cross the polygon colider of path and wheel sink to path

    ReplyDelete
  5. Your articles is excellent but unfortunately your website​ design is not responsive.

    ReplyDelete