In Unity, there are multiple approaches to creating vehicle movements. While one simple method involves moving a vehicle object in 3D space and adding wheel rotation animations, this approach lacks realism as it ignores physics.
To accurately simulate vehicle movement, Unity provides a specialized component called WheelCollider
. This collider is designed to mimic the behaviour of grounded vehicle wheels. The attached vehicle can be realistically moved by applying forces to the wheel colliders, similar to real-world vehicles.
The WheelCollider
component offers various properties that can be adjusted, such as mass, radius, suspension settings, and friction settings, allowing users to fine-tune the physical properties and achieve the desired effect.
1. Getting Started
We will import a vehicle object (3D model of a vehicle) to make it more realistic and attractive from the Unity asset store. Don't worry; many free assets are available in the Unity asset store. This one is very small in size and free too.
However, if you prefer, you can also use your own 3D model of a vehicle or follow along using the basic primitive 3D objects provided by Unity (such as cubes and cylinders).
2. Starting the Project
- Open the Unity Hub and click on the
New Project
button to create a new project. - Select
3D core
from the list of available templates, give the project a proper name and location. - You will have a sample scene created and opened for you with a
Main Camera
andDirectional Light
in the scene. In your project view, you can find the scene insideAssets
>Scenes
>SampleScene
. You can rename the scene if you want.
3. Importing the Assets
- Click on the
Add to My Assets
button on the Unity Assets store. You may need to sign in with your Unity account if you have not signed in already. - Accept the
ToS
andEULA
if prompted. This will add the asset to your account. - Then click on
Open in Unity
and open the link. It will open thePackage Manager
with the option to download the asset into your project. Make sure thecar
asset is selected, and click on theDownload
button. - Once the download completes, it will show a button to import the asset into the project. Click on the
Import
button on thePackage Manager
. You should see a new folder inside yourAssets
folder containing the imported assets.
- If you want to use a
3D model
that you already have on your device, you can simply drag and drop it into yourProject Window
.
4. Setting Up the Scene
- Add a plane to the scene from the
+
button in theHierarchy
window and give it a scale of100
along thex-axis
andz-axis
, so that we have enough area to drive the car around.
- Find the
vehicle2-blue
object insideAssets
>LowPolyVehicle2
>Models
. Drag and drop it into the hierarchy. You should see the car model in theScene View
too.
- Did you notice how the
vehicle2-blue
object is blue while the others are not? It is because it is set as aprefab
object. We will make changes within this object, so let's unpack it completely. - Right-click the object and select
Prefab
>Unpack Completely
. - Let's rename the object to 'Car'.
5. Setting Up the Vehicle Physics
We need to add the Colliders
and Rigidbody
components to our vehicle to use it as a physical object with physical properties. For that, follow the listed steps.
- Select the
Car
object and click theAdd Component
button on the inspector.
Click on theSearch box
and typeRigidbody
. You will see two matching components,Rigidbody
andRigidbody 2D
.Rigidbody
is used for 3D objects, whileRigidbody 2D
is used for 2D objects. - Click on
Rigidbody
to add rigidbody components to the object,Car
. - Within the
Rigidbody
component properties, update the mass of the car from1
to1500
(kg) and drag to0.01
(optional). - Click on
Add Component
and typeBox Collider
. - Select
Box Collider
from the filtered list to add a box collider component to the object,Car
. - Within the
Box Collider
component, adjust theSize
andCenter
of the collider to match the base of the car.
Mesh Collider
or combine multiple colliders to match the shape of the vehicle. I will be using a single Box Collider
for demo purposes.6. Setting Up Vehicle Components
Let us take a look at the Car
object. If we expand it, we can see the body, doors, windows, wheels, etc., as the Child Objects
of the main Car
object.
Let's group the wheels together.
- Right-click the
Car
object and click onCreate Empty
. It will add an empty object inside theCar
object. (Make sure itsTransform Values
are set toDefault
, i.e.,0
's for position and rotation and1
's for scale). - Rename the newly created object as
Wheels
. - Select all four wheel object
WheelBL
,WheelBR
,WheelFL
,WheelFR
within theCar
object and drag them intoWheels
. This will make them child-objects ofWheels
.
- Similarly, create another empty object inside
car
and rename it asWheelColliders
. Again, create an empty object insideWheelColliders
and rename it asWCFL
.
- Select the
WCFL
object and click onAdd Component
button in theInspector
view. TypeWheelCollider
in the search box and select theWheelCollider
option from the filtered list. This action adds a wheel collider component. - You may need to select the
Car
object to see theWheelCollider
component on the scene view. Move theWCFL
object to align itsCenter
with one of the car wheels. Then adjust its radius in theInspector
view to match the size of the car wheel.
- We need to move
WCFL
toWheelFL
position. Copy the transform values ofWheelFL
ontoWCFL
and adjust theY-position
value manually.
- Duplicate
WCFL
three times and rename them asWCFR
,WCBL
andWCBR
respectively. Copy the positions ofWheelFR
,WheelBL
andWheelBR
ontoWCFR
,WCBL
andWCBR
respectively.
- Create an empty object within
Car
and rename it toCenterOfMass
. Then position it somewhereCenter
at the base of the car body. This will be used to set the center of mass of the car. It is recommended to place it a little forward from the center (closer to the engine) because that is the center of mass in real cars.
7. Setting Up Input
Our vehicle is now set up and ready to move. We need to read the input from the user, which controls the vehicle movements.
- Create a C# script named
InputController.cs
. - Add the following properties in the script to read input values from the user:
Here, each property uses a public get
and a private set
. This allows the values to be accessed from outside this script but modified/controlled within this script only.
- Now, let us read the input values and assign them to the properties declared above from the
Update
method.
Our script is ready to read input from the user. Now we need to attach it to a GameObject
.
- Create an empty
GameObject
in the hierarchy and rename it asInputController
. Then attach the script we just wrote to theGameObject
.
8. Controlling the Vehicle with Input
Now, we will use the input values to control the vehicle movements.
- Create a C# script named
VehicleController.cs
. - Declare some variables that are used to control the vehicle movements:
-Force
will be used to drive the vehicles usingWheelColliders
.
- Positive value and negative value of force will move the vehicle forward and in reverse, respectively.
-Angle
will be used to steer the vehicle left or right.
-BrakeForce
will be used to apply brakes.
Let's create methods to use these values in action.
Here, we assume the vehicle is a front-wheel drive, and we use indices 0
and 1
to reference front WheelColliders
. We use motorTorque
to apply rotating force on the front WheelColliders
, which results in the vehicle's movement. Similarly, steerAngle
and brakeTorque
are used to apply steering and braking force, respectively.
- The above code updates the
WheelColliders
and results into vehicle movements, but the wheels (tyres) of the vehicles are not updated. Note that theWheelColliders
are not visible in the gameplay. - Moving a vehicle without moving the visible wheels would be unrealistic. So, the next step is to move the wheels to match the vehicle's movement. To achieve this, we will copy the
WheelCollider
movements (positions, rotations) onto thewheel-transforms
.
using UnityEngine;
public class VehicleController : MonoBehaviour
{
...
private void Brake()
{
...
}
private void UpdateWheelMovements()
{
for (var i = 0; i < Wheels.Length; i++)
{
Vector3 pos;
Quaternion rot;
WheelColliders[i].GetWorldPose(out pos, out rot);
Wheels[i].transform.position = pos;
Wheels[i].transform.rotation = rot;
}
}
}
We have the methods to move the vehicle. Let's call them inside FixedUpdate
function.
using UnityEngine;
public class VehicleController : MonoBehaviour
{
...
private void UpdateWheelMovements()
{
...
}
private void FixedUpdate()
{
Steer();
Drive();
Brake();
UpdateWheelMovements();
}
}
- This should be enough to make the vehicle move, turn, etc. Let's make it more stable so it won't trip over while steering. We will use
centerOfMass
position for this purpose. The lower the center of mass, the more stable the vehicle.Rigidbody
of the vehicle is used to set thecenterOfMass
position. We will set that in theStart
method. We can access theRigidbody
component usingGetComponent<Rigidbody>()
.
using UnityEngine;
public class VehicleController : MonoBehaviour
{
...
private void FixedUpdate()
{
...
}
private void Start()
{
GetComponent<Rigidbody>().centerOfMass = CenterOfMass.localPosition;
}
}
That's it. Our VehicleController
script is ready. Let's move some methods up/down and see the final script.
using UnityEngine;
public class VehicleController : MonoBehaviour
{
public InputController InputCtrl;
[Tooltip("Set ref in order of FL, FR, RL, RR")]
public WheelCollider[] WheelColliders;
[Tooltip("Set ref of wheel meshes in order of FL, FR, RL, RR")]
public Transform[] Wheels;
public Transform CenterOfMass;
public int Force;
public int Angle;
public int BrakeForce;
private void Start()
{
GetComponent<Rigidbody>().centerOfMass = CenterOfMass.localPosition;
}
private void FixedUpdate()
{
Steer();
Drive();
Brake();
UpdateWheelMovements();
}
//Drive forward/backward
private void Drive()
{
WheelColliders[0].motorTorque = WheelColliders[1].motorTorque = InputCtrl.Vertical * Force;
}
//Steer left/right
private void Steer()
{
WheelColliders[0].steerAngle = WheelColliders[1].steerAngle = InputCtrl.Horizontal * Angle;
}
//Apply brakes
private void Brake()
{
WheelColliders[0].brakeTorque = WheelColliders[1].brakeTorque = InputCtrl.Brake * BrakeForce;
}
//imitate the wheelcollider movements onto the wheel-meshes
private void UpdateWheelMovements()
{
for (var i = 0; i < Wheels.Length; i++)
{
Vector3 pos;
Quaternion rot;
WheelColliders[i].GetWorldPose(out pos, out rot);
Wheels[i].transform.position = pos;
Wheels[i].transform.rotation = rot;
}
}
}
- It is time to attach this script to a
GameObject
and provide the necessary references. - Let's create an empty
GameObject
inside the vehicle to mark theCenterOfMass
position.
- Attach
VehicleController.cs
script onto theCar
object and provide references.
Now, your vehicle is ready to be controlled using arrows or WSAD
keys and Spacebar
. You can experiment by tweaking the values until you find the movements of your liking. Congratulations on making your first vehicle simulation using wheel colliders in Unity.
Thank you for reading. I hope it was helpful and easy to follow. More on vehicle controller coming soon.