CS 491 Lecture 7 – Rigidbody
TODO
- Watch Health HUD. On a 1/4 sheet, draw a sketch of a screen space overlay that you might want to present in your first- or third-person games this semester. Annotate each widget with a short but meaningful purpose.
Lab
Our goal today is to learn how to integrate objects we create into the physics system of our games. We’ll look at how to use colliders and rigidbodies to make a moving, firing cannon.
Terrain and FPSController
Quickly add a terrain and an FPSController
. The flatter the terrain, the easier testing will be—at least initially. Give the terrain a texture.
Import Cannon
Import the cannon model by dragging the cannon’s Blender file and texture into the project assets. You will be a significantly happier person if the origin of both the wheels and the barrel lies within the center of the axle at world position 0, 0, 0 and there are no rotations or scales applied at the object level. If this isn’t already true or you don’t understand what I’m talking about, I suggest using this reference model so that you can move on to the objectives of today’s lab.
Assign the texture to the cannon’s material and place the entire cannon object on your terrain near your FPSController
. But keep it a little above the ground that we can see gravity pull it down in a later step.
Barrel Controller
Unity’s physics engine (PhysX, licensed from NVIDIA) helps us move our objects realistically without a lot of work on our part. However, we don’t want too much realism. In particular, we want to be able to change the elevation angle of the barrel easily without fighting gravity.
To enable raising and lowering the barrel, we’ll add a new axis of input. Go to Edit / Project Settings / Input. Expand Axes and increase the size by one to add a new axis that’s a clone of the last one in the last. Expand the clone and rename it Tilt. Enter q
for Negative Button, e
for Positive Button, clear out the alternate buttons, and set Gravity and Sensitivity to 3. However over the labels to learn what these settings mean.
In code, we can determine the tilt “pressure” with a line like this:
float pressure = Input.GetAxis("Tilt");
The value returned by GetAxis
is in [-1, 1].
Add a BarrelController
script to only the barrel object. In Update
, use this pressure reading along with transform.Rotate
to raise and the lower the barrel. Test!
Wheel Physics
Unlike the barrel, the wheels should behave realistically. We want them to react to the ground over which they roll. We will need two things for an object to be managed by the physics engine:
- A collider for marking the physical extent of an object. The simpler the bounding volume, the faster collision detections will be.
- A rigidbody for specifying physical properties like mass and for applying forces.
Add both of these to the wheel object. Use a Capsule Collider oriented with the axle and fitting snugly around both wheels.
Test. How did that go? Hopefully you saw the wheels drop but the barrel stay put. Recall, the barrel is not managed by the physics system. It only moves when we tell it to. Let’s fix this by manually locking the barrel to the wheels:
- In
BarrelController
, add a publicTransform
for the wheels. - Drop the wheels game object into the new
Transform
input in the Inspector. - In
Update
, set the barrel’s position to be the same as the wheel’s position:transform.position = wheels.position;
Does this behave more like you’d expect it to? We will never escape this need for a medley of both physics and manual control.
Rolling the Cannon
Now let’s add another input axis for rolling the cannon forward or backward. (We won’t worry about turning left or right in this lab, which requires wheels that spin independently.) Call it Roll and configure it much like Tilt, but use different keys.
When we manually control an object, we listen for user input and manipulate an object’s transform accordingly in Update
. When the physics engine controls an object, we listen for user input and manipulate an object’s rigidbody in FixedUpdate
. The physics system updates via FixedUpdate
at a steady rate, whereas screen redrawing via Update
happens more irregularly. If you apply forces in Update
, the results will be less realistic.
Add a WheelController
script to the wheels. Cache a reference to the rigidbody component with something like this:
private new Rigidbody rigidbody;
void Start() {
rigidbody = GetComponent<Rigidbody>();
}
The keyword new
in the declaration is used to hide an inherited property of the same name. Without it, we get a warning about clashing names.
Add a FixedUpdate
method, query the Roll pressure, and experiment with Rigidbody.AddForce
to make the cannon roll.
Shot
Let’s give the cannon something to fire. Create a sphere game object, add a material and rigidbody, and drag it to the project assets to turn it into a prefab—as it’s something we’ll want make instances of programmatically.
In BarrelController's
Update
, we want to fire off an instance of our shot when the user clicks the mouse. Input.GetButtonDown
can tell us if a "Fire1"
event is occurring. Recall that instantiation is used in this way:
Instantiate(prefab, initialPosition, initialRotation)
We’ll need a public for the prefab to instantiate. initialRotation
can just be Quaternion.identity
.
Regarding the initialPosition
, let’s create an empty for the shot’s seed point or birthplace. Make an empty game object as a child of the barrel. Reset its position to 0, 0, 0 and then move it somewhere inside the barrel.
Add a public Transform
for this seed point to BarrelController
and drop in the empty in the Inspector. This is very common idiom for creating little 3D “bookmarks” to positions of interest. Their visual nature makes them easier to understand and edit than hardcoded numbers.
After we’ve instantiated the shot, we need to apply some force to it to project it outward from the barrel. Finding the direction in which the force should be applied can be a little tricky. Click on the seed point empty, select the translate tool, and find which axis points along the barrel. If it’s red, our direction of force is shotBirthplace.right
. If green, shortBirthplace.up
, If blue, shortBirthplace.forward
. You may need to negate the vector.
Apply force through the shot’s rigidbody along the outward vector.
Explosions
Let’s audio some sound effects. Download sfxr and generate a pleasing explosion sound. Import into into the project assets.
Add an AudioSource component to the barrel. Add an AudioClip public and wire in the explosion clip. When the player fires, ask the audio source to play the sound:
audioSource.PlayOneShot(explosionClip);
You’ll want to grab a reference to the audio source much like you did for the rigidbody.
Block Stack
Make a stack of blocks to give the cannon something to knock down. Configure one block first and then make it a prefab. What components will you need? Scripts?