teaching machines

CS 491 Lecture 7 – Rigidbody

February 16, 2016 by . Filed under gamedev3, lectures, spring 2016.

TODO

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:

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:

  1. In BarrelController, add a public Transform for the wheels.
  2. Drop the wheels game object into the new Transform input in the Inspector.
  3. 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?