CoD: Modern Warfare Killstreak Syncing Sounds

In CoD: MW players can perform unique abilities called Killstreaks. Many of these consist of long animations and events that require sound playback to be in sync with visual designs.

In order to achieve this, I first needed to design fragments of the overall feature in chunks of 10 seconds or less in order to avoid having to stream audio, which would cause critical sync issues not desired in a multiplayer environment. With 10 second chunk assets, all pertinent audio could be loaded in-game. Splitting a 30 second event every 10 seconds involved taking a cut layer in my DAW and sending the subsequent section down a track with a clean fade-out from part A and a fade-in for part B. Realistically, each segment consisted of several items, but that’s the basic idea.

In script, I first ran checks to ensure any game object entity my audio would be attached to existed at run-time:

{

  • if isValid(entity) then … carry out audio functions

  • else, do nothing

  • // when testing, I often script print out statements to confirm checks, conditions, etc.

}

Once confirmed, could trigger audio playback several different ways:

  • I would attach the sound playback entity to the existing game object as soon as the event started, and sound would travel with the object

  • If there’s no tangible game object to work with, then I’d use in-game XYZ coordinates to determine the start location for audio playback and have the sound entity travel to a target location over X amount of seconds

    • playSound( sound entity name, …, XYZ location );

    • moveTo( target location, time in seconds to travel );

  • If an event happens to be long I could thread several functions that handle the audio event per chunk, having the first thread carry out logic immediately and having subsequent ones wait X seconds before performing their operations

    • eventFunctionTwo( ) // threaded off main function

    • {

      • wait( 10 ); // causes local pause for 10 seconds until moving on

      • // play audio

    • }

In the case that a game object entity is deleted before I wanted audio to stop, I would simply create a flag that would trigger audio playback, as a required condition, at the location and point in time in which this event occurs to reflect an outro sound.

  • {

    • set playOutroFlag; // somewhere in script’s main function, flag is created

  • }

  • {

    • gameObject delete; // game designer deletes game object here

    • activate playOutroFlag; // activating flag lifts the restriction on the outro audio playing

  • }

  • {

    • wait( playOutroFlag is true ); // in the threaded function, we wait for the flag condition to be true before we move on and play audio

    • play outroSound;

That’s the overview of how I could get sound playback to sync for long in-game events. Split layers with pre-defined fades in my DAW, threaded functions with wait times, and conditional flags for additional support where needed.

CoD: WWII Enemy Tanks

Using a proprietary C# based scripting language, I created a system to handle audio playback behavior for the enemy tanks in the CoD: WWII campaign.

First, I perform a check to see if the tank entity is alive and not null. If not alive, do nothing.

If it is alive, then thread an init tank audio function to kick things off:

////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////

{

  • If tank entity is null, then do nothing

  • else, thread init_enemy_tank_audio();

  • // threaded methods branch off the parent function, and remaining logic in parent function

  • // is not dependent on the threaded methods’ behavior carrying out through completion

}

////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////

  • Initiate static data, such as:

    • Sound mapping tables with data used to establish distance-based playback behavior (close idle engine sounds vs. distant idle engine sounds, etc.)

      • Some of these table parameters include min and max distances, fade in and out durations

    • Initial tank entity values such as initial speed, health, etc., to compare against current values on first iteration

  • Initiate dynamic data such as:

    • Engine, tread, and metal wronk/groan handler methods

  • I requested getter methods from a gameplay engineer to periodically obtain data about the tank and update behavior accordingly, for example:

    • Current tank health

    • Current player distance from tank

    • Current tank speed

    • Current tank cannon rotation value

////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////

init_enemy_tank_audio()

{

  • thread init_static_tank_data(); // sound mapping tables, initial entity values

  • thread init_dynamic_tank_data(); // engine handler, treads handler, metal wronk handler, etc.

}

/* the dynamic content is obtained while the tank is alive, so each piece of data in its own threaded function with recurring checks to manage what sounds play */

Engine handler() // example with abstract explanation of logic design below

{

  •  get current speed()

  •  If current speed > 0, play tank movement sound (if not already playing)

    • If prev speed == 0, also play lurch sound (mvmt sound is ducked based on parameter behavior handled in asset spreadsheet)

    • else, do nothing

  • else, play idle sound (if not already playing)

    • if prev speed > 0, also play stopping sound

  • prev speed = current speed

}

////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////

A similar treatment is given to the cannon rotation sounds, using a start one shot, a rotation loop, and an end/outro sound.

If the tank dies, then:

  • Stop any sound currently playing

  • Play breaking down sounds

  • Delete and clean up audio data