Singleton, childs and resources?

Godot Version

4.4.1

Question

Hi,
I created an Audio Manager, extending from Node, containing many AudioStreamPlayers as childrens, each AudioStreamPlayer dedicated to a sound fx (a wav file) or a music playlist (see screencopy). When I use this AudioManager as a a child of my main game scene, everything works fine and I can trigger sounds at will.
But I’d like to use this AudioManager as a singleton, in order to avoid duplicates and also sound or music chops between scene changes.
As soon as I install this AudioManager as a singleton, when the program runs, it seems that every AudioStreamPlayer “forgets” about its declared AudioStream resource. When debugging, I see that every AudioStream field of every AudioStreamPlayer is empty… Why does it lose this resource ???

In general, is it normal that a Scene instanced as a singleton looses its resources ?

singleton :

one AudioStreamPlayer in inspector:

singleton in debugger, with empty AudioStreamPlayers :

You should use the .tscn file as your singleton, not .gd

1 Like

I did something like this by adding a global script I called PlaySound, so I can call functions on it like cannon_shot(). That lets me call PlaySound.cannon_shot() from anywhere in the code.

It looks kind of like:

extends node

const CANNON_SHOT: String = "res://Assets/Audio/CannonShot.wav"
const LASER_SHOT:  String = "res://Assets/Audio/LaserShot.wav"
[...]

@onready var player_cannon_shot: AudioStreamPlayer = _setup_stream("Game", 1.0, 1, 1.05, preload(CANNON_SHOT))
@onready var player_laser_shot:  AudioStreamPlayer = _setup_stream("Game", 1.0, 1, 1.05, preload(LASER_SHOT))
[...]

func _setup_stream(bus: String, vol: float, polyphony: int, variance: float, res: Resource) -> AudioStreamPlayer:
    var asr:  AudioStreamRandomizer = AudioStreamRandomizer.new()
    var plyr: AudioStreamPlayer     = AudioStreamPlayer.new()

    plyr.bus           = bus
    plyr.stream        = asr
    plyr.max_polyphony = polyphony
    plyr.volume_db     = vol

    asr.add_stream(0, res)
    asr.random_pitch = variance

    add_child(plyr) # Adds to scene root...
    return plyr

func cannon_shot() -> void: player_cannon_shot.play()
func laser_shot()  -> void: player_laser_shot.play()
[...]

I’ve also got a more complex function in there for setting up positional sounds; those you need to spawn individual per-sound nodes and place them in the scene tree to make positional audio work, so I’ve left that out for simplicity.

1 Like