7. Health & Damage

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

  1. Open the Level 1 scene
  2. Select the Player object
  3. Add a new integer object variable called Healthwith a default of 3
  4. 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.

  1. Open the HUD scene
  2. Select the Row object under HUD
  3. Add a new flow machine component 
  4. 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.

  1. 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.
  2. We get the player health with our singleton scene variable.
  3. 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):

Manual: States

Manual: Transitions

Now let's practice by creating our first state machine!

  1. Select the player object
  2. Add a new State Machine component (located under Bolt as well)
  3. Create a new macro for it called PlayerHealth
  4. 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.

This article was helpful for 20 people. Is this article helpful for you?

+1

I think in the end of the section where it says "...health value in the inspector right now will trigger the Hurt or Death events in our player health machine...." it should actually be " ...health value in the inspector right now won't trigger the Hurt or Death events in our player health machine...." (?)

+1

Good catch, fixed! Thanks.

+4

Sorry List of Game Objects is no where to be seen as a search or under  Codebase > System > Collections > Generic

I only see under Systems Boolean, Float, Integer, Object and String. No Collections. Latest Bolt as of 4th Jan 2018

+1

I got the same problem as you.  Can't see the List of Game Objects.

Yep, can't find it as well. Stuck at this step.

This was fixed in v.1.3.

However, if you are targetting an AOT platform, it will still not appear, because generic lists are not AOT-safe.

One possible workaround is to use the Create List unit for this:

I am lost at Step 4. Where is the Root State

+6
Hopefully this helps. Collections>List. Create List worked for me. I think it changed a bit with the updates.

Did you manage to get this to work with the manual change of the health integer value. I followed you use of the game objects for the hearts but cant get the manual changing of the integer to affect the HUD.

Why the Index of my list is 3 instead of 2.

I got it, i should use body output instead of Exit.

What If we wanted to do this with a slider instead?

+1

This is unrelated to the tutorial, but this general approach would work:


+1

Hey there! I'm using Unity 2018.2 and I don't have the On Timer Elapsed event at all :(


I tried starting a timer in the Invulnerable state to then trigger a custom event to work around it, but that didn't work, since WaitForSeconds should be in a coroutine.

+13

If anyone else is having issues with that (since the guide wasn't updated yet at the time of posting.

Here's the solution to the issue (On Timer Elapsed is removed in 1.4):

  • Add "On Enter State" event
  • Mark it as a coroutine (click on it, and check the checkbox in the graph inspector)
  • Add "Wait For Seconds" timer
  • Connect timer output to "Trigger Transition"

Final Graph:


Thanks I was looking for this.