DEV Community

Cover image for Learn Godot 4 by Making a 2D Platformer — Part 15: Lives, Score, & Attack Boosts #3
christine
christine

Posted on

Learn Godot 4 by Making a 2D Platformer — Part 15: Lives, Score, & Attack Boosts #3

*You can find the links to the previous parts at the bottom of this tutorial.

In Donkey Kong, you increase your score points by jumping, dodging, or smashing things, and then adding those points to whatever is left on the bonus timer upon clearing the board.

For our final Pickup item, we will boost our player’s score by 1000 points. We’ll also up our score if they jump over a box or a bomb, but if they get damaged, we’ll decrease their score by 10 points.


WHAT YOU WILL LEARN IN THIS PART:

  • Further practice with RayCast2D nodes.

STEP 1: INCREASING OUR SCORE

The score boost that the player gets when they jump over a box or a bomb will depend on how long they are jumping over it. We’ll be using another RayCast2D node to check if the player is jumping over the box or the bomb. The RayCast will continuously hit our nodes below, so each time it hits our box or bomb it will increase the score.

Figure 10: Demonstration of our score if our Player jumps over a collision.

Let’s add a new RayCast2D node to our Player scene. Call it “ScoreRayCast”.

Godot 2D Platformer

In the inspector panel, enable the ScoreRayCast’s collider to also collide with areas.

Godot 2D Platformer

In our Player script, let’s create a new variable that will capture our Player’s score. We also need to create a new signal that will update our score value in the UI later on.

    ### Player.gd

    #older code

    #custom signals
    signal update_lives(lives, max_lives)
    signal update_attack_boost(attack_time_left)
    signal update_score(score)

    #score stats
    var score = 0
Enter fullscreen mode Exit fullscreen mode

We should also create a new variable that will capture if our player got hurt or not because we don’t want the raycast to increase the score if the player got hurt whilst jumping over the box or bomb.

    ### Player.gd

    #older code

    #health stats
    var max_lives = 3
    var lives = 3
    var is_hurt = false
Enter fullscreen mode Exit fullscreen mode

We will set this value to true if our player gets damaged in the take_damage() function. In our take_damage() function we will also decrease our score by 10.

    ### Player.gd

    #older code

    # takes damage
    func take_damage():
        #deduct and update lives    
        if lives > 0 and Global.can_hurt == true:
            lives = lives - 1
            update_lives.emit(lives, max_lives)
            #play damage animation
            $AnimatedSprite2D.play("damage")
            #allows animation to play
            set_physics_process(false)
            is_hurt = true
            #decrease score
            if score >= 10:
                score -= 10
                update_score.emit(score)
Enter fullscreen mode Exit fullscreen mode

Then, we’ll reset our is_hurt variable back to false after our jump animation has been completed.

    ### Player.gd

    #older code

    #reset our animation variables
    func _on_animated_sprite_2d_animation_finished():
        if attack_time_left <= 0:
            Global.is_attacking = false
        set_physics_process(true)
        is_hurt = false
Enter fullscreen mode Exit fullscreen mode

We will calculate our score boost in our physics_process() function. If the player’s is_hurt variable is false (meaning they didn’t get hurt by the box or bomb), and their ScoreRayCast is hitting the box or the bomb, we’ll increase their score by 1 each time it hits the collision whilst jumping over it.

    ### Player.gd
    #older code

    #movement and physics
    func _physics_process(delta):
        # older code

        #score boost
        if Input.is_action_pressed("ui_jump"):
            # Get the colliders of our raycast
            var target = $ScoreRayCast.get_collider()

            # Check if target is valid and player is not damaged
            if target != null:
                if target.name == "Box" or target.name == "Bomb":
                    if is_hurt == false:
                        #increase score
                        score += 1
                        update_score.emit(score)
Enter fullscreen mode Exit fullscreen mode

Finally, we’ll increase our score when our Player picks up our Score Boost pickup.

    ### Player.gd

    #older code

    #adds pickups to our player and updates our lives/attack boosts
    func add_pickup(pickup):
        #increases life count if we don't have 3 lives already
        if pickup == Global.Pickups.HEALTH:
            if lives < max_lives:
                lives += 1
                update_lives.emit(lives, max_lives)

        #temporarily allows us to destroy boxes/bombs
        if pickup == Global.Pickups.ATTACK:
            Global.is_attacking = true

        #increases our player's score
        if pickup == Global.Pickups.SCORE:
            score += 1000
            update_score.emit(score)
Enter fullscreen mode Exit fullscreen mode

If we print our score and we run our scene, we should see our score update when we jump over collisions, as well as when we pick up a score boost.

Godot 2D Platformer

I also want us to increase our score when we are attacking collisions. Each time we hit the boxes or the bombs while our attack boost is enabled, I want our score to increase by 10. To change our score and emit our signal each time we want to increase or decrease our score is a bit tedious, so let’s create two custom functions to handle our score increasing and decreasing. Then, wherever we increase/decrease our score we can simply call the function and pass in the value that we want to add or subtract as a parameter.

    ### Player.gd

    #older code

    #increases our score
    func increase_score(score_count):
        score += score_count
        update_score.emit(score)

    #decreases our score
    func decrease_score(score_count):
        if score >= score_count:
            score -= score_count
            update_score.emit(score)
