Doers of Stuff.org

A place to Do Stuff and see Stuff Done…

Determining How Long Powerup Effects Should Last

I remember growing up and playing “Army” with my friends. We’d crawl around in the woods with our toy rifles, trying to sneak up on each other. Catching sight of my enemy, I’d take aim, breath out and gently pull the trigger. Then I’d lean up and shout as loud as I could “I got you!” At this point, my friend had a choice. He could either descend into a gloriously Shakespearean death scene, or he could boldly shout back “No you didn’t! You missed!” Now, in my father’s time, this was a little less likely to happen since they were actually shooting BB guns at each other, but I digress.

This did get me thinking about Object Oriented programming and how objects communicate. Fundamentally, objects are self-contained. Each object contains both the data and behavior that defines it. This includes defining how other objects should communicate with it.

I thought of objects being like biological cells and/or individual computers on a network, only able to communicate with messages (so messaging came at the very beginning – it took a while to see how to do messaging in a programming language efficiently enough to be useful).

Alan Kay

As I start adding more and more game objects to my game and increase the need for those objects to communicate with each other, I continuously ask myself who does what and how do they know what to do? This came up recently with the addition of my game’s first powerup. The first powerup is a triple shot laser. It is controlled by a simple boolean that says whether or not it is enabled. Every time we fire, we check this boolean. If false, we fire the standard laser. Otherwise, fire the triple shot.

using System.Collections;
using System.Collections.Generic;
using UnityEngine;

public class Player : MonoBehaviour
{
    [SerializeField] private bool         tripleShot      = false;
    [SerializeField] private float        tripleShotTimer = 5f;
    [SerializeField] private GameObject   tripleShotPrefab;

    ...
}

...

    private void FireLaser()
    {
        if (tripleShot)
        {
            Instantiate( tripleShotPrefab
                       , transform.position + laserOffest
                       , Quaternion.identity);
        } else {
            Instantiate( laserPrefab
                       , transform.position + laserOffest
                       , Quaternion.identity);
        }
        laserCanFire = false;
        StartCoroutine(LaserCoolDown());
    }
 
 ...

}

The next question is how do we toggle this variable? We know the powerup is collected by colliding with it. This is when we will “collect” (destroy) the powerup game object. The powerup game object will have an OnTriggerEnter2D() method in which we call the Destroy() method. One possible solution would be to reach out here and toggle the boolean in the Player class. However, even if we do so using a public getter/setter type method, this seems like a bit of a reach in. Kind of like getting hit by a BB rather than just having my buddy yell at me and letting me decide what to do about it.

It seems more polite, for the powerup to simply send a message to the player like “Hi, I’m a Triple Shot powerup and you just collected me!” The player would then be free to decide what to do with that information. But as it turns out, even that is unnecessary. After all, why bother sending a message to the player when the player can figure out what it just hit all by itself? Our Player class already has an OnTriggerEnter() method. All we have to do is add to it.

    private void OnTriggerEnter2D(Collider2D other)        
    { 
        if (  other.tag == "Enemy") { TakeDamage(); }
        if (  other.tag == "TripleShotPU"
           && !tripleShot)          { }
    }

Since this code is in our Player class, it is free to modify the tripleShot boolean directly. We can also set the rules for disabling it. In fact, we can do both with a simple coroutine.

    private void OnTriggerEnter2D(Collider2D other)        
    { 
        if (  other.tag == "Enemy") { TakeDamage(); }
        if (  other.tag == "TripleShotPU"
           && !tripleShot)          { StartCoroutine(PowerUpTripleShot()); }
    }

    private IEnumerator PowerUpTripleShot()
    {
        tripleShot = true;
        yield return new WaitForSeconds(tripleShotTimer);
        tripleShot = false;
    }

The coroutine immediately sets the boolean to true, enabling the powerup. It then yields for the prescribed amount of time, then comes back and disables it.

Neat, clean, no crosstalk and no BBs.

Leave a Reply

Determining How Long Powerup Effects Should Last

by Robert time to read: 3 min
0