How to make a Power-Up System in Unity

Power-ups are a critical gameplay component. In this Unity tutorial by @KevnSmall, you’ll learn how to design and build a reusable power-up system. By Kevin Small.

Leave a rating/review
Save for later
Share
You are currently viewing page 2 of 4 of this article. Click here to view the first page.

Power-Up Coding Checklist

  1. Implement PowerUpPayload to issue the payload.
  2. Optionally, implement PowerUpHasExpired to remove the payload from the previous step.
  3. Call PowerUpHasExpired when the power-up has expired. If the power-up expires immediately, tick the ExpiresImmediately checkbox in the inspector as there is no need to call PowerUpHasExpired.
Note: To create a power-up script, you must create a subclass of the PowerUp class and observe the following checklist. This tutorial refers back to this checklist several times, so keep it handy!

Think about what the Star power-up does: it simply provides a small health bonus. You’ll only need a bit of scripting to make this happen.

Creating Your First Power-Up Script

Add a new script to the PowerUpStar GameObject, name it PowerUpStar and open it up in your editor.

Add the following code, replacing most of the boilerplate Unity starter code while leaving the using statements at the top as they are.

class PowerUpStar : PowerUp
{
    public int healthBonus = 20;

    protected override void PowerUpPayload()  // Checklist item 1
    {
        base.PowerUpPayload();

        // Payload is to give some health bonus
        playerBrain.SetHealthAdjustment(healthBonus);      
    }
}

The code is pretty short, but this is all you need to implement your Star logic! The script addresses each of the checklist points as follows:

  1. PowerUpPayload awards some health by calling playerBrain.SetHealthAdjustment. The PowerUp parent class has already taken care of obtaining the playerBrain reference. The fact that you have a parent class means you will need to call base.PowerUpPayload to ensure all core logic executes before your code.
  2. You don’t need to implement PowerUpHasExpired because the health award is permanent.
  3. This power-up expires immediately, so again, you don’t need to code anything; simply tick the ExpiresImmediately checkbox in the inspector. This is a good time to save the method and switch back to Unity to make some inspector settings.

Creating Your First Power-Up in the Scene

After you save and switch back to Unity, the StarPowerUp GameObject will look like this in the inspector:

Populate the inspector values as follows:

  • Power Up Name: Star
  • Explanation: Recovered some health…
  • Power Up Quote: (I will become more powerful than you can possibly imagine)
  • Expires Immediately: Ticked
  • Special Effect: Drag in the prefab from project folder Prefabs/Power Ups/ParticlesCollected
  • Sound Effect: Drag in the audio clip from project folder Audio/power_up_collect_01
  • Health Bonus: 40

Once you have done this, your power-up will look like this:

Once you’re happy with your PowerUpStar GameObject settings, drag it into the project tree folder Prefabs/Power Ups to create a prefab.

Use your new prefab to add a few Stars to the right of the scene in places you find pleasing.

Run the scene and steer the hero towards the first power-up. Some audio and a glorious particle effect accompany your collection. Awesome!

Message-Based Communication

The next power-up you will create needs some background information. To get that information, you need a way for your GameObjects to communicate with each other.

For example, when you collect a power-up, the UI must know what information to display. When the player’s health changes, the health bar needs to know what the updated health level should be. There are lots of ways you can do this, but the Unity manual lists several mechanisms to accomplish this.

Each communication method has its pros and cons, and one size certainly does not fit all. What you see in this game is message based communication as outlined in the Unity manual.

You can categorize GameObjects as being message broadcasters, message listeners, or both:

On the left in the above diagram are some message broadcasters. You can think of this as the objects “shouting out” when something interesting happens. For example, the player broadcasts the message “I got hurt”. On the right in the above diagram are the message listeners. As the name suggests, these listen for messages. Listeners don’t have to listen to every message; they simply listen for those messages that they might want to react to.

Message broadcasters can also be message listeners. For example, sthe power-up broadcasts messages, but also listens for player messages. A good example of this is a power-up that will expire when a player gets hurt.

You can imagine that if there are many broadcasters and many listeners, there could be many lines criss-crossing between the left and right sides. To make life easier, Unity provides an EventSystem component that sits in between, like this:

Unity uses the extendable EventSystem component to handle input. The component also manages much of the sending and receiving event logic.

Phew! That’s a lot of theory, but the bottom line is that such a messaging system will allow power-ups to easily listen in to the gameplay and reduce hard-wiring between objects. This should make adding new power-ups quite simple, particularly later in development, because most messages will already be broadcast.

Steps to Set Up Message-Based Communication

This is the last bit of theory before you can get back to building. To broadcast a message, you need to take care of these steps:

  • Broadcasters define what message they want to broadcast as a C# Interface.
  • Broadcasters then send messages to listeners stored in a listener list.

See this video on C# Interfaces if you need a refresher.

In short, an interface defines method signatures. Any class that implements the interface promises to provide functionality for these methods.

You will see it more clearly with an example. Look at the code in the IPlayerEvents.cs file:

public interface IPlayerEvents : IEventSystemHandler
{
   void OnPlayerHurt(int newHealth); 
   void OnPlayerReachedExit(GameObject exit); 
}

This C# interface has methods for OnPlayerHurt and OnPlayerReachedExit. These are the messages that the player can send. Now look at the method SendPlayerHurtMessages in the PlayerBrain.cs file. The snippet below contains numbers referenced in the explanation that follows:

   private void SendPlayerHurtMessages()
   {
      // Send message to any listeners
      foreach (GameObject go in EventSystemListeners.main.listeners)  // 1
      {
         ExecuteEvents.Execute<IPlayerEvents>                   // 2
             (go, null,                                               // 3
              (x, y) => x.OnPlayerHurt(playerHitPoints)            // 4
             );
      }
   }

The method above handles the sending of the message OnPlayerHurt. The foreach loop iterates through every listener held in the list EventSystemListeners.main.listeners and calls ExecuteEvents.Execute against each listener, which sends the messages.

Taking each numbered comment in turn:

  1. EventSystemListeners.main.listeners is a list of GameObjects globally visible in the singleton EventSystemListeners object. Any GameObject that wants to listen to any message must be in this list. You can add GameObjects to this list by giving the GameObject a tag of Listener in the inspector, or by calling EventSystemListeners.main.AddListener.
  2. ExecuteEvents.Execute is a method provided by Unity that sends the message to a GameObject. The type in the angle brackets is the interface name that contains the message you want to send.
  3. This specifies the GameObject to send the message to and null for extra event information, following the Unity manual syntax example.
  4. This is a lamba expression. This is an advanced C# topic that is beyond the scope of this tutorial. Basically, a lambda expression lets you pass code as a parameter into a method. In this case, the code contains the message you want to send (OnPlayerHurt) together with the parameters it needs (playerHitPoints).

The project is already set up to broadcast all necessary messages. You might find some of these useful if you want to extend the project and add your own power-ups later. By convention, all interface names begin with the letter I:

  • IPlayerEvents: Used for messages when player gets hurt or reaches exit.
  • IPowerUpEvents: Used for messages when a power-up gets collected or expires.
  • IMainGameEvents: Used for messages when player wins or loses.

The above interfaces have comments throughout if you do choose to explore them. It’s not critical that you understand them for this tutorial, so you can move along if you like.

Kevin Small

Contributors

Kevin Small

Author

Gijs Bannenberg

Tech Editor

Chris Belanger

Editor

Eric Van de Kerckhove

Final Pass Editor and Team Lead

Over 300 content creators. Join our team.