Enter fullscreen mode Exit fullscreen mode

Now, let’s increase our score when we hit our box or bomb. We’ll also swap out our code to call our function instead when our player jumps over the collisions. Our score will increase indefinitely by 10 if we hit our Boxes, but because our bomb is still moving along our path when we move it, our score will increase for however long our AttackRayCast is hitting it. This is not an issue though, because the score should increase more if we hit a bomb (it’s not a bug, it’s a feature!).

    ### Player.gd

    #older code

    #movement and physics
    func _physics_process(delta):
        #older code

        #countdown for attack boost
        if Global.is_attacking == true:
            attack_time_left = max(0, attack_time_left - 1)
            update_attack_boost.emit(attack_time_left)

            if Input.is_action_pressed("ui_attack"):
                #gets the colliders of our raycast
                var target = $AttackRayCast.get_collider()
                #is target valid
                if target != null:
                    #remove box
                    if target.name == "Box":
                        Global.disable_spawning()
                        target.queue_free()
                        increase_score(10)
                    #remove bomb
                    if target.name == "Bomb":               
                        Global.is_bomb_moving = false   
                        increase_score(10)                  
                Global.can_hurt = false
            else:
                Global.can_hurt = true

        #score boost
        if Input.is_action_pressed("ui_jump"):
            # Get the colliders of our raycast
            var target = $ScoreRayCast.get_collider()

            # Check if target is valid and player is not damaged
            if target != null:
                if target.name == "Box" or target.name == "Bomb":
                    if is_hurt == false:
                        increase_score(1)
Enter fullscreen mode Exit fullscreen mode

Then, let’s update our code in our take_damage() function:

    ### Player.gd

    #older code

    # takes damage
    func take_damage():
        #deduct and update lives    
        if lives > 0 and Global.can_hurt == true:
            lives = lives - 1
            update_lives.emit(lives, max_lives)
            #play damage animation
            $AnimatedSprite2D.play("damage")
            #allows animation to play
            set_physics_process(false)
            is_hurt = true
            #decrease score
            decrease_score(10)
Enter fullscreen mode Exit fullscreen mode

And in our add_pickups() function:

    ### Player.gd

    #older code

    #adds pickups to our player and updates our lives/attack boosts
    func add_pickup(pickup):
        #increases life count if we don't have 3 lives already
        if pickup == Global.Pickups.HEALTH:
            if lives < max_lives:
                lives += 1
                update_lives.emit(lives, max_lives)

        #temporarily allows us to destroy boxes/bombs
        if pickup == Global.Pickups.ATTACK:
            Global.is_attacking = true

        #increases our player's score
        if pickup == Global.Pickups.SCORE:
            increase_score(1000)
Enter fullscreen mode Exit fullscreen mode

Now if we run our scene our score should update if we hit our boxes and bombs.

STEP 2: SCORE UI

Just like with our Attack and Health pickups, we need to display the value of our score on our screen. Copy and paste your Attack UI element and rename it to “Score”.

Godot 2D Platformer

Change your Sprite2D texture to “res://Assets/star/star/sprite_04.png”. Then, change its position to be (x: 25, y: 17).

Godot 2D Platformer

Change your Score ColorRect node’s size to (x: 200. y: 40) and its position to (x: 260, y; 20).

Godot 2D Platformer

We also want to change the size of our Label node since our score will take up a lot of space. Change its size to be (x: 150, y: 22). Also re-anchor it to be centered-right.

Godot 2D Platformer

It should look like this:

Godot 2D Platformer

Attach a new script to your Score node and save it underneath your GUI folder.

Godot 2D Platformer

Just like we did in the two previous parts, we’ll create a function that will be connected to our update_score signal to display the score value in real time.

    ### Score.gd

    extends ColorRect

    #ref to our label node
    @onready var label = $Label

    # updates label text when signal is emitted
    func update_score(score):
        label.text = str(score)
Enter fullscreen mode Exit fullscreen mode
    ### Player.gd

    #older code

    func _ready():
        current_direction = -1
        #set our attack timer to be the value of our wait_time
        attack_time_left =  $AttackBoostTimer.wait_time

        #updates our UI labels when signals are emitted
        update_lives.connect($UI/Health.update_lives)
        update_attack_boost.connect($UI/Attack.update_attack_boost)
        update_score.connect($UI/Score.update_score)

        #show our correct lives value on load
        $UI/Health/Label.text = str(lives)
Enter fullscreen mode Exit fullscreen mode

Your code should look like this.

Now if you run your scene your score should update if you run through score boosts, hit collisions, or jump over collisions!

Godot 2D Platformer

Congratulations on setting up the base for your Score Boost System. In the next part, we’ll add our Level Progression mechanics which will give our player a rating and feedback once they complete the level, and it will allow them to move on to the next level or retry the current level.

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!

Godot 2D Platformer


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!

Godot 2D Platformer

This book will be updated continuously to fix newly discovered bugs, or to fix compatibility issues with newer versions of Godot 4.

Top comments (0)