Basic to Advanced Car or Vehicle Tutorial

I see this all the time. Some one posts a piece of code asking how to make Trasform.Translate or whatever work for a car… make it believable. I say BUPKIS. Controlling a vehicle by code is about the most difficult thing to do, and make it look right. Does that mean you shouldn’t, no. It means there is an easier way…

I am going to attempt to go from scratch to a working vehicle in a tutorial in this multi-part tutorial. The best thing is, I am going to do 90% of it with stuff found here in THIS forum. It’s all here, I have done it before. Let’s do it again.

The Basics:

First things first, we are not going to use anything that Unity doesn’t have to start with. We will get to making a car and importing it and doing whatever later.

We start with a nice NEW scene. Into this scene we are going to add the following packages:

Character Controller.unityPackage
Projectors.unityPackage
Scripts.unityPackage
Skyboxes.unityPackage

NOTE: Not a single one of these it totally nessecary, they will be used later on in the more advanced areas. The only thing I usually import is the Scripts, because it contains the SmoothFollow script.

OK with a new scene, lets create a box, name it Ground. set its scale to 500,1,500 and it’s position to 0,0,0. Being the ground, this is of course what we are going to be driving on, so were making it nice and big.

Next, we are going to create a box, and name it Car. Set its scale to 3,1,5 and its position to 0,5,0. This is the building block of the car.

OK, we have 3 pieces in our scene, Car, Ground and Main Camera.

From the Standard Assets folder, we are going to the Scripts folder and the Camera Scripts folder in that. We find a SmoothFollow script in there. Drag that onto the Main Camera in the Hierarchy view.

In the Hierarchy view, click on the Main Camera, and in the Inspector, scroll down to the Smooth Follow (Script). The Target is None (Transform). We need to drag the Car from the Hierarchy view onto the None (Transform) label there.

Now, we are going to add a Rigidbody to the mix. So with the Car selected, goto Component/Physics/Rigidbody.

Scroll down to the Rigidbody in the inspector and change the Mass to 1000;

The next thing we are going to add, is a Directional Light. (GameObject/CreateOther/Directional Light) Rotate it some in the screen so that not everything looks so bland.

Now we can play it, and you will see the camera moving some, and a block, it falls, hits the ground, and it’s still pretty bland.

OK, lets create some wheels. (GameObject/CreateOther/Sphere)

Create one, and we want the Scale to be 1,0.2,1. We will need to rotate it, and move it to a location where a wheel should be. The rotation should be 0,0,90 and the position should be about 1.5, 4.5, 1.8. This is all in how you look at it though.

OK, next, we are going to set this wheel up. First, we need to parent the wheel to the car, and label it… In my case I am going to label the wheel FrontRight. Next, delete the Sphere Collider from the wheel. (Click on the Gear beside the Sphere Collider in the Inpsector and click Remove Component.) Now Add a WheelCollider to the Wheel. (Component/Physics/Wheel Collider)

OK, now we have a basic wheel collider attached to one wheel. NOTICE how the radius of the wheel collider is 0.5. This is because the Scale of the original scale of the wheel is 1. This will be very important in later tutorials.

OK, now if we run it, it is going to drop, hit the ground, but will hobble on the one wheel we set up.

If you are along with me, then we can go to the next step.

Click on the wheel we just set up, duplicate it (Ctrl-D) and move it to the back. (the measurements have changed, now we are in the local measurements, so it is 0.5, -0.5, -0.366) Rename this one, RearRight

Select Both wheels, and duplicate them. Then move them over to the other side. (move them over, then change thier X values to -0.5) Rename them repsectively FrontLeft and RearLeft.

OK, now with everything in place, we run it, it falls to the ground, but has no springs, so it sits there.

So lets build up some suspension on it. This is what is going to make it react like a car.

