teaching machines

CS 491 Lecture 3 – Boulder Over Your Shoulder

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

TODO

Lab

Remember that scene in Raiders of the Lost Ark?

Let’s recreate that today. Game development concepts and Unity tricks that we’ll run into include:

With a partner, work through the steps below in Unity. Ask a lot of questions!

Groundwork

Let’s start by creating a ground plane for our tunnel dashing. Create a new Unity project in 3D. Don’t import any asset packages yet.

Last week we made our own 3D models. We could make a ground model ourselves, but Unity comes with a few simple models of its own. Find them in GameObject / 3D Objects. Add a plane to your scene, and scale it to form a long runway.

Create a material in your assets library. Change its albedo (the base color of an object, ignoring light, microfacets, etc.) to something suitable for a ground plane.

First-person Controller

We want to walk around on this plane from a first-person perspective. Unity comes with a prefabricated first-person controller that makes this pretty straightforward. Import it via Assets / Import Package / Characters. Deselect all assets by clicking None. Check the CrossPlatformInput, FirstPersonCharacter, and Utilities directories.

Drag the FPSController prefab that you just imported (find it!) into the scene view or hierarchy. Where exactly is it located? How can you find out?

Move it to one end of your runway.

Play your game. How does the character move? Run? Jump? How can you turn off head-bobbing?

Ball

Add a sphere to your scene. A big one. If you squint, it looks a little like a boulder. Give it a grayish albedo.

The Indiana Jones scene has the boulder behind Indie, chasing him. That worked well for a third-person camera. But ours is first-person. To build tension, our first person controller needs to see the boulder coming at it. Place the boulder at the opposite end of the runway.

Wall

Add walls to enclose your tunnel and channel the sphere toward the player. Give them a distinct albedo.

Place the walls close enough that there’s not much room for the player to avoid getting run over by the boulder. Instead, create little nooks/refuges in the wall that the player must run between for safety.

You might find it helpful to create one wall and then duplicate it with Control/Command-D.

Constant Force

The boulder isn’t very intimidating just hanging in the air like a nearby moon. To hand it off to Unity’s physics engine, add to it a Rigidbody component. This will make it subject to gravity and collisions.

However, a Rigidbody alone doesn’t make it “bloodthirsty.” It needs some extra nudging. Add a Constant Force component. What settings send it toward the player?

Failure

A key element to games is uncertainty. The player must not be assured of survival or a reward. If these are certain, the player loses interest in the game. A loss of interest in performing some behavior is called extinction by psychologists. Some extinction is inevitable, and Ultima Online designer Raph Koster says that we should lose interest in a game when we’ve learned all we can from it. Generally, though, we want to slow down the extinction process by presenting the player with challenges that beg to be resolved and carry a certain amount of risk.

What does all this mean? The player must flatten like a pancake when the boulder hits it. As the boulder rolls off, some of the player’s intestines must cling momentarily to it, then slide off and hit the floor with a splat.

Such behavior isn’t built into any existing game object or component. However, we can create our own components by writing scripts!

With the FPSController selected in your hierarchy, click Add Component / New Script in the Inspector. Name the script PlayerController. Double click on this script to open it for editing. Wait five minutes for MonoDevelop or Visual Studio to open. (I use vim to edit my scripts.)

Delete Start and Update. Where we’re going, we don’t need them. Instead, we want a callback for when our player collides with things:

void OnCollisionEnter(Collision collision) {
  // what to do when the player hits something
}

First, add a little debug message:

Debug.Log("I hit " + collision.gameObject.name);

Play your game to make sure collisions are being detected. What do you notice?

We only really care about collisions with the boulder. There are several ways we can filter out these collision events, but probably the best is by using tags. Click on your boulder and in the Inspector click on the Tag dropdown menu. Add a new tag named Boulder and assign it to your boulder.

Then do a conditional check on the colliding object’s tag. If it’s a boulder we hit, we want to the player to stop moving. One way to do this is by destroying the player game object:

Destroy(gameObject);

What happens when your player gets hit? Can you think of a better way to handle player death?

Rebouldering

Almost there. Another key element of a good game is repetition. We’re more eager to learn a skill or mine a monster’s statistics if we many opportunities to use that skill or knowledge. So, we need not just one boulder, but many.

Let’s set up a boulder loop. When one boulder falls off our runway, let’s destroy it and replace it with another back at the start. Add a new script to your boulder and call it BoulderController.

Delete Start. That’s where one-time setup code goes. In Update, we put code that must execute every frame. User input is usually handled here. There’s also a FixedUpdate that gets called every time the physics engine calculates objects’ positions. Since we want to destroy the boulder when it drops off the platform, let’s put our code inside FixedUpdate.

We ask a game object about it’s position through its implicitly available transform property. How can you query the boulder’s y-coordinate? Delete it when the boulder falls below the ground plane.

To make a new one back at the start, we can use the Instantiate method:

Instantiate(this, new Vector3(?, ?, ?), Quaternion.identity);