Sound Component - UQcsse3200/2023-studio-2 GitHub Wiki
First see Components.
The SoundComponent
is a custom component used to manage entity sounds through an event driven system. This component does not do anything by itself. It instead loads the sounds defined within the given SoundsConfig
and begins listening for events to interact with these sounds through the entities EventHandler
.
When to use it
Use this component when you would like sounds to be conditional upon an action performed by an entity (e.g. player movement, enemy death, grenade exploding, button clicked, etc).
When not to use it
Do not use this component for game sounds which are not conditional upon an action performed by an entity (e.g. game music, ambient sounds such as birds chirping, etc). For these use cases, you should instead instantiate and use sounds within the GameArea
itself.
Usage
Initialisation
To initialise the SoundComponent, you must provide a SoundsConfig
class as its only parameter which stores an ObjectMap
of sound names and their respective file locations. Sounds cannot be added after initialisation, so the SoundsConfig
given must include all the sounds that you wish to use throughout the lifetime of the entity. Please see the SoundsConfig
section for more information surrounding how to setup and ideally load the SoundsConfig
from a JSON file.
Here is an example of how to create initialise the SoundComponent and add it to an entity. For this example, the SoundsConfig
will be programmatically set, however it is recommended that you load this from a customisable JSON file for actual use.
SoundsConfig config = new SoundsConfig();
// Add some sample sounds. The files specified in the config must be
// already loaded via the ResourceService. Failure to do so will result
// in the sound failing to load.
config.soundsMap.put("sound_1", "sounds/sound_file_1.wav");
config.soundsMap.put("sound_2", "sounds/sound_file_2.wav");
config.soundsMap.put("sound_3", "sounds/sound_file_3.wav");
// intialise component
SoundComponent component = new SoundComponent(config);
Entity entity = new Entity();
// add component to entity
entity.addComponent(component);
The sound component will now be added to the entity and will listen for play, loop, and stop events triggered through the entities EventHandler
.
Triggering Sounds
Once you have added the SoundComponent
to the entity as described, you can trigger the registered sounds via the entities EventHandler
. This can be retrieved via the entities getEvents()
method.
The available events are playSound
, loopSound
, and stopSound
. The event must be called with one parameter, the sound name (this is different to the file name) which is the key given to the sound in the SoundsConfig
sounds map.
playSound
Triggering this event will play the sound specified through once. If the sound name given does not exist in the sound map, nothing will happen. The sound will respect the sound volume set in the settings screen which could be set to 0. If this is the case, the sound will still play, although it will not be audible. Please ensure you have set the sound volume to be non-zero when testing your sounds.
Here is an example showing how to call this event. It assumes the component already exists on the entity.
entity.getEvents().trigger("playSound", "sound_1");
loopSound
Triggering this event will loop the sound indefinitely until the component is disposed or the stopSound
event is triggered with the same sound name. If the sound name given does not exist in the sound map, nothing will happen. The sound will respect the sound volume set in the settings screen which could be set to 0. If this is the case, the sound will still play, although it will not be audible. Please ensure you have set the sound volume to be non-zero when testing your sounds.
Here is an example showing how to call this event. It assumes the component already exists on the entity.
entity.getEvents().trigger("loopSound", "sound_1");
stopSound
Triggering this event will stop a looping sound from playing. It will not stop a sound triggered via the playSound
event. If the sound name given does not exist in the sound map or the specified sound is not looping, nothing will happen.
Here is an example showing how to call this event. It assumes the component already exists on the entity.
entity.getEvents().trigger("stopSound", "sound_1");
SoundsConfig
The SoundsConfig
class is used to specify which sounds an entity may use throughout its lifetime. It consists of only one property, soundsMap
which is an ObjectMap
from a sound name to its file location. The sound name specified in the config is what will be used to play, loop, or stop it through events. It is recommended that you load in each SoundsConfig
through a JSON file to make it easy to change the sounds used at a later date. This can be done by creating a JSON file with the following. format.
{
"sound": {
"soundsMap": {
"sound_1": "sounds/sound_1.wav",
"sound_2": "sounds/sound_2.wav"
}
}
}
This can then be loaded in as follows (where {file_name} is replaced with the name of the file).
SoundsConfig config = FileLoader.readClass(SoundsConfig.class, "configs/{file_name}.json");
If you are already using a config for the entity however, the SoundsConfig
can instead be added to the existing config as follows by adding it as a public parameter in the existing config class. For example, take the EnemyConfig
.
public class EnemyConfig extends HealthEntityConfig {
public int speed = 1;
public EnemyBehaviour behaviour;
public EnemyType type;
public boolean isBoss = false;
public int specialAttack;
// add SoundsConfig parameter
public SoundsConfig sounds;
}
You must then add the sounds parameter to the existing config file like so.
{
"health": 20,
"baseAttack": 10,
"speed": 5,
"behaviour": "PTE",
"type": "Melee",
"spritePath": "images/enemy/base_enemy.atlas",
"sounds": {
"soundsMap": {
"enemyDeath": "sounds/enemyDeath.mp3",
"enemySpawn": "sounds/enemySpawner.mp3"
}
}
}
It can then be read in using the same method.
SoundsConfig config = FileLoader.readClass(EnemyConfig.class, "configs/enemy.json");
Sequence Diagram
Here is a sequence diagram to better show the interaction between the SoundComponent, Entity, EventHandler, and other components.
sequenceDiagram
participant Entity
participant AbstractComponent
participant EventHandler
participant SoundComponent
participant Sound
par setup
AbstractComponent ->>+ Entity: addComponent(abstractComponent)
SoundComponent ->>+ Entity: addComponent(soundComponent)
SoundComponent->>+ Entity: events = getEvents()
SoundComponent ->>+ EventHandler: events.addListener("playSound", playSound)
SoundComponent ->>+ EventHandler: events.addListener("playSound", loopSound)
SoundComponent ->>+ EventHandler: events.addListener("playSound", stopSound)
AbstractComponent ->>+ Entity: events = getEvents()
end
par playSound
AbstractComponent ->>+ EventHandler: events.trigger("playSound", "sound_1")
EventHandler ->>+ SoundComponent: playSound("sound_1")
SoundComponent ->>+ Sound: play()
end
par loopSound
AbstractComponent ->>+ EventHandler: events.trigger("loopSound", "sound_2")
EventHandler ->>+ SoundComponent: loopSound("sound_2")
SoundComponent ->>+ Sound: loop()
end
par stopSound
AbstractComponent ->>+ EventHandler: events.trigger("stopSound", "sound_2")
EventHandler ->>+ SoundComponent: stopSound("sound_2")
SoundComponent ->>+ Sound: stop()
end