When starting the project, it was important to first make sure we understood and agreed on the game's mechanics. Once we had these clarified, I prepared a basic asset list using my audio design document template (Link).
Though memory optimization wasn't an issue at this scope, I generally like to start by setting myself up for an efficient project by going into ShareSets > Default Conversion Settings to set my default format to Vorbis with quality level 7.
For the music, I updated this to high-quality Vorbis. Alternatively, uncompressed PCM would have been valid for key, smaller assets, since we had the space and this eliminates the need for the CPU to convert those files at runtime, decreasing CPU load in exchange for slightly more memory usage.
When thinking about my resource utilization, I was pretty lenient with pre-loading and didn't stream anything, rather loading everything at once in a single bank. Obviously this wouldn't work for a large-scale project, but was not an issue for this game jam.
Card Placement Event
As our game's core mechanic is chaining cards with different elements and abilities together specific to each enemy, it quickly became apparent that my most frequently-used effect, card placement, would need to account for variation in chain intensity and element types. The Wwise event plays the a refined version of those two layers together in order to form the card placement sound.
I prefer to use less events with more variability in order to simplify the in-engine implementation process (and protect myself from needing to make lots of updates down the line), so I decided to handle this using Wwise's state and RTPC game sync features in order to fine-tune this single event with maximum variability.
For the elemental layer, I created a switch container based on the "card_element" state group. The correpsonding state from that group determines the audio file to be played for that layer (card_fx_air, card_fx_earth, ...).
In the related script, I used the Wwise API to update this state:
The impact layer's purpose is to convey growing intensity as players chain cards together and come closer to completing their attack.
Since different enemies require different numbers of cards to complete the chain, this needed to be flexible. So, I decided to create a Real-Time Parameter Control, since this is a continous parameter which uses a float variable type as its input variable. This would allow us to set the precise intensity by updating a global variable which calculates number of cards played vs. the total number possible.
AkSoundEngine.SetRTPCValue("chain_intensity", (float)chainSize / cardSlots);
//chainSize increments from the card being played before this function is called
I created four impact sounds with increasing intensity levels. In order to play the right blend, I decided to use a blend container.
The blend container crossfades between the different intensity sounds to pick the appropriate clip to play.
With all of our parameters updated, the last thing left to do was post the event.
Since the song was broken into segments including an 8-bar intro (before the loopable song area with all instruments), I created a music playlist container which held two music segments, one for the intro and one for the main song. I set these to "sequence continuous" (so they play in order) and set the intro loop count to 1 and the loop count of the main section to infinite. Since the initial imports didn't work as expected, I had to tweak in the music segment editor a bit to get the transition and loop to play through seamlessly.
Wanting the music to have a nice fade in, I set the music playlist container to add a high-pass EQ filter when the game state is set to "menu".
For more about the music itself, see the corresponding section below.
Beyond simple volume adjustments after using soundcaster and playtesting, I wanted to make sure the dynamics were considered.
Since the card placement sounds had lots of heavy, impactful content, this created a clash with the music, specifically in the mid-low frequency area. In order to mitigate this muddying of the mix, I decided to feed these interactive card sounds into their own bus and to use these impulses as a sidechain which compresses the volume of the music when they play.
In the SFX bus, I've added the Wwise Meter effect, which takes the incoming signal levels and feeds these into the "sfx_vol" RTPC.
Then, I set up the RTPCs for the music bus, so that its volume decreases when the "sfx_vol" parameter exceeds a threshold of -20dBFS. Using Soundcaster, I tested the effect, trying different attack and release settings until the effect solved the masking issue without being immediately noticeable to the listener.
Being a light-hearted, casual card game, my mind immediately went to games like Hearthstone, MTG: Arena and Gwent as games with comparable sonic atmospheres.
However, one key thing which set us apart from those games was the lack of a medieval, fantasy element. Our game was set in a breakroom at a workplace for monsters, who've set up card-based puzzle challenges for us to work through.
Though the scope remained relatively minimal, it was important to communicate the key bits of information to the player. My main intention was for the sounds you hear to feel appropriate and respond to your actions.
For that reason, it was important to have element- and intensity-specific sounds. For the elements, I layered some basic ingredients together:
- Air: gust of wind, ambient noise (with sharp in-and-out volume automation for a whoosh effect)
- Earth: boulder impact, footsteps in gravel, falling boulders, white noise
- Fire: flame burning, fireball, white noise
- Water: footsteps landing in water, splashes, modulated ambient noise
and did diverse processing on them based on feel, fit and balance (EQ, pitch, chorus, saturation, reverb).
Then, I used Reaper's extremely helpful "Analyze Loudness" Action (available as part of the popular SWS extension available here) and normalized their levels to a fixed point (in this case, -23 LUFS) before applying limiting on the master bus and bouncing these assets at a proper, even level of loudness.
For "Win Condition", we wanted a low-intensity track; something which would loop in the background and keep a relaxed atmosphere. Contrary to similar reference titles like Heartstone, MTG: Arena or Gwent, we didn't specifically have a fantasy setting, so I decided to take it in a different direction, while keeping some similar elements (acoustic guitar, hand percussion).
Starting with guitar, I tried a few chord progressions with lots of 7 chords, hoping for a laid back, jazzy vibe. After finding a set of chords I liked, I recorded them, and started playing with lead lines.
Once I got something I was happy with, I recorded several takes in order to double these for added stereo depth. There were a few room noises in those recordings you might notice, and with more time I certainly would have gone through and ensured I got clean takes. For now though, they honestly don't sound far removed from the game setting so I felt okay leaving those as-is.
I filled the remainder of the track using virtual instruments, including a jazz organ, a solo upright bass and bongo drums. Listening back, I could've probably also took out some more low-mids from the acoustic guitar to make more room for that bass, but you know, hindsight is 20/20.
I liked slowly removing the high-pass EQ filter as the song fades in, so to translate this to the game environment I added the paused game state in Wwise which adds and removes this high pass effect. Though we didn't build a pause functionality into the game in the two days, this is still used in the transition from the title menu into the game itself.
Okay, so that's a wrap! I hope this little glimpse behind the scenes at what went into those two days of work was interesting. I'm proud of what we got done and am happy to have been lucky enough to find a great developer and artist. Both were very talented and we were able to coordinate and communicate well, despite working remotely and in three different time zones.
If you have any questions, feel free to email me at [email protected]
Until next time!