Chromatic Dissociation
Game Description
This project is the 3D bullet action gauntlet of dealing against the consequences of shutting yourself off. This was developed for the 2024 Bullet Hell Game Jam 5.
What I Worked On...
For this project I worked on the sound design and implementation of all sound assets in Wwise, programming them into Godot using GDscript
Music Composition
Music was composed by Johnum Palado for the following areas
Main Menu Music
Hub Theme
Enemy Themes
Results Stingers
Credits
Music Systems
Music Implementation

Due to the scope of the game, all of the different levels were in one node in Godot with the enemy and attacks swapping out for each level. I decided to put all the music tracks into one central switch container in Wwise that was called at the start of the game. Every time a level was loaded, I used the AkState node to set the state accordingly.

Most of the tracks featured a unique intro that only played at the beginning, and then an A and B section.
Enemy Attack Sync
Most of the attacks were in a separate switch container, but the first enemy had a unique setup. The attack sound was a bass synth synced to the music, so the enemy sound had to be put into the music switch container but separately bussed and spatialized.
Results Music

We had two musical stingers that would play if the player lost or won a battle. The events featured a play for the stinger and a stop for the main music. The defeat stinger was placed on the player and tied to their HP, and the success stinger was placed on the Enemy and tied to their hp.
#in Enemy node func next_attack(): if attack_index == attacks.size(): CharacterManager.bosses_cleared += 1 $Ak_Victory.post_event() #in Player node func take_damage(amount): if hp <= 0: $Ak_DeathStinger.post_event()
Sound Design Systems
Enemy Attack System

If we were to attach each enemy attack sound to each projectile, the soundscape would have been vastly overcrowded. Instead, we made a looping attacks sounds in the music containers in Wwise. This allowed for clean looping, easy adjustment in Wwise, and let us combine repeated sounds with continuous sounds and while spacing them out evenly.
The event was spatialized to the enemy node and was triggered on ready.

Since their were multiple phases, I needed a way to mute then unmute the enemy attack looping sound. I started by associating a state to the volume property in Wwise. Whenever the boss lost all hp, a timer started that would wait to trigger the next phase of attacks and I set the state to OFF, muting the enemy track during the cooldown.
func lose_hp(amount: int): #print("enemy lost " + str(amount) + " HP") hp -= amount current_boss_bar.value = (float(hp) / float(max_hp)) * 100 if hp <= 0: Wwise.set_state("EnemyAttack", "Off") next_attack()
In the Spawner script, whenver the timer hit 0, I set the Enemy Attack back to ON. Initially, I had these as event calls that would start or stop the loop, but since the timer logic was in the _process function (called every frame), I switched to using states so that multiple calls wouldn’t trigger duplicate enemy attack loops.
func _process(delta): if timer > 0: timer -= delta if timer <= 0: Wwise.set_state("EnemyAttack", "On")
Whenever an enemy lost its hp and switched phases, the spawner would be deleted and then re-added to the node. So I placed a call to mute the enemy attacks and critical hit sound effect in the exit tree function of that node’s script
func _exit_tree(): $AK_CriticalHit.post_event() Wwise.set_state("EnemyAttack", "Off")
Player Attack and Hit
Since the player had less projectiles and needed more accurate aural feedback, we called the attack sound in the player script.
func fire_projectile(weapon_idx : int): $AttackSound.post_event()
Using the same reasoning about accurate aural feedback, whenever a player or enemy was hit, a corresponding function in the pool manager was called.
func _ready(): bullet_pool.connect("hit", _on_player_was_hit) player_pool.connect("hit", _on_enemy_was_hit) func _on_player_was_hit(bullet): if player.invuln_timer <= 0 and player.scene_transisitoning == false: $PlayerHitSound.post_event() func _on_enemy_was_hit(bullet): if enemy.scene_transisitoning == false: $EnemyHitSound.post_event()
UI and Volume Control



Anytime I get the opportunity, I setup volume controls for the main, music, and sound effects. I start by creating 3 RTPCs in Wwise with the default set to whatever is the default value for the sliders in the game engine (usually 50%).

Then I create 3 busses and route all the sounds through the appropriate bus while associating the volume with the corresponding RTPC.
In Godot, on the node with the audio controls, there is a function that is called every time the slider value is changed. The function receives a float and string parameter for the bus value and name respectively. As long as I name the Wwise busses the same names and have the correct range on the parameters, I can use those variables directly.
func _on_bus_changed(bus_value : float, bus_name : String): AppSettings.set_bus_volume_from_linear(bus_name, bus_value) Wwise.set_rtpc_value(bus_name, bus_value, null)
