Creating a Modular Power-Up System.

Michel Besnard
5 min readApr 11, 2023

--

Objective: create a modular system to identify and process randomly spawned “power-up” game objects in Unity.

The approach to creating a modular system to handle your game power-ups stems from the idea that they all share a similar logic. While the Spawn Manager will look after spawning the power-ups at various times, there are other common features these power-ups will share. They all need to travel across the Game Scene, generally in the same manner. They need to be collectible when intercepted by the Player, and upon collection need to trigger a specific effect. Once collected, shot at, or after leaving the Game Scene, they need to be destroyed.

Since they all share many common elements, it makes no sense to rewrite the same code to define the behavior for each power-up, even if how they affect the gameplay will vary.

The question then becomes: how do we modularize the power-up script? Instead of hard-coding the OnTriggerEnter2D method, what happens when a power-up collides with other.CompareTag(“Player”), we need to write the code in such a way as to detect which power-up collides, and only then set specific values in order to trigger a behavior associated with that unique power-up.

Let’s take this hard-coded example and turn it into a more modular snippet.

In my Spawn Manager, I am currently setting up eight power-ups split into three arrays. The first array looks after my basic ones: Triple Shot, Speed Boost, and Shields. The second array has my weapons: Ammo, Homing Missiles, and Lateral Laser Canon. The third array has my Health Boost and the Negative Power-Up. You can view examples of the code I used below:

SpawnManager script ~ Setting up my three power-up arrays.
SpawnManager script ~ StartSpawning() method initiates coroutines to start spawning enemy waves and power-ups.
SpawnManager script ~ example of a power-up spawning coroutine.

So how can we uniquely identify the power-up to which this script is attached? Let’s examine how I worked out the problem…

Power-Up script ~ setting up the variables.

Create a new script, call it “PowerUps”, and set up references to the Player, SpawnManager, GameManager, and AudioManager scripts. Add a private Game Object to hold the explosion prefab (for when the power-ups get destroyed), a private float to cache a reference to the power-up speed from the Game Manager (based on the game difficulty level), and a bool to track if the power-up belongs in the initial start screen of the game (in my version, the player needs to collect two types of weapon power-ups before their weapons are free to fire).

It makes sense at this point to use an integer system to identify each power-up. So let’s create an ID for these power-ups. In my above script, I defined a private int, named it _powerupID, and serialized the field so I can see it and modify it in the Inspector. Attach the script to each power-up prefab and set its “Power Up ID” to a unique integer. Set the Power Up ID for each power-up prefab to its own unique ID. “0” for TripleShot, “1” for Speed, “2” for Shields, etc.

Example ~ Notice how the Power Up ID is different for each of these power-ups.

In the Start() method, cache the Player, SpawnManager, GameManager, and AudioManager scripts, and NULL check them.

Power-Up script ~ caching references and NULL checking.

Call the Movement() method via the Update(). If the power-up is part of the intro screen, then we set its speed to 0 to ensure it remains stationary until collected. Otherwise, we get the desired speed from the Game Manager based on the current difficulty level of the game. We continue to translate the power-up game objects down the screen and destroy them when they go beyond -9.0 on the y-axis.

Power-Up script ~ moving the power-up down the screen.

In our collision detection, we NULL check the player to ensure it’s still alive. We then run a switch statement to compare the Power Up ID of the power-up and run the applicable method in the Player class before destroying the power-up.

Power-Up script ~ Switch-Case statements.

Power-ups, in my version of the game, are susceptible to destruction from either the player or the enemy lasers, unless the power-up has an ID of 7. This is my “Negative Power-Up” and it can’t be destroyed. The required logic is shown below:

Power-Up script ~ detecting collisions between power-ups and laser game objects.

Using this approach of setting a unique ID value to each powerup allows us to easily differentiate between these game objects, which in turn ensures we call the correct function. We are left with a single script that can be easily modified to handle expansion by simply adding another case x: to the Switch statement.

I hope that you have found this article helpful and that it will assist you as you implement various features into your own Unity game project. Thanks for reading :)

--

--

Michel Besnard

Military member with 35+ years of service, undertaking an apprenticeship with GameDevHQ with the objective of developing solid software engineering skills.