In this part, we will add health and damage to our player.
Unfortunately, we won't get to test it completely until we add enemies in the next part, but feel free to revisit this section after having completed the next.
1. Add a health variable
- Open the Level 1 scene
- Select the Player object
- Add a new integer object variable called Healthwith a default of 3
- Apply changes to the prefab so other player instances are affected
2. Display the health in the HUD
The last thing we need to do is empty the heart sprites in the HUD when the player loses health.
- Open the HUD scene
- Select the Row object under HUD
- Add a new flow machine component
- Set its source to Embed
Setting this machine's source to Embed will allow us to refer to objects in the same scene directly, without having to use variables. This is convenient to get a reference to all our heart images. Be aware though that we are only able to do this because we are only using this graph once, on this object, which isn't a prefab.
In the graph, we will iterate over each of the heart objects in the HUD. If the health of the player is greater than the index of the heart, it should display as full. Otherwise, it should display as empty.
- You can easily find the List of Game Object literal by typing List of Game Object in the fuzzy finder.
Otherwise, look under Codebase > System > Collections > Generic.
After creating the literal unit, add three items, and select each heart object in order in the fields.
- For Each Loop is located under Control. For more information on loops, have a look at this manual page:
Manual: Control Units Reference
- The HudHeartFull and HudHeartEmpty sprites are located under Sprites / Hud
Let's recap what's happening here.
- For each heart object, we check its index. The first will have an index of 0, the second 1, the third 2.
Note that in scripting, indices are always zero-based, which is why they're 0-1-2 here, not 1-2-3.
- We get the player health with our singleton scene variable.
- If the health of the player is greater than the index of the heart (e.g. 2 HP > index #1, i.e. the second heart), we enable the full sprite.
Otherwise, we switch to the empty heart sprite.
If you test your game now, you should be able to play with the health variable of the player from the editor and see the sprites get updated in real time:
2. Create the player health state machine
From this point on, everything we do is in preparation of the next part: enemies. We won't get to test it until then, so pay close attention!
We will use a state machine instead of a flow machine for our player's health, because there will be two states:
- The player is Vulnerable to damage (default state)
- After taking a hit, the player will be briefly Invulnerable to damage
Take a moment to read the introduction to states and transitions in the manual (it takes about 5 minutes):
Now let's practice by creating our first state machine!
- Select the player object
- Add a new State Machine component (located under Bolt as well)
- Create a new macro for it called PlayerHealth
- Apply the changes to the prefab
In the new state graph, there should be a single default Start state:
We will transform that state into our Vulnerable state. Select the node to show its inspector in the Graph Inspector. Here, you can rename Start to Vulnerable, and add a description if you want:
You'll see our state is now renamed:
For now, we'll focus on this state which will do most of the damage handling, and then we'll add the invulnerable state later. Double-click the node to get inside the Vulnerable state.
3. Implement the damage mechanic
While all flow states start with On Enter State, Update and On Exit State, you don't have to use these events. In fact, for our vulnerable state, we will use none of them, so you can go ahead and delete them.
Instead, we will use a custom event to handle the damage, like we did earlier for spikes and death. This event will be called Damage and take 1 integer argument, the amount of damage inflicted to the player. Add a custom event in the vulnerable graph, then set the Arguments field to 1 and the name field to Damage:
When we receive it, all we have to do is subtract this argument from the health variable, while making sure the health doesn't go below zero:
After updating the health variable, we'll check if it reaches zero, meaning the player should die.
- If it dies, it will send itself the Death event that we implemented earlier for spikes. This will reload the current level like before.
- If it doesn't die, we'll trigger a Hurt animation in the animator. We will also trigger a Hurt custom event, which we will use to transition to our invulnerable state in the next step.
4. Make a temporary invulnerable state
Back in the root state graph, create a new flow state and call it Invulnerable. Then, create two transitions, back and forth between our two states:
The first thing you'll notice is that our transitions are yellow and that the invulnerable is dimmed out. This is because we haven't told Bolt when to transition from one state to the other.
Open the transition going from Vulnerable to Invulnerable by double-clicking its node. The only node in the graph will be the state transition trigger:
It is dimmed out because it's never entered. However, we want to transition from vulnerable to invulnerable as soon as we receive the Hurt event that is sent in our vulnerable state. Therefore, all we have to do is add this custom event, and connect it with the trigger:
We can also rename this transition like we renamed our states, with the graph inspector. When no node is selected in a graph, you're inspecting the graph itself, hence why you can change its title and summary:
Going back to the parent state graph, we now see that our invulnerable state is properly named and entered:
Before exiting the invulnerable state, we want to wait for a small delay. Open the transition from invulnerable to vulnerable and connect it to a On Timer Elapsed event (under Events > Time) with a delay of 1:
The parent state graph should now look like this:
Finally, we have to implement invulnerability! We use a simple Unity trick for this: when the player becomes invulnerable, we change its layer from Player to PlayerInvincible. Because this layer is set to not collide with the enemies in the collision matrix, later on, it will keep the player from being hit by enemies. This is why we changed our collision matrix in the project setup part:
When we enter the invulnerable state, the player's layer should switch to PlayerInvincible. When we exit the invulnerable state, it should change back to Player. For this, we'll use the On Enter State and On Exit State events (but you can delete the Update event):
- Set Layer is located under Codebase > Unity Engine > Game Object
- Name to Layer is located under Codebase > Unity Engine > Layer Mask
That's it for invulnerability!
Note that playing with the health value in the inspector right now will not trigger the Hurt or Death events in our player health machine, because we're directly changing the value instead of using our Damage event.
In the next part, we will create enemies that call Damage on collision with the player, enabling us to fully test our health mechanic.
Customer support service by UserEcho