For each wheel, we need to set 6 values. The Spring Distance (All wheels should be 0.25), the Suspension Spring.Spring (1500 for the front wheels, 1000 for the rear), the Suspension Spring.Damper (All wheels are 2), the Suspension Spring.Target Position (All wheels should be 0.25), the Forward Friction.Stiffness Factor (All wheels should be 0.02) and the Sideways Friction.Stiffness Factor(All wheels should be 0.02)

This means, that a 1000 KG car takes 5000 (or five times the mass) units of force to keep it springy.

Now for a simple piece of code to make it all work:

//CarController1.js
var wheels : Transform[];

var enginePower=150.0;

var power=0.0;
var brake=0.0;
var steer=0.0;

var maxSteer=25.0;

function Start(){
	rigidbody.centerOfMass=Vector3(0,-0.5,0.3);
}

function Update () {
	power=Input.GetAxis("Vertical") * enginePower * Time.deltaTime * 250.0;
	steer=Input.GetAxis("Horizontal") * maxSteer;
	brake=Input.GetKey("space") ? rigidbody.mass * 0.1: 0.0;
	
	GetCollider(0).steerAngle=steer;
	GetCollider(1).steerAngle=steer;
	
	if(brake > 0.0){
		GetCollider(0).brakeTorque=brake;
		GetCollider(1).brakeTorque=brake;
		GetCollider(2).brakeTorque=brake;
		GetCollider(3).brakeTorque=brake;
		GetCollider(2).motorTorque=0.0;
		GetCollider(3).motorTorque=0.0;
	} else {
		GetCollider(0).brakeTorque=0;
		GetCollider(1).brakeTorque=0;
		GetCollider(2).brakeTorque=0;
		GetCollider(3).brakeTorque=0;
		GetCollider(2).motorTorque=power;
		GetCollider(3).motorTorque=power;
	}
}

function GetCollider(n : int) : WheelCollider{
	return wheels[n].gameObject.GetComponent(WheelCollider);
}

Save the code, drag it to the Car and let it rip.

What you have, the car falls to the ground, allows you to drive it. (if you click on the Car, and click the Gizmos in the top of the Game sceen screen, you can see what it basically sees in physics.)

Two things I want to add here, for your use… One… add some textures so that things aren’t so bland… And you could use the MouseOrbit script, instead of the SmoothFollow. This allows you to see things happening a little better.

I hope this at least gets some of you started. I will be adding onto this later. There is alot more to cars and vehicles than a simple box.

You can see an example of this here.

1 Like

Nice tutorial. :slight_smile: Any chances of putting up a webplayer?

Example posted

Very nice!

The next part is taking a little longer… and people keep bugging me a little more to actually work at work. So for today, we will discuss a couple of concepts which will be implemented into the newer scripts and why.

GetComponentsInChildren:
This is a very interesting method that allows you to get a type of component from all of the children attached to a game object. This will be very useful in setups where we will be able to define an object with children and get all of them in one shot.

As from my research it returns Component[ ] where upon the documentation gives something different. Here is some test code:

function Start(){
	var x : Component[]=gameObject.GetComponentsInChildren(MeshFilter);
	print(x);
	for(y in x)print(y);
}

Note that GetComponentsInChildren also returns the object with it’s children.

WheelCollider:
OK, this is a simple component. My discussion here is more to do with the MeshFilter component. The WheelCollider can be added to a mesh with a MeshFilter, it just so happens to take on the size from the MeshFilter’s bounds. This is a good thing. It makes creating the collider about 5 steps shorter.

The purpose for this is that you could copy a wheel, put a MeshCollider on it and it would then have the size needed for what you needed. Oh to do things the easy way. This will of course NOT be how we are going to do things.

Instantiate:
I bring this up after doing some research. Instantiate is very powerful. Perhaps more powerful than I need for what I am doing. Instantiate will not only copy an object, but all of it’s children with it. Then it will restart any scripts attached to it. So that kind of sucks if you for some of the purposes that I need it for here. Great for other things.

This test was done to see if you could copy something and re use it. But this also shall not be used. :wink:

Til Tommorrow.

The Model:

