Do Something Captain! We’re Taking Damage!
Objective: Implement a Health collectible system in your 2D Space Shooter style Unity Game Project.
As I worked through the Space Shooter style Unity 2D Game Project from GameDevHQ, the section in the course that dealt with Player damage had always seemed to me sort of weak. In the course, we are taught how to activate game objects upon collision detection that depict damage to the ship. In this case, a fire with a streaming smoke plume. Upon the first collision detection (with an Enemy ship or enemy laser), one engine would catch fire. The next collision would light up the second engine. I think every collision would cost you a life, so the third collision would destroy your ship and you would be floating debris in space…
The next time you played, it was always the same sequence. I think this is what I didn’t like: the predictions of events.
What I envisioned was a world where damage is random, and where a spaceship can repair damage when given the proper tools (think Health Power-Up).
So, let me take you along on my quest to do just that. Let’s go!
I began by adding a few new variables to my PlayerScript script. Four private Game Objects to store damage animations (_playerdamage0x). I also needed two public Lists of Game Objects to store the “damage” animations and keep track of the “activated” ones.
I then created four empty Game Objects, renamed them PlayerDamage01 thru PlayerDamage04, and set these as children of the Player ship. I used the provided Fire Sprites and damage animation, added them as components to each of the four children, and adjusted the Transform for each child to give me “damage” in four different areas of the ship with varying severity
These four children are then turned on/off as required by changing their respective SetActive to True or False.
With the Player selected in the Hierarchy, I then set the Size of the Pool Damage Animations List in the Inspector to 4 and dragged each of these children into the Field Elements.
What this List does is simple: it is where my Game Objects representing damage are stored. Via script, I will show you how I randomly pick one of these Elements, turn it on (SetActive = True), remove it from the List (Pool), and keep track of which Element has been turned on by storing them into the Activated Damage Animations List.
This is what happens: when we detect a collision, the Damage() method is called. Inside that method, we count how many elements are in the poolDamageAnimations List. If the count is greater than zero, we generate a random number between 0 and the total count, and store this value in rdmDamage. This will become our index number for the List. We then store into temp the Game Object in the List at index number rdmDamage.
We then add this Game Object stored in temp into the activatedDamageAnimations list, turn it on by flipping the SetActive to True, and the final step is to remove this same Game Object from the pool of damage animations.
In my version of the Game Project, I have 4 “damage” animations (4 children) inside the List. Every time I take damage, one of these animations will be turned on. Whichever one is picked is done so randomly, and that same animation is then removed and stored in a separate List that keeps track of the activated animations. So this means, as my game is currently set up, I can take four “hits” before the poolDamageAnimations is “empty”.
If the poolDamageAnimations is equal to 0 (empty), only then will I lose one of my lives. The UI gets updated showing one less “life” ship icon. We then instantiate a big explosion where the player’s ship was, and then we check to see if there are any lives remaining. If yes, we reset the lists (poolDamageAnimations and activatedDamageAnimations) by calling the ResetDamageAnimationsList() method, reset the player’s position (think respawn) by executing the ResetPlayerPosition() coroutine, and continue on with the game.
We respawn the Player’s position back to the dead center of the Game Scene, instruct the SpawnManager script to execute the OnPlayerReady() and StartSpawning() methods (these are needed to sort of clean things up a bit and reset certain default settings in the game, but won’t be discussed here because they’re outside of the scope of this article), and we’re good to go.
If on the other hand there are no remaining lives, then we stop spawning game objects in the SpawnManager, instantiate a big explosion where the Player met its fate, and finally destroy the Player’s game object. Game. Over. Man!…
The ResetDamageAnimationsList() method basically does the reverse. After telling the SpawnManager to run the OnPlayerReset() method, we execute a While Loop. This While Loop counts how many elements are stored into the activatedDamageAnimations list. As long as the count is greater than zero, it randomly selects an index position based on the count, stores the Game Object at that index position into temp, adds it back to the poolDamageAnimations list, turns it off (SetActive = False), and finally removes that same Game Object from the activatedDamageAnimations list.
We now have four different damage animations, are able to turn them on in a random sequence, and reset the Lists when the Player loses a life. What about reversing some of the damage? Time to create a Health power-Up!
I started out by creating this little blinking health collectible and stored it as a Prefab in my Game Assets.
I adjusted the size of my Player Power Ups list in my Spawn Manager via the Inspector, and dragged my PlayerHealthpowerUp Prefab into Element 3.
I then went into my PowerUps script, added a comment to keep track of its ID number (in this case, Health Power-Up is ID 3).
Then adjusted the switch statement in the OnTriggerEnter2D() method to include case 3: which is the _powerUpID of the Health Power-Up. This in turn will execute the HealthBoostActivate() method in the PlayerScript script.
Let’s not forget, in the SpawnManager script, to adjust the range of the random selection (int randomPowerUp) to ensure it captures the Health Power-Up ID.
Back to the PlayerScript script, when the HealthBoostActivate() is called, it randomly picks one of the activated damage animations using the same logic discussed above, turns it off, then removes it from the active list and sends it back into the pool of available damage animations. I also play a custom Audio Clip which tells the gamer that his ship is getting repaired.
The result is that when a Health Power-Up is collected by the Player, it randomly reverses (repairs) one of the activated damaged areas. A demonstration of this is depicted below:
The beauty of this system is that you could have a dozen or so “damage” animations in your pool, adjust your code to set your threshold on how many “hits” the Player can take before he loses a life, and you could potentially have little repetition in the sequence of damage accumulation during gameplay.
As always, I hope this article will provide you with some inspiration (and maybe even a little guidance) as you work on your own Unity 2D Game Project. Thanks for reading :)