Engine Audio Design & Implementation (Devlog #2)

The first significant audio event I decided to work on was engine audio for the spaceship in our game. Getting auditory feedback for core mechanics is essential for player engagement and immersion, important metrics for our monthly playtesting sessions. The game is divided into two gameplay phases, “resource” and “minigames”, both of which involve one or both of the players piloting the spaceship; for this reason I decided engine audio should be the first to be developed.

At the time of development I did not have many details about the ship, or its engine, so decided to create a simple engine synth patch in Vital to allow for rapid iteration. Drawing inspiration from the Sea-moth in Subnautica, I decided to begin with an oscillating sine wave type sound, with automation for the pitch and LFO frequency to give the sound an engine characteristic. After iterating this patch a couple of times, I ended up with something that conveyed the character I was looking for and stayed consistent with the tone and theme of the game.

To differentiate between the OnLoad and OffLoad I duplicated the patch and modified it to have less high frequency content and a slightly different tonality. Exporting these RPM loops was somewhat tedious, achieved with a MIDI clip and adjusting the Modulation between takes, giving me 14 different RPM stages for each Load. Next, I just had to create seamless loops from each RPM stage and export them to Wwise.

Inside Wwise I’ve used blend containers with base 1.41 logarithmic curves to create smooth transitions between RPM loops. In addition to the fades, I automated the pitch of each SFX object in the blends to bend up and down 100 cents at their respective positions in the RTPC range so that each RPM loop blends seamlessly. This process was repeated for both OnLoad and OffLoad, both of which were then added to a parent Blend to be controlled with a separate “Load” RTPC. Both blend containers were set to continuous mode to reduce voice count.

Game engine implementation was relatively straight forward. The two parameters I needed to drive were “RPM” and “Load”. RPM required a normalized velocity value from the ship, and load needed a way to detect acceleration / user input. As the gameplay is broken phases, there are two separate classes for the different perspective movement systems. Although fundamentally similar, they were designed in slightly different ways, requiring changes to be made to the functions and variables I created to drive the Wwise RTPC’s.

Load calculation was really simple for both, just determining whether the player was currently accelerating. In the case of the OrbitShip class, this meant getting an absolute value from the thrust vector.

FlightShip class had separated values for X and Y inputs, so I just summed these in a comparison to determine input.

For RPM calculation it again was very similar in both classes, the only difference being how velocity is obtained. In FlightShip velocity was split into its respective X and Y values, so to get a normalized speed I had to find the largest, absolute velocity value, divided by the maximum speed and multiplied by a max RTPC variable (allowing for weighting without modifying the Wwise event). OrbitShip provided a pre-calculated speed variable that I used directly.