About 10 years ago I built a little test car that I could drop into a game to test how to, well get cars into that game. It’s seen many little rinky dink games in the past and will see our tutorial next…

561701–19838–$TestCar.zip (354 KB)

OK, as you can see I have sized it in 3ds max to roughly 50 units That is 5 units in Unity… this equates to 5 Meters, or roughly 5 yards or 15 feet. (its all in about sizes, lol)

The first thing we should notice about this file is that it has some weir thing going on with it’s wheels. flCol, frCol and the rest. This will make sense later on. The datchas is the main collider that we are going to use for our car.

Bring the model into Unity, set it on the box and lets get started with the real part of what this tutorial is about.

Rebuilding from the ground up:

Yes, rebuilding… As of the last tutorial, we had a script that would make it run… today were going to build a few scripts that make it work! So the bulk of this tutorial is redoing tutorial 1 with a few extra perks. And this tutorial is almost all about scripts.

We first need to create 2 specific objects. They are named: GC and EC. In the inspector for each, make sure that their position is all zeros. this is a MUST! These objects will also remain when we open new levels. So we are going to reserve some names for our game. GameController and EnvironmentController. (in other words, don’t use those names for any other objects)

All Code is will be kept up to date HERE

These are just basic scripts right now. We will be updating them as we add new features.

Now we are going to relook at our controller. We had our wheels and vehicle all in the same controller. And where this would work in the grand scheme if every vehicle had 4 wheels and the first two steered the other two did not would be perfect. We are going for versatility here though, so we are going to make two scripts: One to control the vehicle, the second to control the wheels. These are integrated so you can’t just shove a wheel script on something and have it work. You can, however drop a vehicle controller on something, but with no wheels it wont work.

So to begin, lets look at the model that I provided here. It has a body, a couple of pieces to the body, a collision mesh, then four wheel colliders with a wheel mesh attached to each. It is all sized and all of the transforms are set to the center of the world in max save the wheels which are centered to themselves. Where it is important to do the centering of the wheels to themselves it is by no means completely necessary. A little extra code to set them up and bind them is possible.

So basically we are dealing with the following two scripts:
All Code is will be kept up to date HERE

Of these, you will notice that only part of the code still remains from the previous post. We are now doing alot of setup work on the vehicle. This is so that we don’t have to do it in editor. Also, alot of numbers here are derived from the mass and size of the wheels, so we don’t have to worry about alot of information that we could miss while setting up.

To use these scripts you need to import a vehicle. That vehicle needs to have a few things specified:

At least one collision mesh. It works on a parented structure, so we only define one collision mesh, the rest are children of it and are picked up automatically.

Each wheel needs to start with a collision mesh of it’s own and all the visible pieces of that wheel are children of it. (Look at the TestCar.3ds I provided for an example)

Two scripts: VehicleController.js and WheelController.js (Mind the spelling and capitol letters)

OK, The rest is simple, Drag the VehicleController script onto the TestCar base, set it’s mass and power. Check the isPlayer field. Leave the rest alone for now.

Drag the WheelController onto Each of the wheel collider meshs (the parents) You need to set 1.0 for steering wheels on the steering and 1.0 for driving wheels on the driving wheels. (kind of simple, yeah) The script controls the rest of the factors of the wheels.

For the moment, if you did everything right you now have a basic car up and running again. There is one thing I added to this that was not in the previous one; The wheel vis updates. This is the part that makes a wheel look like it’s turning and shifting and such.


WORKING DEMO

OK, now to the discussion of tweaking. This actually has to be added because you may not like the way the car handles. There are various places that you can tweak things. The easiest places are things like mass, maxSteering and enginePower. These are very self explanitory and easy to use. They don’t do much for handling though.

The centerOfMass is a big way to control how the vehicle reacts. In the script, I do not add that number to the existing center of mass, I replace it. So the center of mass is 1 forward of the center of the world in 3ds max. (which is below the frame of the vehicle) This is actually why this interpretation is very stable. The center of mass is about 2 feet below where it should be. Center of mass is not reset during game time using the script, you can adjust it in the rigidbody during runtime. Once you find a good center of mass, just jot the numbers down and feed them in the script while not running.

