What Problems Existed With The Old Dialog System?
1. In the previous dialog system, content integrators could start specific dialogs at specific locations in the game. However, this made it hard for the narrative designers to add new or change existing dialogs as they always required the help of the content integrators to do so.
2. Generally, content integrators prefer to work on higher priority tasks, rather than adding all the written dialog variations to the game. Unfortunately this often resulted in outcomes that the narrative designers did not intend.
3. Content integrators had to create multiple logic and in different systems to ensure that dialog started/stopped at the right time (for example, 'check if the person is already speaking a more important line', 'check if the person is alive', 'check if the line was already played before' and so on). This made already complicated systems even more complex by having them deal with dialog logic.
How Does The DRS Solve These Problems?
The DRS decouples dialog-feedback from specific game logic. It achieves this by adding a layer of abstraction between something-is-happening and dialog-feedback-is-started. In this way the DRS permits responsibilities to be split between content integrators and narrative designers. So, content integrators are now only responsible for providing information about the state of the game-world and the events that are happening in it, while narrative designers are responsible for converting those inputs into interesting dialog outputs.
This is a very different approach in comparison to the old system - content integrators no longer start dialog lines, they just inform the DRS that an event has been initiated and that the event can be linked to a dialog. This might sound like an additional step to just get a dialog line to be played, but it has many positive effects particularly when you have more than just trivial dialog scenarios. Also, all logic related to overlapping dialog, timeouts for dialog lines, variations (based on many things in the game world) are now dealt with centrally in the DRS and not in various different systems.
How Does It Work?
There are some central concepts used in the DRS that you should fully understand before using it:
- Signals: These are sent by game logic (and by the DRS itself) whenever something potentially interesting is happening, such as signal_enemy_spotted, signal_received_damage, or signal_area6_entered.
Note: Just because a signal is sent does not mean dialog will be played. This a decision for the narrative designers who must decide if/how any dialog will react to game-events.
- Variables: A name and a value that describes the state of the game. It can be created or updated by game logic and by the DRS. These variables can then be used in conditions to find the best fitting dialog to an event. Currently it supports Integer, Float, Bool and Strings (Note: that the actual string is not stored internally, but is stored as a hashed version of it).
- VariableCollections: A named group of related variables. Every variable is part of a collection, so within a collection each variable-name needs to be unique. You can distinguish between three different types of collections:
- Context variables collections are passed along with a signal, for example a damage-received signal could have a context variable collection with variables such as AmountOfDamage, DamageSource and HitZone. Note: these collections only exist while the signal is being processed.
- Local Variable collections are linked to an actor, so an actor might have a local variable Name, Health or CurrentWeapon. Local variable collections only exist as long as the actor exists. Note: the name of a local collection is always the same as the name of the actor.
- Global variable collections exist for the whole game. They contain global information, for example Quest4Solved, NumChildrenRescued or CurrentMapName.
Note: Even though they are called global you can have just one global collection per level.
- Responses: Are always connected to a specific signal, so when a corresponding signal is sent by some game logic (or the DRS) the response is executed. Responses can contain several conditions and actions where different conditions will result in different actions being executed.
- Conditions: The output of a response can/will depend on different conditions. This is the dynamic part in DRS and is based on the current state of the world (provided through DRS variables) and where the output of a response may be different. For example, the dialog output for the signal signal_received_damage might depend on the health of the receiving actor, the amount of damage taken and or the source of the damage. Games can also implement custom conditions, so for example say you are creating a game involving a math-professor you might need a condition like isPrimeNumber.
- Actions: A response might contain several actions. The most common action is probably the speakLine action, which executes a line of dialog. Other default actions are ChangeVariable (for example to keep track of how often a line has already been played), PlaySound, SendSignal or CancelResponse. Games can also implement custom actions, for example you might want to trigger AI Actions based on dialog.
- Actors: Every object that is part of the DRS. This means every object that sends signals is a DRS actor. Every object that has local variables is an actor and every object that is used in an action is an actor. Note: actors are normally referenced by their name. Responses can also change the active actor in order to let another object react to a signal. For example, narrative designers might decide to let the player-companion say the line "what do you see"? when actually the player received an enemy-spotted signal.
Game logic can decide to always send a signal sg_door_opened when the player opens a door. It's then up to the narrative designer as to what they do with this signal, they might just ignore it, or they might add a response to the signal that then starts a generic dialog such as "look at me, I am opening a door".
The narrative designer can also decide that no dialog will be played when opening a door, but they may still create a response for the signal which for example just increments a variable (created by themself) in the local variable collection of the door-opener-actor named opened_door_counter. They may then use this variable later on, for example in the response to the signal sg_princess_freed, by having a specialized dialog-line such as "I had to open more than 70 doors to get to you, but boy it was worth it”.
This shows how narrative designers can create interesting and more specialized (and therefore better fitting) dialog when they get enough input signals/variables to work with. If the sg_door_opened signal has a context variable containing the name of the opened door, then the narrative designer might have used this additional information to trigger dialog for very specific doors only.
The following example shows how the game event, player was shot by someone is converted into actual dialog output. The "???" block is the part where the DRS comes into play.
Flow diagram for a game event
The diagram below shows the flow of the same signal as above. This time in a vertical way and is based on the outcome of the conditions when different paths are chosen.
When the DRS receives a signal:
Step 1: Find the linked response for the signal.
Branch: Outcome: No response for this signal -> done.
Step2: Check if all conditions for the response are met.
Branch: Outcome: condition not met -> done.
Step3: Execute all actions in that response (simultaneously, unless the DRS user specified some InitialDelays for one of the actions).
Step4: Wait for all actions to finish.
Step5: Find the best Follow-up Response.
Branch: Outcome: None found -> done, otherwise continue with Step 3.
Depending on the depth of the tree, the execution of the whole response tree might result in either one of the following results:
- A lot of output (spoken lines, changed variables, etc.)
- No output at all (if the conditions are not matched, or there is simply no response linked to the signal).
It is also worth mentioning that a response can also send another signal by itself, this may start another response-tree in parallel. This can be useful to split up response trees, reuse branches or to realize looping.
Remark at Step 5:
How does the DRS know what the best response is?
Well, by simply assuming that the most specific response is the one that best fits the current situation. So the DRS will pick the response with the most conditions (which of course all need to be met, otherwise the response is not taken into consideration). This approach does make sense when for examplke you have to choose between the generic lines "there! an enemy" (a response with no conditions) or a very specific line such as "oh no, an orc with a bow and just 15 seconds after we defeated this big manticore!" (a response with many conditions). Clearly the second option here is the best fit for the sitution. Each response can have any number of follow-up responses, but only one is executed i.e. the one with the most matched conditions.
Flow signal in a vertical path