CTIN 483

Week 9 Tuesday

Today

  • Fall break check-in
  • Original game project
  • Presence exercise
  • Drawing

Fall Break

Presence Exercise

Drawing

Good Simple Games

Hexecutable, 2014

Simple Machine, 2017

Simple Machine, 2012

Null References

and the Impermanence of Things

When your code instantiates a new thing, you can keep a reference to the new thing like this:

GameObject newGO = Instantiate (missilePrefab);

Then newGO will point to that new missile, for as long as it exists.

If you do this in Player.cs:

It's like calling a number that has been disconnected.

And then the missile, unbeknownst to you, is destroyed, then the variable newGO will still exist, but it will point to nothing, or null.

GameObject newGO = Instantiate(missilePrefab);

What you are doing is asking nothingness (null) for its name. By definition, nothingness does not have a name. So you will get a Null Reference Exception.

If you try to get to a thing that has been destroyed, for example like this:

Debug.Log(newGO.name);

So how can you prevent NullRefs? From now on you are going to start littering your code with null checks, like this:

This is such a popular error that it has a nickname: NullRef.

if (newGO != null) {
   Debug.Log(newGO.name);
} 

It is annoying, but necessary anywhere that an object might be destroyed.

if (newGO != null) {
   Debug.Log(newGO.name);
} 

Look for anything matching the pattern "something dot something".

If you are getting a NullRef error in your game, double-click on the error in the Console and it will take you right to the offending line.

Here we have Debug.Log and newGO.name. Debug cannot be null, so we know that newGO must be the problem.

Debug.Log(newGO.name); 

Review:
Animation Jargon

Unity's animation system has a lot of moving parts, as you saw in the homework. Here's a recap of the terminology.

Animation Clip + Window

An animation clip, sometimes just animation, is a file that stores the information about what's changing: the object's sprite, position, rotation, etc.

The Animation window is where you set up and edit animations.

Animator Controller

An animator controller is a file that remembers a set of animations and how they are connected (transitions).

The file looks like boxes and arrows, just like you see in the Animator window.

The Animator window is where you set up the animations and transitions using boxes and arrows.

 

You can control what is happening from code using parameters that you set up here.

AnimatOR Component

The AnimatOR component is how you attach a controller to a specific GameObject.

The Animator component can control the Sprite Renderer (and other) components for that GameObject. For example, it can tell the Sprite Renderer which sprite to show.

Coroutines

Coroutines are complicated. You don't have to understand them fully to make Unity games, but they are powerful.

StartCoroutine(PlayFlagpoleAnimation()); 

IEnumerator PlayFlagpoleAnimation() {
  //Do stuff
  yield return someCondition;
  //do more stuff
}

The basic setup looks like this:

Coroutines: Pause

The coroutine you will use the most (by far) is Unity's built-in WaitForSeconds coroutine.

//Do stuff

//then wait
yield return new WaitForSeconds(1.5f);
 
//everything after the yield happens after 1.5 seconds
yield return new WaitForSeconds(1.5f);

"yield" means "keep going with the rest of the game".

Everything after "return" is called the yield condition. It is something that will eventually be true. For example, "1.5 seconds has passed".

What is a coroutine, actually?

 

You call them using a special syntax, but they're still just functions. What's special is how they hook into Unity's core loop – the code that it runs every frame.

When you call one, it runs normally until it reaches a yield statement. Then it sets a sort of bookmark and yields, that is, tells the rest of the game to proceed.

 

Each frame right after Update, Unity calls the coroutine again. The coroutine returns to its bookmark and checks its yield condition.

Eventually the yield condition is true — for example, 1.5 seconds have passed. The bookmark is deleted and the rest of the coroutine function runs as usual.

Your coroutine function can have more than one yield statement. It works the same way; the "bookmark" just moves to the latest yield statement.

//Do stuff
//then wait 1.5 seconds
yield return new WaitForSeconds(1.5f);
 

//Do more stuff
//then wait 2 seconds
yield return new WaitForSeconds(2f);

 

Coroutines: Beware

Coroutines are useful, but even when you understand them they can be hard to work with.

Probably okay:

  • to create repeating actions, like in Glitch Garden
  • to run an animation or play a sound that doesn't change the game state

Likely to confuse you:

  • chaining them together
  • any setup where calls might overlap
  • anything that feels complicated

CRC Cards

A CRC card contains three pieces of information:

  • The Class name
     
  • The class's main Responsibilities, described in human language using only a few words
     
  • The class's main Collaborators, that is, the names of other classes that this one talks to.

A collaborator is a class that your class needs a reference to.

 

For example, a Player might spawn Missiles. But if Player doesn't actually need to talk to Missiles, then it's not a collaborator.

When you think about the Collaborators, imagine it as a one-way arrow. What classes does this one talk to?

 

Sometimes one class needs to talk to another, but it's only in one direction. In that case, class A is a collaborator of class B.

 

Other times the classes need to talk to each other. In that case, A is a collaborator of B, and B is also a collaborator of A.

CTIN 483 - W9 Tu Oct 18 - 2021 Fall

By Margaret Moser

CTIN 483 - W9 Tu Oct 18 - 2021 Fall

  • 196