Engine power is my interpretation of what the car’s real BHP is. I know the 2001 Z-28 HP rating was 240 and it weighs about 2600 lbs, so All I had to do was to make the mass about right, and the engine power about right, then make it drive kind of what a Camaro of that year drives like. Tweak as you must to make whatever you are working on right. The best test here is to have two different cars with different weights and HP ratings and make them both work correctly off of the same script. The number you are looking for is in the Update function. it reads:

power *= enginePower * Time.deltaTime * 2500.0;

A few lines down is the brake multiplier. Adjust it if you want different braking characteristics.

While we are here, I wish to show you one of the features that will be in this script later on. You will notice that there is a if(isPlayer){ line. This simply means that if you are not a player, you can’t push a key to operate the vehicle. Later there will be an if(isAI){ line. :wink:

Wheels:
Wheels are where all the actual tweaking is done, drive and steer are only there to handle the multiplication done according to the wheel its self, but even here there is opportunity to make things differently. A steering value of -1.0 will cause the wheel to steer backwards. As well a drive value of -1.0 would cause the wheel to rotate backwards. This can be used to create rear steering or other effects. (this would not be possible in the previous tutorial)

In the Setup of the wheels you will find everything in this section:

	var FB=obj.transform.localPosition.z - VC.centerOfMass.z;
	
	col=obj.AddComponent(WheelCollider);
	col.radius=radius;
	col.mass=bounds.size.y * bounds.size.x * 20;
	col.suspensionDistance=radius/2;
	col.suspensionSpring.spring=VC.mass * 1.5 + (VC.mass * FB * 0.05);
	col.suspensionSpring.damper=2;
	col.suspensionSpring.targetPosition=radius/2;
	
	col.forwardFriction.stiffness=0.02;
	col.sidewaysFriction.stiffness=0.01;// + (FB<0 ? 0.01 : 0.0);

Almost everything there adjustable. The mass of the wheel is based off of it’s size, bigger wheels are heavier. In testing, I worked heavily with the spring multiplier (1.5 here) and the sideways friction. You will notice that FB is based off of the center of mass in the VehicleController (VC).

Well, that does it for this issue. We accomplished alot and hopefully you guys will take some time to look at the code instead of just using it without realizing what it does.

Remember to check out the demo.

Very helpful. I’m looking forward to the if(isAI){ line.

Please make the terrain less flat so we can see how the car behaves while on a bumpy terrain. Also, it took me some time to figure out how to move the camera above the ground (click 'n drag inside the boundaries of the screen). I think a default smoothfollow camera is more intuitive.

Anyway, thanks a lot!

All I have slated for today is more testing and tuning. I can make a unity terrain, that would give the best interpretation of what you are looking for. The reasoning for the mouse orbit is to be able to see how the suspension works. I could actually put both in, and just have a key to swap. :wink:

I’ve had it on my “list of things I want to do in Unity” to try and create a simple homage to the original Halo 1 warthog, which had hilariously unrealistic physics and was not a realistic car simulation at all. But it was FUN.

Having no experience with Unity’s wheel colliders and whatnot, and with every other car/vehicle example out there being overzealously realistic simulations with crazy gear ratios and a million different tweaking variables, this tutorial is exactly what the doctor ordered.

Thanks BMB!

Today, were doing more tuning. Adding some things into the script that will help with it. We are also going to show you guys the beginning of the GameController I use for this. This will allow us to do some car swapping, camera swapping and game pausing. By virtue, you would not want to do these things on any individual car.

OK, lets start off with the GameController. This is actually a serious piece of little code. It is designed to NOT be duplicated. To start: Create a new GameObject, name it GC and make sure it is at world zero. (Yes, bolded and in VERY important that you do NOT name it GameController) Now apply this code to it:

All Code is will be kept up to date HERE
//GameController.js

You will need to fulfill the requirements for it. Drag the main camera to it. Adjust the cars number to the number of vehicles that you want to control. and it should be OK.

The other things that you MUST have is the Standard Assests/Scripts. You need both the SmoothFollow script and the MouseOrbit script. You don’t have to assign any of them though.

What this does is allow you to use the P key to pause, the C key to swap cars and the V key to swap views.

OK, onto the main feature Tweaking… More tweaking than you can imagine. To do this I actually had to setup more than one vehicle with different properties for each so that I could test and make sure that things were going smoothly. So in the demo that we will be looking at I have added two new vehicles. Unfortunately I wont be sharing them, but they are there so that you understand that if you are building a vehicle controller for more than one type of vehicle, you need to test it on the types of vehicles that you are using.

The Code:
// VechicleController.js

I noted the “notable” changes in red. There probably were some other minor things. The big thing was adding the overall suspension information into the Vehicle script then referencing it in the Wheel script. With these values you can force the suspension to be very specific in nature. In the case of the Tractor that was added to the scene has relative suspension amount of zero. A solid axle. Now this isn’t entirely accurate for the tractor, and I am sure that it handles like crap because of it on non smooth surfaces. But having large iron wheels bouncing up and down would kind of make it more unbelievable. How to solve this, will be in a later episode.

A important notation is the drawing of the gizmos for the wheels. This will be important later on as we will use the numbers generated there to figure out if our wheel is slipping sideways and if we should do skid marks and smoke.

Next episode we will cover two very big factor in racing and driving… Realistic suspension and gearing.

For now, enjoy the show

Edit: Seems like it was bombing because I did not set the power, steering and brake to zero when swapping cars. fixed now.

Cool!! Your car changer script crashes my pc a lot of times.

Edit: Thanks! Btw. Select Extrapolate in the RigidBody settings to make the cars’ camera not shake/shutter. :wink:

Thanks again. But what’s wrong with all these camera scripts? The classic “SmoothFollow” has been with us for years now, but I think it’s not good enough. Also in this example - when going uphill- the camera jitters like crazy and, like all other camera scripts, it goes through walls/terrain. A descent camera script for cars seems to be really hard. Even the ‘official’ unity car tutorial has a bad camera script (CarCamera). When you stop the car in the Unity car tutorial (almost impossible by the way) the camera keeps flipping back 'n forth.

I can only hope that you, BigMisterB, will show us how to achieve a nice, smooth car-following camera in all circumstances (even when the camera collides with the terrain or other objects - allthough that might be beyond the scope of this tutorial).

Speaking of which; just how advanced is this tutorial going to be? Will you be teaching us how to create car / track selection menu’s, replay, ghost car, timer, highscore list/leaderboard etc. etc. - or will you mainly focus on the car physics?

For now I’m really enjoying your show. Just curious; who’s paying you to do this?

Are you serious? Does this prevent the camera shake or as I called it: the camera ‘jitter’? Never tried it, but now I will! thanks carking!

Yes. It helps a lot. :slight_smile:

Okay then, this one’s for Google :slight_smile:

unity3d. To prevent camera shake shutter jitter SmoothFollow, set rigidbody interpolate to extrapolate.

this is awesome… something i have been loooking for since much time… it would be great if you can show some implementation of the AI too after this one is done…

Laudable effort, BigMisterB. This will make a great addition to the wiki!

I got a chuckle with the wizard driving the tractor.

OK my friends.

This is probably going to be the last big handling post that I do. (Past this post we will be concentrating on other areas. Any handling will be mentioned but not be a primary subject.)

For my first part, I will have to backtrack here for a second as it was brought to my attention that some variables were left open, thus confusing some people on what they were for. We will take steps to ensure that never happens again.

I have added a bit of administration to the script so that people can’t do dumb things to mess it up. (well, they can do dumb things, but not some dumb things)

I cleaned up the Collision requirements. If you use a bunch of boxes for the collision, you will still have to parent them to one of the boxes, but since they contain colliders, it will not override them anymore.

You MUST fulfill the colliderBase variable. This is primary for telling how the car collides with things. DON’T FORGET

I also cleaned up the Wheel definition. If there is only one mesh, it will now assume that that is the vis mesh and not the collision mesh. Thus not making it invisible.

Handling:

I will start this by pointing out one major fact. Almost all vehicle handling revolves around how the wheels react to the ground that they are on. What I am going to present here is only one interpretation of it. The fact still remains, If you want a vehicle to react to the ground, you HAVE to control how fast that reaction is. When a person drives your vehicle for the first time, in ten seconds he will either quit playing or continue. Nine seconds of that, is handling.

One second of control, which is the first second… is center of mass. Up til now we have been working with whatever the model designed deemed as the center of the car. Modelers are seldom if ever right. In my case, the whole car was standing on Y zero. (Z zero in max) For sure this isn’t right. Well, Unity can save us in this instance. Once you have applied all of your collision objects, it sets the center of mass to all of those bounds averaged. Now this isn’t perfect either but it is an equitable starting point. From here we simply adjust the center of mass to what we need. In the case of what we want now, we will just assume that the new center of mass is Vector3(0,0,1.0). Meaning that the engine causes enough weight difference to cause the rotation of the vehicle to be closer to it.

We need to first fiddle with the relativeSuspensionAmount. This is defaulted to 0.25. Raise the value to give the whole car a bit of lift if you need it. You can test it by simply starting your car in the air, and letting it drop. If it springs up some and bounces just a little. That is what you want. If it hits the ground, and doesn’t bounce, it will have very bad handling from the start.

Next, we test the car with the default settings for everything else. Adjust the center of mass. Start it, if its not right, stop it rinse and repeat. Getting this number right is 90% of the actual work for tuning a car, so take your time here and don’t get confused with the other settings. Center of mass is how the car rotates.

Next, we have to get control of a few things. Power, drag and suspension stiffness. These 3 elements tell how the vehicle responds to events.

The first thing we are going to do is to limit the speed. For this, we are going to use a simple maxSpeed variable. This variable will be used in all of our speed based calculations. How we figure that number is simple: We expose a variable that I use called relativeVeloctiy, Then drive the car around a bit, keep tabs on what seems like a good stable speed, then assign it as the max. MaxSpeed is based on drag in this instance. The closer we get to it, the more drag the vehicle has. Also, maxSpeed is units per second. (or meters per second) not MPH, not KPH… but you can calculate them from it.

The next number we need is a consistent relative max speed. This is the number that we are going to base all of the variables that need to be tuned to an overall value. Things like steering, suspension and drag.

For the actual Power, Drag and Stiffness, we are going to start using a object in Unity called AnimationCurve. For everything we could try to achieve with all kinds of math, these curves do it the easiest and quickest of everything. So much so, that I wish that they were a Unity prefab object that you could just drag and place where you wanted it.

Drag:
Drag is a simple curve that gives a value between zero on 0.5. It is based off of the current forward speed / maxSpeed. The point with Drag is that you don’t want to use it, until near the very end. Then you ramp it up and don’t let the car exceed that speed.

Power:
The power curve is very simple, it is the amount of power that can be used from the enginePower at the speed we are going. Again, derived from speed / maxSpeed.

SidewaysStiffness:
This is a special curve that denotes how much side grip our tires have at speed / 100. I use 100 here because you don’t want a vehicle that is going 20 (maxSpeed) to slide all over the place. 100 seemed nice.

There will be more curves that we will add as time goes on, but these are the most important.

OK, the last change, is an addition. The original post is HERE It is an Anti Roll script. This script will augment the rest of the stuff and help to keep the vehicle level as you drive. I have seen where this script will cause problems in off road type of games where slight jostling of the corners of the vehicles will have odd results. I give it because it is a must for relating actual physics of an antiroll bar in a vehicle.

The Code:

All Code is will be kept up to date HERE