Unity: Creating a modular PowerUp system
Re-useable and easy to edit and advance!
The more PowerUps you want to add, the better it is to use a modular system to determine what function should be called. We have just one PowerUp until now, the Triple Shot. But think about implementing 100 different PowerUps. Creating 100 different functions would not just blow up the file in terms of length and line numbers, it would also be quite challenging and maybe even frustrating to write a different method for every created powerup.
But calm down. You don’t have to do that. There is a solution for this called a switch-Statement.
What is a switch-Statement?
Simply put, a switch statement checks the value of a variable and jumps directly to that part of the code which has the value.
Imagine you have three powerup items and the one with the ID 1 is spawned. You collect the powerup. Instead of checking every single value of the function (what if-statements do), the switch-statement will directly jump to the code with case 1. In this case, the SpeedBoost Coroutine will be called.
switch (_powerUpID)
{
case 0:
_player.ActivateTripleShot();
break;
case 1:
_player.ActivateSpeedBoost();
break;
case 2:
Debug.Log("This is ID 2!");
break;
default:
Debug:logError("Invalid PowerUp ID!");
break;
}
Creating the modular powerup System
Preparation
To get started, we need to create two variables.
On the PowerUp.cs, we have to create a variable to store the powerup ID.
[SerializeField] private int _powerUpID;
To store every single powerup we implement, we need a GameObject array on the SpawnManager.cs.
[Header("Power Ups")]
[SerializeField] private GameObject[] _powerUps;
Creating the PowerUpSpawnRoutine()
We want to wait a bit after the player has started the game before we spawn the first powerup. Therefore, we use a yield return method with the value of 3 seconds.
The powerup should be instantiated at a random position of the screen. For this, we calculate a random X coordinate. I use the enemy borders here because they also spawn inside the visible area of the player screen and therefore, these coordinates can be re-used here as well.
We now have the position, but we still don’t know which powerup will be spawned. For this, we calculate a powerUpID, which will be randomly chosen. The value will be between 0 and the size of the powerup array.
Now it’s the time to spawn the desired powerup! This will be chosen from the PowerUps array. The index will be the just calculated powerUpID. The powerup will be instantiated at the previously calculated randomX coordinate and at the set top position of the enemy. This coordinate works here as well and was used by me because of this.
After that, we have a cooldown of a randomly chosen amount of time between 10 and 30 seconds before we instantiate the next powerup.
Changing OnTriggerEnter2D on the powerup script
private void OnTriggerEnter2D(Collider2D other)
{
if (other.CompareTag("Player"))
{
Destroy(this.gameObject);
switch (_powerUpID)
{
case 0:
_player.ActivateTripleShot();
break;
case 1:
_player.ActivateSpeedBoost();
break;
default:
Debug.LogWarning("Unknown ID!");
break;
}
}
}
As soon as the powerup collides with another GameObject, we should check if it has collided with the Player. Comparing the tag will help us here!
We already null-checked the player at the very start of the game, so we don’t have to do this here for another time.
After the collision, we want to make sure to destroy the powerup GameObject to give the player the illusion of having it collected.
Next up, we need to determine which method we should call from the player script. For this, the given PowerUp ID will be checked and the right method will be called. If the ID has no method assigned, a warning will be displayed in the Console.
And there we go! We successfully converted the powerup system into a modular one. Adding new powerups will be easy-breezy now!