Dialog System

Streaming Audio

The Unreal Engine Audio System does not support simultanous streaming of arbitrary audio content; however you can stream packages that have audio in them. This approach was favored because we wanted to devote as much bandwidth to texture streaming as possible; and having a delay for such fast action games as Unreal Tournament and Gears of War would also be unacceptable, and syncing up the FaceFX anims becomes an additional chore at this point. As an alternative, a special dialog system was created for Gears.

Gears also has a lot of unscripted dialog, so this approach gives us the greatest bang for the buck.

Unscripted Dialog

The Gears 2 dialog system is an unscripted dialog system, or GUDS, which was also used in Gears 1.

The system is event-based and has the Pawns making comments on occurrences in the game, such as when an enemy is spotted, or a man on your team goes down. For each action that can occur, the Pawn has a selection of lines that can be played - it could be any number of lines, even none, for each action. Currently, all the lines a Pawn can speak are held in a GUDBank, which the game will load completely for each pawn in a map. This leads to a large amount of sounds present at any given time.

In order to reduce the memory overhead of the system, the GUDBank for each player is being broken up into several smaller banks. At any given time, a pawn will be guaranteed to have at least one bank loaded, thereby allowing them to respond to any situation they have a setup for.

This process is done during content cooking so that there is no management overhead on the content side. The system introduces a game-specific cooker helper that allows for a game to take control of the cooking process where necessary. Each stage of the cooking pipeline calls into the game cooker helper and allows it to perform any actions it requires. This includes generating the list of packages which should be cooked, the cooking of each individual object, a function that lets the helper load a package (or create one on the fly, as is the case for the GUDS cooking), and a function that is called after a package is loaded for cooking, but before any processing has occurred.

Here is how the GUDS cooking utilizes the system:

The cooker will generate a list of packages it has determined as needing to be cooked. When it is done, it will make the call into the cooker helper where it will add the GUD banks that need to be generated to the lists. These are 'fake' packages, as they do not actually exist. The helper just inserts the names of packages it knows it will need to create. During this time, it will break down the source GUDBanks into several smaller banks as described above. It also generates lists of the sound cues and FaceFX animations that are used by the dialog for use later in the system. (This information will be stored in the persistent cooker data to avoid having to regenerate it each time the cooker is run.) A table of contents package is also put on the list which maps the 'parent' GUDBank class to the collection of GUDBank packages it will be broken into.

The cooker will iterate over the packages in the list, loading each one and cooking the objects in them. When loading a package, it will first make a call into the helper to allow it to load the package. In the case of the GUDS, this is where the helper will create the package on the fly and return it. This involves creating a new GUDBank and filling it in with the appropriate lines. Any FaceFX animations are also inserted into a new AnimGroup that will be applied to the pawn when a bank is set for them. It will also set an internal flag indicating that a GUD package is being cooked or not for use later in the process. The helper will return NULL if it does not need to do any special processing of the given package file, and the cooker will load the package the standard way. This is done for all non-GUDS packages.

After the package is loaded, the helper has a post load function called. In the case of the GUDS system, the helper will find the original GUD banks if the package is GearGame or GearGameContent and clear out their contents. This is to avoid the lines being pulled into their cooked packages. (Note that this operation could have been done during the cooking of the actual GUDBank objects as well, but it demonstrates a usage case for the PostLoad function of the cooker helper.)

The cooker then iterates over each object in the package cooking it. It will first make a call into the helper CookObject function, which returns a boolean indicating whether or not the standard cooker should process the object further. In the GUDS helper case, if the object is a sound cue in the referenced list and the package is not a GUDS package, the sound cue will be set to MarkedByCooker to avoid having it cooked into the current package. If it is a GUDS package, it is set to force export and also has its FaceFX animation group name fixed up if required. If the object is a FaceFX asset or animset, and it is not a GUDS package, any referenced animations used by the GUDS are removed from the set.

At run-time, the GUDS manager handles the loading/unloading of banks. When a pawn is spawned, it gets registered with the GUD manager. If the pawn uses GUDS assets, the manager will see if the collection has already been loaded. If it hasn't, it will kick off an asynchronous load of one of the GUDBank packages that go with the pawn. The loaded package gets put into the proper collection, which is ref counted to avoid hanging onto banks when all of the pawns that use it are gone. At various times, requests can be placed to stream in a new set of banks to reduce repetition of the responses. When a new bank is loaded, old ones can be unloaded.

While this specific system will likely not be usable by your game, it will provide a model for breaking up content based on game-specific needs.

Note that this system is designed for dynamic (unscripted) dialog...

Scripted Dialog

Hard-referenced (situation-specific) dialog would not benefit from this exact approach. A modified GUDS approach could possibly be taken, however, simply breaking the dialog up into much smaller batches. While one batch is being used a manager could be asynchronously loading the next required batch. Once a batch is known to be completed, it could be unloaded, reducing the amount of lines in memory at any given time. Balancing the batch size to ensure minimal memory overhead but no hitches in playback would be the trick in that case.