DEV Community

Cover image for Learn Godot 4 by Making a 2D Platformer — Part 22: Music & SFX
christine
christine

Posted on

Learn Godot 4 by Making a 2D Platformer — Part 22: Music & SFX

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

Music and sound effects can add a new dimension to your game. Imagine playing your favorite video game — now imagine it without sound. What impact would that have on your experience? Would it still be your favorite game? In this part, we’ll be adding the second last piece to our game, which is music and sound effects.

In Godot, you have three primary options for playing audio:

  1. AudioStreamPlayer: This is a general audio player suitable for playing music or any non-positional audio. Use this when you don’t need the audio to have a position in the world. For example, if you have background music or dialog music that should be played irrespective of the position of the characters or objects in the game, use AudioStreamPlayer.

  2. AudioStreamPlayer2D: This is designed for 2D games where you need positional audio. It stimulates the position of the sound in a 2D space and will make sounds quieter the further away they are from the listener (camera, usually). Use this when you are making a 2D game and you want the audio to have a position in your 2D world (e.g., a sound effect that occurs at a certain location on the screen).

  3. AudioStreamPlayer3D: Similar to AudioStreamPlayer2D, but for 3D games. It takes into account the position of the sound in three-dimensional space. Use this in 3D games when you want the sound to emanate from a specific location in the 3D world.

Throughout this tutorial, we will use our AudioStreamPlayer node for constant stable sounds such as our Background music or Dialog music. We will use our AudioStreamPlayer2D node however for our sound effects — because we want some panning in the sound and we also want the sound loudness to depend on the location of the sound.


WHAT YOU WILL LEARN IN THIS PART:

  • How to work with the AudioStreamPlayer & AudioStreamPlayer2D node.

In your project, you’ll see in your Assets directory that I already added a folder with Music in it for us to use. You are welcome to go and source your audio, but for our game, I’m going to be using those assets.

Godot 2D Platformer

STEP 1: MUSIC

Background Music

In your Player scene, add a new AudioStreamPlayer node. This will serve as the audio container for your background music when the game plays.

Godot 2D Platformer

Rename it to “BackgroundMusic”.

Godot 2D Platformer

Since we’ll be adding numerous AudioStreamPlayer nodes, let’s organize them underneath a Node2D node renamed “Music”.

Godot 2D Platformer

Our background music will be the audio file “arguement-loop-27901.mp3”. Godot supports both .wav and .mp3 audio extensions. We can assign this audio file to our BackgroundMusic node in the inspector Panel underneath the Stream property. You can drag this audio file into the property, or you can add it by clicking on it and assigning it via the Quick Load method.

Godot 2D Platformer

Godot 2D Platformer

This audio file is already imported with its looping value enabled, so we don’t need to worry about it playing and then stopping once it reaches the end! If you need to use an audio file that isn’t preconfigured for looping, you can re-import it in your Import dock with looping enabled.

Godot 2D Platformer

To play this BackgroundMusic node, we can reference it in our code wherever we want it to play via the play() method. In our case, we want it to play when the Player enters the scene — which is when our level starts.

    ### Player.gd
    #older code

    func _ready():
        #older code

        #play background music
        $Music/BackgroundMusic.play()
Enter fullscreen mode Exit fullscreen mode

Now if you run your scene and you start a level, the background music should play.

Pause Menu Music

We also want our pause menu to play its track when the player presses the ui_pause input. When the pause button is pressed, we need to stop our background music and play our pause music.

Add a new AudioStreamPlayer node and rename it to “PauseMenuMusic”.

Godot 2D Platformer

Assign the “8bit-music-for-game-68698.mp3” track to its Stream property.

Godot 2D Platformer

In our input() function, underneath our ui_pause conditional, we need to play this audio track.

    ### Player.gd

    #older code

    #singular input captures
    func _input(event):
        #pause game
        if event.is_action_pressed("ui_pause"):
            #show menu
            $PauseMenu.visible = true
            #pause scene
            get_tree().paused = true
            #show cursor
            Input.set_mouse_mode(Input.MOUSE_MODE_VISIBLE) 
            #play pause menu music
            $Music/BackgroundMusic.stop()
            $Music/PauseMenuMusic.play()
Enter fullscreen mode Exit fullscreen mode

We also need to stop the pause menu music from playing when the player resumes the game because we need the background music to play again.

    ### Player.gd

    #older code

    #resume game
    func _on_button_resume_pressed():
        #unpause scene
        get_tree().paused = false
        #hide menu
        $PauseMenu.visible = false
        #stop pause menu music
        $Music/BackgroundMusic.play()
        $Music/PauseMenuMusic.stop()
