Table of Contents
In this post we’re going to continue our series on Godot Fundamentals. We’ll cover adding Audio to our Game to improve the overall feel. We’ll look at The Audio Bus Layout, Adding Audio via Audio Stream Players, and controlling our custom Bus volumes from our UI.
You can watch the video covering this topic, or go at your own pace following this post.
Open the Audio panel at the bottom of our Editor to look at our AudioServer overview containg our default Master Audio Bus. For our game, we'll be adding a Music and SFX Audio Bus. Click Add Bus and call it Music.
Notice at the bottom of the new Bus, it defaults to tracking back to the Master Bus, this let's us control Bus grouping and will cascade based on this setting.
Next create another Bus called SFX. You'll also note that each Bus has it's own volume slider for controlling the db for that Bus. You can also add effects to a Bus to modify all sounds on that Bus.
Next let's organize our Audio Assets. You can download the assets from our Github project , or create your own by going to a tool like Chiptone or jsfxr . You can also check out free music on the Free Music Archive as well.
We'll create a new folder in our
assets folder called
audio and inside that folder let's create a
sfx folder and a
music folder. Copy your assets into the proper folders and ensure they are playing correctly inside Godot. For your BG music, you may want to enable looping. Double click to re-import the music file and check the box to enable looping and then re-import. Now the track will continue to repeat itself once it completes.
We can add our background music to our game by opening our Game scene and adding a child node of type
AudioStreamPlayer. Drag over the music file from your assets and select the Music Bus in the inspector. If you have a very loud or quiet music track, you may also adjust the volume dB in the inspector. We'll be controlling our audio via the UI later on. For our Background music we also want it to Autoplay, so enable that.
Open our Weapon scene and add a child node of type
AudioStreamPlayer and in the inspector drag over the sfx file for firing our gun, select the SFX Bus, and adjust the volume to your needs. Open the Weapon's script
scenes/tank/weapon/weapon.gd and CTRL + Drag a reference of the Node into our script and rename it to audio_player. Inside our fire function we'll add a call to the audio_player's play function.
class_name Weapon ... @onready var audio_player = $AudioStreamPlayer ... func fire(): ... bullet.global_position = global_position audio_player.play()
Next open the Crate scene and add the child node of type
AudioStreamPlayer and repeat the process to setup the inspector to your needs, ensuring it's on the SFX Bus and has the audio file defined. Open the Crate's script
scenes/world/crate.gd and CTRL + Drag a reference of the Node into our script and rename it to audio_player.
Note that when we destroy the crate, we call queue_free() which will destroy this object, which prevents the audio from processing properly. To work around this we'll want to await our audio_player's finished signal and then continue destroying the crate from within our destroy function.
class_name Crate ... @onready var audio_player = $AudioStreamPlayer ... func destroy(): audio_player.play() await audio_player.finished queue_free()
Next for the Pickup Scene, add the child node for
AudioStreamPlayer and set the values in the inspector. Open the script
scenes/world/pickup.gd and CTRL + Drag a refrence of the Node into our script and rename it to audio_player. inside our _on_body_entered function we'll also await the finishing of our audio player before queuing free.
class_name Pickup @onready var audio_player = $AudioStreamPlayer func _on_body_entered(body): if body is Tank: body.collect(self) audio_player.play() await audio_player.finished queue_free()
Open our UI
scenes/ui/ui.tscn and add a new Control node as a child to the existing Control node and rename it to Menu. We can also give it a Unique Name accessor. CTRL + Drag this into the
scenes/ui/ui.gd script to track a reference to our menu in code.
In the 2D view, let's setup our Menu Layout. We'll ensure our new Control node has anchoring for Full Rect. Add a child node of type
ColorRect to our Menu and set it's color to a dark gray semi transparent color. Add another child to our Menu of type
MarginContainer and set it's Left and Right margin constants to 256. Inside the
MarginContainer add a child
VboxContainer and add a
GridBoxContainer as a child of that. Our GridBoxContainer's alignment should fill/expand horizontally, but centered vertically.
Next we'll be adding a
HSlider for the SFX and Music setting inside this
GridBoxContainer. We'll call the Music Label MusicLabel and the Slider MusicSlider.
We'll call the SFX Label SFXLabel and the Slider SFXSlider.
Set the Label's text values to "SFX" and "Music" respectively.
Make sure your Sliders' Layout Container Sizing is set to Shrink to Center to best align in the UI. We can also add H Seperation between elements within the
GridBoxContainer and set equal to 16. Our Slider's are also going to be mapping from 0 - 1 and should have their max set to 1 and their step size set to a nice snappy amount such as .05, and the starting value to 1 which is our default audio level.
Next for each slider we'll attach the Range's value_changed signal which passes the new value to the function. We'll connect these the UI's script, which will be called _on_music_slider_value_changed and _on_sfx_slider_value_changed. These funcitons will both call the
AudioServer's set_bus_volume_db and set_bus_mute functions to update the bus volume and mute if we are under a threshold with our new value. These calls require the BUS index which we can also retrieve from our
AudioServer and store in vars on ready. We'll also convert the Slider's value in linear to the db equivilant using the linear_to_db funciton.
class_name UI ... @onready var SFX_BUS_ID = AudioServer.get_bus_index("SFX") @onready var MUSIC_BUS_ID = AudioServer.get_bus_index("Music") @onready var menu = %Menu ... func _on_music_slider_value_changed(value): AudioServer.set_bus_volume_db(MUSIC_BUS_ID, linear_to_db(value)) AudioServer.set_bus_mute(MUSIC_BUS_ID, value < .05) func _on_sfx_slider_value_changed(value): AudioServer.set_bus_volume_db(SFX_BUS_ID, linear_to_db(value)) AudioServer.set_bus_mute(SFX_BUS_ID, value < .05)
Lastly, we'll add a simple input event to check if we are pressing our
ui_cancel action, we can toggle our Menu to be visible/invisible.
func _input(event): if event.is_action_pressed("ui_cancel"): menu.visible = !menu.visible
There we have a great overview of adding Audio assets to your game in the form of Music and SFX separation using Audio Buses and the ability to control them in the UI using the slider node. Be sure to join the discussions on our Discord community server and stay tuned for more content.