After banging my head some more, i finally figured it out.
For reference, this is the design i came up with:
game loads (root) [(currently using)] (medium sized game)
load audio script (singleton)
preload audio library of game
load changeable scene
load every other nodes of the scene
call audio (singleton script) via events/signals
load audio node [scene tree library] (load all audio of game)
play necessary audio
queue_free() after sound plays
removes loaded audio node
menu.gd (Just to call the function in the singleton)
extends Node
func workNowPlsForCryingOutLoud():
var volume_db = 10
var pitch_scale = 3
audioManager.play("confirmButton", {volume_db = volume_db, pitch_scale = pitch_scale})
audioManager.gd (Singleton)
extends Node
var audioScene = load("res://scenes/audioPlayer.tscn") as PackedScene
sample way to call it:
audioManager.play("confirmButton", {volumedb = 100, pitchscale = 9})
func play(audioSoundName, optionals = {}):
var audio = audioScene.instance(); addchild(audio)
var sound = audio.getnode(audioSoundName)
_setSoundProperties(sound, optionals)
sound.play()
return sound
func _setSoundProperties(sound, optionals):
var volume_db = (100 if not optionals.has('volume_db') else optionals['volume_db'])
var pitch_scale = (0 if not optionals.has('pitch_scale') else optionals['pitch_scale'])
var mix_target = (0 if not optionals.has('mix_target') else optionals['mix_target'])
var bus = ('Master' if not optionals.has('bus') else optionals['bus'])
var autoplay = (false if not optionals.has('autoplay') else optionals['autoplay'])
var stream_paused = (false if not optionals.has('stream_paused') else optionals['stream_paused'])
# MIX_TARGET_STEREO = 0 The audio will be played only on the first channel.
# MIX_TARGET_SURROUND = 1 The audio will be played on all surround channels.
# MIX_TARGET_CENTER = 2 The audio will be played on the second channel, which is usually the center.
sound.mix_target = mix_target #int value of any from above comment
sound.volume_db = volume_db #tested range: 0-100
sound.pitch_scale = pitch_scale #range from 0-16
sound.bus = bus #string name of bus channel
sound.autoplay = autoplay #bool: true or false
sound.stream_paused = stream_paused #bool: true or false
return sound
What the problem was.
- I was calling the audio scene node from a location it wasn't loaded in to.
- Hence, i keep on getting null based errors.
If you missed it, calling the function can be as simple as: audioManager.play("confirmButton")
Some manifestations of the solution.
Returning the instance after playing the sound is so it's capable of calling built in audio functions.
Implemented like so:
extends Node
var bgSound = audioManager.play("gameBgMusic")
# logic to accept command/signal to change scene
# stop playing the bg sound before changing scenes.
func _changeScene():
bgSound.stop()
# change scene code here
I was really banging my head for this to work so i could stick to this kind of design pattern when handling audio - permanently.
In the end,
I realized this wasn't optimal. So i designed a couple of ways to structure a project - keeping the balance of CPU usage and Memory load in mind.
Disclaimer: The perceived size of the game for a design is a personal assessment. This is not in any way backed up by research.
game loads (root) (medium sized game) [(Used in this solution)]
load audio script (singleton)
preload audio library of game
load changeable scene
load every other nodes of the scene
call audio (singleton script) via events/signals
load audio node [scene tree library] (load all audio of game)
play necessary audio
queue_free() after sound plays
removes loaded audio node
####################################################################
game loads (root) (very small game) [(What i used to do)]
load changeable scene
load every other nodes of the scene
call audio via events/signals
play necessary audio
load audio node
####################################################################
game loads (root) (small-medium sized game)
load changeable scene
setup scene root for audio stream export vars
load every other nodes of the scene
load audio node [empty stream property]
call audio via events/signals
set audio stream property
play necessary audio
####################################################################
game loads (root) (medium sized game)
load changeable scene
load audio node [scene tree library] (load all audio of scene)
load every other nodes of the scene
call audio via events/signals
play necessary audio
queue_free() after sound plays
removes loaded audio node
####################################################################
game loads (root) (medium-big sized game)
load audio script (singleton)
setup audio libraries to preload
load changeable scene
load every other nodes of the scene
call audio (singleton script) via events/signals
load audio node [scene tree library] (load all audio of scene)
play necessary audio
queue_free() after sound plays
removes loaded audio node