Module 5 · Examples

Unity-Specific Tools

Coverage

🛠Unity skills

  • Platformer support: Rigidbody2D (Dynamic)
  • Coroutines: near-term, fixed-count periodic actions
  • Scriptable Objects: a templated graphical interface to data
  • Particle Systems: the beginning of creating effects

💡Concepts

  • Nothing much here: mostly how to use Unity to accomplish specific tasks.

Examples for Module 5

5.1

Support for Platformers

Run behavior
DSlides toward the right. Watch out: moving right is only possible on ThePlatform (the upper brown platform) or in the air; you cannot move right on TheFloor (the black platform). Awkward to control and no fun, but it makes the point that different (types of) platforms can have different behaviors.
AMoves left
WStrange: seemingly wants to jump
SpaceFlies upward

Make sure to look at the Console printout, and tell “Collision” apart from “Trigger”!!

  • Brown (ThePlatform) / Black (TheFloor) / Orange (StationaryObstacle): simple platforms
  • White (Collide-And-Overlap): only detects collision (allows overlap)
  • Pink (Collide-And-Push): detects and maintains collision (no overlapping)

Scene setup

ObjectBoxCollider2DRigidbody2D
Hero Is Trigger off: it's not just for detecting collisions, it's more. Try switching it on and watch the hero free-fall. (When on, you only want to know there's a collision.) The active party in collisions: this object collides with others.
  • Body Type: Dynamic (things move under gravity now)
  • Constraints: Freeze Rotation Z (the object will not rotate as a result of collisions)
Platforms
(brown/black/orange)
YesNone: they're not active colliders. Other active objects (with a Rigidbody2D) are expected to collide with them.
Collide-and-Overlap
(white)
Is Trigger on (only for detecting collisions). That's why the Hero can detect colliding with it, and can overlap it.None
Collide-and-Push
(pink)
Is Trigger offDynamic: you can push the box.
Kinematic: the box stays stationary.

The Hero also has the HeroMovement script, described next.

HeroMovement.cs

  • mHeroPhysics: a reference to the Rigidbody2D component.
  • [SerializeField]: notice this attribute; it allows access to private data from the editor.
  • Update():
    • A/W: try to change the position directly. Notice W doesn't work well at all; it fights with the physics after the first y-movement!
      LessonWhen Dynamic is on, changing the position directly may not work. (Moving right seems to work because physics isn't operating while the Hero rests on a platform.)
    • Space: changes the traveling velocity (it goes back to zero because of gravity).
    • D: only allows rightward movement when touching the brown platform, or not touching anything (in the Default layer).
CallbackWhen it's called
OnCollisionEnter/Stay/ExitCollision with objects whose Collider's Is Trigger is off. Default: collide and stay (e.g., standing on a platform), unless the other object has a Dynamic Rigidbody2D, in which case it's pushed.
OnTriggerEnter/Stay/ExitThe objects will overlap: detects a collision with no behavior assumptions.
Learned
  • How to build a platformer with Collider2D and Rigidbody2D
  • Collider2D Is Trigger: on = only triggers, and objects overlap; off = triggers, and with a Dynamic Rigidbody2D, pushes the object
  • Rigidbody2D Body Type: Dynamic for simulating gravitational physics; Kinematic for stationary
  • Constraints: freeze the relevant rotations in the physics simulation
Check your understanding
  1. In the air, you hold D and Space together. Does the hero fly up and to the right? Look at the order of the checks in HeroMovement.Update().
    Show answer

    No, it flies straight up. D sets linearVelocity = (mSpeed, 0), but the Space check comes later in the same Update() and sets linearVelocity = (0, mSpeed), replacing the whole velocity. The last assignment wins, so the sideways part is lost.

  2. The hero's Rigidbody2D has Freeze Rotation Z checked. What would you see without it, when the hero runs into the orange StationaryObstacle?
    Show answer

    The hero would tip over and start tumbling. A collision pushes on the edge of the hero's box, which makes a Dynamic body rotate. Freezing the z rotation keeps the physics from turning the hero, so it stays upright.

5.2

Coroutines

Short-term periodic actions, usually a fixed number of them

Same scene and controls as above; the only interesting thing is the white block.

Two changes from 5.1D now works everywhere: the 5.1 rule (move right only on ThePlatform or in the air) was removed. The Hero also no longer logs its collisions and triggers, so the Console shows only the white block's messages.
Run behavior Move the Hero into the white block from the left or right, and watch the block change color:
  • From the right: the white block blinks yellow/magenta
  • From the left: the white block goes through a series of color changes

Scene setup

Identical to the above. Recall that the white block:

  • does not have a Rigidbody2D (it can't actively collide with another object), and
  • does have a Collider2D with Is Trigger on, so it can report collisions (through the OnTrigger functions).

WhiteMagic.cs on the white block

  • mColorChanging: of data type Coroutine; when not null, the white block is in color-changing mode.
  • OnTriggerEnter: tries to enter color-changing mode.
  • OnTriggerExit: tries to exit color-changing mode.
  • ColorChangeFromLeft/Right(): note the yield return statement. This is where the function yields execution until later.
  • EnterColorChangeMode():
    • rightFunc: of data type IEnumerator, which can hold the address of a function.
    • Calls StartCoroutine() to start either the left or the right color-changing mode.
  • ExitColorChangeMode():
    • StopCoroutine(an argument of data type Coroutine): you can have many coroutines running and choose a specific one to stop.
    • StopAllCoroutines(): stops every single coroutine.
Watch out Coroutines run synchronously (e.g., after the Update() calls of all components). This is powerful and can be confusing, and, very importantly, it is very challenging to debug! Use with care; don't misuse it.

Reference: Unity Manual: Coroutines ↗

Check your understanding
  1. ChangeRate is 0.5 seconds and the color sequence has 7 colors. You move the Hero into the white block from the left and keep it there. How long until the block turns white again?
    Show answer

    About 3.5 seconds. ColorChangeFromLeft() shows each of the 7 colors, then waits 0.5 seconds (yield return new WaitForSeconds(ChangeRate)) before the next one, so 7 × 0.5 = 3.5 seconds. Then it calls ExitColorChangeMode(), which restores white. If the Hero leaves earlier, OnTriggerExit2D() stops it right away.

  2. You replace yield return new WaitForSeconds(ChangeRate); with yield return null;. What do you see now?
    Show answer

    Just a brief flicker. yield return null resumes the coroutine on the very next frame, so all 7 colors go by in 7 frames, about a tenth of a second at 60 fps.

5.3

Scriptable Objects

A templated graphical interface to data
Run behavior D slides right, A moves left, Space flies upward. Pay attention to the colors:
  • White/Black/Red block: collide from the right and it blinks between two colors; collide from the left and it goes through a series of color changes.
  • Pink/Orange: on collision, spawns an egg at the Hero's location that behaves like a Red block.

Scene setup

Similar to the above. Take note of the Resources folder:

  • a Sprite: Egg
  • the ColorBlocks folder: three ColorBlocks (these are ScriptableObjects, discussed next)

ScriptableObject/ColorsForBlocks

ThinkA MonoBehaviour is for modifying behavior; a ScriptableObject is for providing data to the behavior.

How it works (see ColorsForBlocks.cs):

  1. Create a new C# class that subclasses ScriptableObject.
  2. Take note of [CreateAssetMenu()]:
    • fileName: the default file name
    • menuName: what shows in the Create menu
    WarningIf you change these argument strings, you'll probably have to restart Unity.
  3. Now code any data you want to provide; in this case, the colors for the WhiteMagic behavior from the previous example. Notice it only provides data: no behavior and no modification of the data.

How to use: ColorsForBlocks is like a Sprite

In the Assets folder, you can now create an instance of ColorsForBlocks: right-click in the Project window, then Create › Create colors for Blocks (the name set by [CreateAssetMenu(menuName = ...)])!

Egg (a Sprite)RedBlock (a ColorsForBlocks)
CreateDrag in an image to create a new Sprite, and rename it EggRight-click › Create › Create colors for Blocks, and rename it RedBlock
UseDrag onto any GameObject's SpriteRenderer to give it a lookDrag onto any ColorBlock to provide its colors for color-changing
ChangeChange the image, and all GameObjects referring to Egg change appearanceChange RedBlock's colors, and all ColorBlocks referring to RedBlock get the new colors

The three Resources/ColorBlocks are three instances of ColorsForBlocks with different color values: data for the ColorBlock script.

ColorBlock.cs

This was the WhiteMagic.cs script. Instead of defining the color data in code, it now has a reference to a ColorsForBlocks:

ColorBlockdefines the behavior, without the actual color data. Like a SpriteRenderer, which defines how to show an image on a GameObject without the actual image.
ColorsForBlocksdefines the data. Like a Sprite, which defines the image.
Notice BlackBlock, WhiteBlock and RedBlock define very different colors. The ColorBlock-White/-Red/-Black objects all have the same ColorBlock component, but with different ColorsForBlocks, so they have the same behavior with different data: they all blink, in different colors.

DynamicBlock.cs

  • Shows/verifies how to create a ColorBlock from scratch.
  • The pink/orange blocks have this component.
Check your understanding
  1. While the game is running, you select the RedBlock asset in Resources/ColorBlocks and change its colors. Then you stop the game. Are the new colors still there?
    Show answer

    Yes. RedBlock is an asset, not part of the scene, so the “Play mode changes aren't saved” rule doesn't apply: edits to a ScriptableObject asset during Play mode stay. Every ColorBlock that refers to RedBlock, including the eggs the pink and orange blocks spawn, uses the new colors right away, too.

  2. DynamicBlock calls AddComponent<ColorBlock>(), then sets mColorMagic on the next line. ColorBlock.Start() reads mColorMagic. Why isn't it null there? When would it be?
    Show answer

    A newly added component's Start() doesn't run during AddComponent(); it runs later, just before the component's first Update(). By then the next line has set mColorMagic. Awake(), though, runs immediately inside AddComponent(), so if ColorBlock read mColorMagic in Awake(), it would still be null.

References

✦

Particle Systems

  • Take a look at the Unity Manual: Particle systems ↗
  • Focus on the Built-in Particle System.
  • Imagine a collection of points following a pseudo-random function.