All lessons
Lesson 17

Background Music

Loop a song underneath the whole game, and pause it right where it is instead of starting over.

Loading the game engine. This takes a moment the first time.

The scenes

UI node · 2D node · % unique name · .tscn instanced scene

background_music.tscn
  • BackgroundMusic Control background_music.gd
  • VBoxContainer
  • TitleLabel Label
  • StatusLabel Label %
  • PlayButton Button %
  • PauseButton Button %
  • StopButton Button %
  • VolumeLabel Label
  • VolumeSlider HSlider %
  • HomeButton Button %
  • MusicPlayer AudioStreamPlayer %
extends Control

## The music track, loaded from a file in this lesson's folder.
var music_stream: AudioStream = preload("res://lessons/background_music/ambience.mp3")

# "%MusicPlayer" means "the node named MusicPlayer in this scene."
@onready var music_player: AudioStreamPlayer = %MusicPlayer

func _ready() -> void:
	# Hand the loaded song to the player node so it has something to play.
	music_player.stream = music_stream
	# Each line below names the function to run when that button is clicked.
	%PlayButton.pressed.connect(_on_play_pressed)
	%PauseButton.pressed.connect(_on_pause_pressed)
	%StopButton.pressed.connect(_on_stop_pressed)
	# "value_changed" is a signal the slider sends every time you drag it.
	%VolumeSlider.value_changed.connect(_on_volume_changed)
	%HomeButton.pressed.connect(_on_home_pressed)

func _on_play_pressed() -> void:
	# Unpause first, in case the song was paused instead of stopped.
	music_player.stream_paused = false
	# Only call "play()" if nothing is playing. Calling it while the song is
	# already going would jump it back to the beginning.
	if not music_player.playing:
		music_player.play()
	%StatusLabel.text = "Status: Playing"

func _on_pause_pressed() -> void:
	# "not" flips true to false and false to true, so this one button both
	# pauses and unpauses.
	music_player.stream_paused = not music_player.stream_paused
	if music_player.stream_paused:
		%StatusLabel.text = "Status: Paused"
	else:
		%StatusLabel.text = "Status: Playing"

func _on_stop_pressed() -> void:
	# "stop()" forgets where the song was. Pressing play starts it over.
	music_player.stop()
	%StatusLabel.text = "Status: Stopped"

# "value" is the slider's number, from 0 to 100.
func _on_volume_changed(value: float) -> void:
	# Ears do not hear loudness evenly, so Godot measures volume in decibels.
	# "linear_to_db()" turns the slider's plain number into decibels for us.
	music_player.volume_db = linear_to_db(value / 100.0)

func _on_home_pressed() -> void:
	# Stop the music first, or it keeps playing on the lesson list screen.
	music_player.stop()
	get_tree().change_scene_to_file("res://shared/ui/lesson_select.tscn")