How to make a Power-Up System in Unity
- Getting Started
- The Power-Up Lifecycle
- Creating a Simple Star Power-Up
- Separating Game Logic with a Class Hierarchy
- Power-Up Coding Checklist
- Creating Your First Power-Up Script
- Creating Your First Power-Up in the Scene
- Message-Based Communication
- Steps to Set Up Message-Based Communication
- A Power-Up for Increased Speed
- Creating the Speed Power-Up in the Scene
- A Push Power-Up
- Optional Challenge: An Invulnerability Power-Up
- Where To Go From Here?
What would Sonic The Hedgehog be without gold rings and power sneakers; Super Mario, without mushrooms; or Pac-Man without power pellets? The games wouldn’t be nearly as much fun!
Power-ups are a critical gameplay component as they add extra layers of complexity and strategy to keep the action moving.
In this tutorial you will learn how to:
- Design and build a reuseable power-up system.
- Use message-based communication in your game.
- Implement these in a top-down “dodge-em-up” game filled with your own power-ups!
You’ll need Unity 2017.1 or later to follow this tutorial, so upgrade your Unity installation if you haven’t already.
The game you’ll work with a 2D top-down arena dodge-em-up; it’s a little like Geometry Wars without the shooting — and without the commercial success. Your helmeted hero has to dodge the enemies to get to the exit; bumping into enemies reduces health. When all health is depleted, it’s game over.
Download the starter project for this tutorial and extract it to a location of your choosing. Open the project in Unity and take a look at the project folders:
- Audio: Contains the sound effect files for the game.
- Materials: The materials for the game.
- Prefabs: Contains the Prefabs for the game, including the play area, player, enemies, particles and power-ups.
- Scenes: The main game scene is in here.
- Scripts: Contains the C# scripts for the game, which have been thoroughly commented. Feel free to nose around the scripts if you’d like to become more familiar with them before starting.
- Textures: The source images used for game and splash screen.
Open up the scene named main and press the Play button.
You’ll see the game has no power-ups yet. As a result, it’s hard to complete and perhaps a little dull. Your task is to add some power-ups and liven things up. When the player collects a power-up, a quote from a famous movie series will appear on screen. See if you can guess the movie series, the answer is at the end of this tutorial!
The Power-Up Lifecycle
A power-up has a lifecycle, which consists of several distinct states:
- The first stage is creation which can happen during gameplay or at design time when you manually place power-up GameObjects into your scene.
- Next comes attract mode, when power-ups can animate or otherwise do something to attract attention.
- The collection stage is the act of picking up the power-up, which can trigger sounds, particle systems or other special effects.
- The collection leads to the issuance of the payload, which is the power-up “doing its thing”. The payload can be anything you can imagine, from a humble health bonus to giving the player some awesome superpowers. The payload step also sets up the expiry check. The power-up may be set to expire after a set amount of time has elapsed, after the player has been hurt, after a certain number of uses, or after some other gameplay condition has occurred.
- The expiry check leads to the expiry stage proper. The expiry stage will destroy the power-up and marks the end of the cycle.
The above lifecycle contains elements you’d want to use repeatedly in every game and elements that would only pertain to the current game. For example, detecting if a player has collected a pickup is a feature you would like in every game, but a specific payload of making the player invisible might be something you only want in your current game. This is important to consider when designing the script logic.
Creating a Simple Star Power-Up
You’re likely familiar with “entry-level” power-ups, such as gold coins, stars or rings that provide a simple score or health bonus. You will now create a Star power-up in the scene that will instantly provide a health bonus to the player and give your hero a better chance of making it out alive.
Relying solely on the star is not going to be good enough to escape unscathed, but you’ve still got other power-ups to add later on in this tutorial that will definitely give your hero a competitive edge.
Create a new Sprite, name it PowerUpStar, and position it just above the player at a location of (X:6, Y:-1.3). To keep the scene tidy, make the sprite a child of the empty PowerUps “folder” GameObject in the scene:
Now define the sprite’s appearance. Set the Transform Scale to (X:0.7, Y:0.7), in the Sprite Renderer component, assign the star to the Sprite slot and set Color to a pale brown color (R:211, G:221, B:200).
Add a Box Collider 2D component, tick the Is Trigger checkbox and set the Size to (X:0.2, Y:0.2):
You’ve just created your first power-up! Play the game to see if everything looks good. The power-up appears, but nothing happens when you try to pick it up. To fix this, you will need to do some scripting.
Separating Game Logic with a Class Hierarchy
Being a conscientious developer, you want to make the best use of your time and reuse elements from previous projects. To do this with a power-up system, you need to design it with a class hierarchy. The class hierarchy separates the power-up logic into a reusable engine part and a game-specific part. If you are new to the idea of class hierarchies and inheritance we’ve got a video that explains all of this.
The diagram above shows the class
PowerUp as the parent class. It contains the game-independent logic, so you can reuse this mostly as-is in other projects. The tutorial project already contains the parent class. The parent class orchestrates the power-up lifecycle, manages the different states a power-up can have and handles collisions, collection, payloads, messages and expiry.
The parent class implements a simple state machine that tracks the power-up lifecycle. What you must implement is a subclass and some inspector values for each new power-up, and you will be good to go!