Enter fullscreen mode Exit fullscreen mode

Now if you run your scene and you pause your game, your pause menu music should play. If you resume your game, your background music should play.

Game Over Music

When our player dies, we need to stop our background music and play our death music. In your Player scene, add a new AudioStreamPlayer node and rename it to “GameOverMusic”.

Godot 2D Platformer

Assign the “kl-peach-game-over-iii-142453.mp3” track to its Stream property.

Godot 2D Platformer

Let’s play it when the player dies. We also need to stop the background music from playing.

    ### 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

        if $AnimatedSprite2D.animation == "death":
            # pause game
            get_tree().paused = true
            # show menu
            $GameOver/Menu.visible = true
            # make modular value visible
            $AnimationPlayer.play("ui_visibility")  
            #hide the player's UI 
            $UI.visible = false
            #get final values
            final_score_time_and_rating()
            # show player values
            $GameOver/Menu/Container/TimeCompleted/Value.text = str(Global.final_time)
            $GameOver/Menu/Container/Score/Value.text = str(Global.final_score)
            $GameOver/Menu/Container/Ranking/Value.text = str(Global.final_rating)
            #show cursor
            Input.set_mouse_mode(Input.MOUSE_MODE_VISIBLE) 
            #play game over music
            $Music/BackgroundMusic.stop()
            $Music/GameOverMusic.play()
Enter fullscreen mode Exit fullscreen mode

We also need to continue the background music when our player presses the restart button on our game over screen.

    ### Player.gd

    #older code

    #restarts game
    func _on_restart_button_pressed():
        #hide menu
        $GameOver/Menu.visible = false
        # Restart current scene
        get_tree().reload_current_scene()
        if Global.get_current_level_number() > 1:
            #unpause scene
            get_tree().paused = false
        #start background music
        $Music/BackgroundMusic.play()
Enter fullscreen mode Exit fullscreen mode

Now if you run your scene, your music should play when our game is over.

*If your music is too loud/soft for your taste, you can edit its Volume property to be lower or higher in the Inspector panel. You can test this by enabling and disabling the Playing property.

Godot 2D Platformer

Main Menu Music

We also need music to play in our MainMenu scene. Create a new Node2D node called “Music” and add an AudioStreamPlayer node to it which you should rename to “BackgroundMusic”.

Godot 2D Platformer

Assign the “8bit-music-for-game-68698.mp3” track to its Stream property.

Godot 2D Platformer

Let’s play it when the MainMenu node enters the scene.

    ### MainMenu.gd

    extends CanvasLayer

    func _ready():
        #pause it by default 
        get_tree().paused = true
        #show cursor
        Input.set_mouse_mode(Input.MOUSE_MODE_VISIBLE) 
        #play background music
        $Music/BackgroundMusic.play()
Enter fullscreen mode Exit fullscreen mode

Now if you run your scene, your background music should play when your MainMenu is open.

Level Up Music

We also need music to play in our LevelDoor scene when the player enters its collision body. Create a new Node2D node called “Music” and add an AudioStreamPlayer node to it which you should rename to “LevelUpMusic”.

Godot 2D Platformer

Assign the “cool pigeon.wav” track to its Stream property. If you don’t like this audio, you can also use the “kl-peach-game-over-iii-142453.mp3” or “computer(turn on).wav” audio.

Godot 2D Platformer

Let’s play it when the LevelDoor node collides with our player body.

    ### LevelDoor.gd

    #older code

    func _on_body_entered(body):
        if body.name == "Player":
            Input.set_mouse_mode(Input.MOUSE_MODE_VISIBLE) 
            # pause game
            get_tree().paused = true
            # show menu
            $UI/Menu.visible = true
            # make modular value visible
            $AnimationPlayer.play("ui_visibility")  
            #hide the player's UI 
            body.get_node("UI").visible = false 
            #get final values
            body.final_score_time_and_rating()
            # show player values
            $UI/Menu/Container/TimeCompleted/Value.text = str(Global.final_time)
            $UI/Menu/Container/Score/Value.text = str(Global.final_score)
            $UI/Menu/Container/Ranking/Value.text = str(Global.final_rating)
            #play music
            $Music/LevelUpMusic.play()
Enter fullscreen mode Exit fullscreen mode

Now if you run your scene, your level-up music should play when your player reaches the door.

STEP 2: SFX

Jump SFX

When our player jumps, we want to play a jumping sound effect. In your Player scene, add a new AudioStreamPlayer2D node and rename it to “JumpSFX”.

Godot 2D Platformer

Assign the “jump.wav” track to its Stream property.

