*You can find the links to the previous parts at the bottom of this tutorial.
Now that we have our enemies set up, we need to give our player some health stats so that if a bomb or a box hits us it deducts a life. Our player will start with 3 lives, and later on, we’ll create a death and game-over feature that ends the game if we’ve reached 0 lives. We’ll also create life pickups which will restore our life values in the next part.
WHAT YOU WILL LEARN IN THIS PART:
- How to create custom signals.
- How to call functions via the body method.
Having lives will encourage our player to dodge the boxes and jump over the bombs — because they are put in place to slow the player down and make the game a bit more difficult.
In our player script, let’s create a few variables that will keep track of and update our player’s health stats. Our lives variable defines our player’s current lives, and our max_lives define the max amount of lives our player has.
### Player.gd
#health stats
var max_lives = 3
var lives = 3
We can use custom signals to update our health values. We’ve worked with built-in signals before, but never have we created our custom signals. Signals are messages that nodes emit when something specific happens to them, like a button being pressed. We can also have node-independent signals, called custom signals. We’ll use this in situations such as when we want to show a game over screen when the player’s health reaches zero. To do so, you could define a signal named “died” or “health_depleted” when their health reaches 0.
At the top of your Player script, create a new signal called update_lives. A signal can optionally declare one or more parameters. The signal parameter shows up in the editor’s node dock, and Godot can use them to generate callback functions for you. However, you can still emit any number of parameters when you emit signals. For us we want our signal to declare two parameters: lives, and max_lives.
### Player.gd
#custom signals
signal update_lives(lives, max_lives)
# health stats
var max_lives = 3
var lives = 3
Our signal can now be used to inform other parts of the program or objects that the number of lives has changed, allowing our variables to update their respective states or behaviors accordingly.
Now, let’s create a new function that we will call in our Bomb and Box scripts when the box or bomb collides with our Player. This function will allow our player to take damage, and if they are taking damage we will deduct a life from them, update our lives continously via the signal, and play the damage animation.
### Player.gd
#older code
# takes damage
func take_damage():
#deduct and update lives
if lives > 0:
lives = lives - 1
update_lives.emit(lives, max_lives)
print(lives)
#play damage animation
$AnimatedSprite2D.play("damage")
Currently, our damage animation will not play. This is because we are processing our animations in our physics_process() function, which takes precedence over our take_damage() function since its processes each at each frame. To allow our damage animation to play, we need to temporarily disable our physics processing. We can do this via the set_physics_process() method. This method enables or disables physics (i.e. fixed framerate) processing.
### Player.gd
#older code
# takes damage
func take_damage():
#deduct and update lives
if lives > 0:
lives = lives - 1
update_lives.emit(lives, max_lives)
print(lives)
#play damage animation
$AnimatedSprite2D.play("damage")
#allows animation to play
set_physics_process(false)
This will now allow our animation to play, but our animations defined in our physics_process() function will not play because we haven’t enabled our physics processing. We can set the physics processing value back to true after our animation plays in our _on_animated_sprite_2d_animation_finished() function.
### Player.gd
#older code
#reset our animation variables
func _on_animated_sprite_2d_animation_finished():
Global.is_attacking = false
set_physics_process(true)
We also need to call our take_damage() function in our Bomb and Box scripts, so that our player can be dealt damage when they are hit!
### Bomb.gd
#older code
func _on_body_entered(body):
#if the bomb collides with the player, play the explosion animation and start the timer
if body.name == "Player":
$AnimatedSprite2D.play("explode")
$Timer.start()
Global.is_bomb_moving = false
#deal damage
body.take_damage()
### Box.gd
#older code
func _on_body_entered(body):
# If the bomb collides with the player, play the explosion animation and disable spawning
if body.name == "Player":
$AnimatedSprite2D.play("explode")
# Disable spawning in BoxSpawner
Global.disable_spawning()
#deal damage
body.take_damage()
Your code should now look like this.
Now if you run your scene and you collide with your boxes/bombs, your lives should deduct, and your animations should play accordingly.
Congratulations on giving your player the ability to be damaged. Right now we can’t see our health value change visually, and there is no consequence to us losing lives, but we will add the functionality to that in the next parts to come. In the next part, we will add some pick up items that will restore our lives, as well as give us the ability to destroy boxes and bombs for a limited time. We’ll also be adding our score system.
Now would be a good time to save your project and make a backup of your project so that you can revert to this part if any game-breaking errors occur. Go back and revise what you’ve learned before you continue with the series, and once you’re ready, I’ll see you in the next part!
Next Part to the Tutorial Series
The tutorial series has 24 chapters. I’ll be posting all of the chapters in sectional daily parts over the next couple of weeks. You can find the updated list of the tutorial links for all 24 parts of this series on my GitBook. If you don’t see a link added to a part yet, then that means that it hasn’t been posted yet. Also, if there are any future updates to the series, my GitBook would be the place where you can keep up-to-date with everything!
Support the Series & Gain Early Access!
If you like this series and would like to support me, you could donate any amount to my KoFi shop or you could purchase the offline PDF that has the entire series in one on-the-go booklet!
The booklet gives you lifelong access to the full, offline version of the “Learn Godot 4 by Making a 2D Platformer” PDF booklet. This is a 451-page document that contains all the tutorials of this series in a sequenced format, plus you get dedicated help from me if you ever get stuck or need advice. This means you don’t have to wait for me to release the next part of the tutorial series on Dev.to or Medium. You can just move on and continue the tutorial at your own pace — anytime and anywhere!
This book will be updated continuously to fix newly discovered bugs, or to fix compatibility issues with newer versions of Godot 4.