Godot 2D Platformer

In our input() function, underneath our ui_jump conditional, we need to play this audio track.

    ### Player.gd

    #older code

    #singular input captures
    func _input(event):
        #older code
        #on jump
        if event.is_action_pressed("ui_jump") and is_on_floor():
            velocity.y = jump_height
            $AnimatedSprite2D.play("jump")
            #sfx
            $Music/JumpSFX.play()
Enter fullscreen mode Exit fullscreen mode

You can also play this if your player is climbing ladders.

    ### Player.gd

    #older code

    #singular input captures
    func _input(event):
        #older code
        #on climbing ladders
        if Global.is_climbing == true:
            if Input.is_action_pressed("ui_up"):
                $AnimatedSprite2D.play("climb") 
                gravity = 100
                velocity.y = -160
                Global.is_jumping = true
                $Music/JumpSFX.play()
Enter fullscreen mode Exit fullscreen mode

Now if you run your scene, your sfx should play when your player jumps or climbs.

Damage SFX

When our player gets damaged, we want to play a damaged sound effect. In your Player scene, add a new AudioStreamPlayer2D node and rename it to “DamageSFX”.

Godot 2D Platformer

Assign the “embarrassed.wav” track to its Stream property.

Godot 2D Platformer

In our take_damage() function, we need to play this audio track.

    ### Player.gd
    #older code

    # takes damage & death
    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)
            $Music/DamageSFX.play()
        #death
        if lives <= 0:
            $AnimatedSprite2D.play("death")
Enter fullscreen mode Exit fullscreen mode

Now if you run your scene, your sfx should play when your player gets hurt.

Pickup SFX

When our player picks up any one of our pickups, we want to play a different sound. In your Player scene, add three new AudioStreamPlayer2D nodes and rename them to “ScoreSFX”, “BoostSFX”, and “HealthSFX”.

Godot 2D Platformer

Assign the “score.wav” track to the ScoreSFX’s Stream property.

Godot 2D Platformer

Assign the “boost.wav” track to the BoostSFX’s Stream property.

Godot 2D Platformer

Assign the “upgrade.wav” track to the HealthSFX’s Stream property.

Godot 2D Platformer

In our add_pickup() function, we need to play these audio tracks.

    ### 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)
            $Music/HealthSFX.play()

        #temporarily allows us to destroy boxes/bombs
        if pickup == Global.Pickups.ATTACK:
            Global.is_attacking = true
            $Music/BoostSFX.play()

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

Now if you run your scene, your sfx should play when your player runs through the pickups.

Attack SFX

When our player presses their ui_attack input, we want an attack SFX to play. In your Player scene, add a new AudioStreamPlayer2D node and rename it to “AttackSFX”.

Godot 2D Platformer

Assign the “attack variation.wav” track to its Stream property.

Godot 2D Platformer

In our input() function, underneath our ui_attack conditional, we need to play this audio track.

    ### Player.gd

    #older code

    #singular input captures
    func _input(event):
        #pause game
        if event.is_action_pressed("ui_pause"):
            #show menu
            $PauseMenu.visible = true
            #pause scene
            get_tree().paused = true
            #show cursor
            Input.set_mouse_mode(Input.MOUSE_MODE_VISIBLE) 
            #play pause menu music
            $Music/BackgroundMusic.stop()
            $Music/PauseMenuMusic.play()

        #on attack
        if Input.is_action_just_pressed("ui_attack"):
            if Global.is_attacking == true:
                $AnimatedSprite2D.play("attack")
                $Music/AttackSFX.play()
Enter fullscreen mode Exit fullscreen mode

Now if you run your scene, your sfx should play when your player attacks when their attack boost is active.

IncreaseScore SFX

When our increases their score, we want the game to be notified via a soundtrack. In your Player scene, add a new AudioStreamPlayer2D node and rename it to “IncreaseScoreSFX”.

Godot 2D Platformer

Assign the “Pickup Coin.wav” track to its Stream property.

Godot 2D Platformer

In our increase_score() function, we need to play this audio track.

    ### Player.gd

    #older code

    func increase_score(score_count):
        score += score_count
        update_score.emit(score)
        $Music/IncreaseScoreSFX.play()
Enter fullscreen mode Exit fullscreen mode

Now if you run your scene, your sfx should play when your player ups their score.

Your code should look like this.

Godot 2D Platformer

I challenge you to go ahead and add more SFX if you’d like. Maybe play a sound if the player presses on a button, or when the Box and Bomb hits the Wall collisions. I’m going to stop here for now, since this is a good base. In the next part, we’ll be adding our particle effects to our game when our game. 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)