Search Unity

Managing dialogue cues

Discussion in 'Game Design' started by Sendatsu_Yoshimitsu, Jun 24, 2018.

  1. Sendatsu_Yoshimitsu

    Sendatsu_Yoshimitsu

    Joined:
    May 19, 2014
    Posts:
    691
    I have a pretty neat conversation and cutscene system which, given an ID to a valid cutscene, can launch the cutscene, execute any logic/scripting embedded in the scene, then return control to the player. Since I can execute scripts from inside the cutscenes, figuring out a bulletproof workflow for managing where and when the proper cutscenes execute should be enough to control all of the high-level gameflow and narrative progression.

    However, I'm hitting a really hard wall when it comes to actually creating a management system which is both easy to work with, and easy to debug. The simplest, most bulletproof approach I've played with is to create some table<string ID, bool value> representing the narrative's global context, getting/setting flags via scripts, and giving every NPC a list of cutscenes they can play accompanied by the <flag, setting> pairs that must match the global context to trigger that cutscene.

    This approach is simple, and it's relatively easy to build content for, but debugging it is a nightmare: in a full game the global context table would likely be hundreds or low thousands of values long, and since both the conditions to play and the logic that sets/changes a flag are hand-written, a typo or simple mistake is going to instantly disappear into the sea of cutscenes and become impossible to locate once someone notices that scenes are being triggered in a weird order.

    So is there a reasonable alternative I'm missing? I've been looking at what adventure games and narrative-focused RPGs do, but most of them seem to either rely on an absurdly heavyweight custom editor maintained by more developers than we have on our entire project, or rely on terrible workflows (like my global flag table) that drive their writers crazy.
     
  2. JoeStrout

    JoeStrout

    Joined:
    Jan 14, 2011
    Posts:
    9,859
    It's an interesting issue. And not one I've dealt with myself, but I won't let that stop me from trying to help. :)

    It seems to me that you want the logic for triggering a cutscene to be kept with the cutscene logic itself. So then at least if somebody finds cutscene #73 is playing when it shouldn't (or failing to play when it should), you know exactly where to look.

    You say that you have scripts inside the cutscenes. Are you talking about ordinary MonoBehaviour C# classes, or something else? Where do those scripts live?

    In any case, can you add one more script, in some special slot on the cutscene, whose sole job is to watch for the conditions that should trigger that scene? Yes, there will be a lot of these, but as they won't be doing any rendering etc., they should be pretty efficient.
     
  3. TonyLi

    TonyLi

    Joined:
    Apr 10, 2012
    Posts:
    12,698
    Can you provide an example of the data? Is it perhaps a vector of bools like below that describe the current game state:

    Code (text):
    1. String ID    | Bool Value
    2. -------------|-----------
    3. defeatedOrcs | True
    4. helpedOrcs   | False


    And maybe a table of cutscenes like this:

    Code (text):
    1. Cutscene    | Vector Value
    2. ------------|-------------
    3. Cheer       | <defeatedOrcs=true, helpedOrcs=false>
    4. Shake_Fist  | <defeatedOrcs=false, helpedOrcs=true>
    5. Draw_Sword  | <defeatedOrcs=false, helpedOrcs=true>

    And an NPC might have a list of available cutscenes:

    Code (text):
    1. <Cheer, Shake_Fist>


    (BTW, how are you editing the data?)

    So when you want to check what cutscene to play, you go through the NPC's list of cutscenes and find the vector values that are true.

    If that's the case, a lightweight custom editor may actually be helpful for debugging in the Unity editor. It would probably be really useful if it simply showed the table of cutscenes, with two filters: (1) Cutscenes available to a specified NPC, and (2) Cutscenes available to a specified NPC that are currently true according to the current game state (vector of bools). Perhaps with a button to jump to the most recently played cutscene.
     
  4. Sendatsu_Yoshimitsu

    Sendatsu_Yoshimitsu

    Joined:
    May 19, 2014
    Posts:
    691
    Whoops, no, ambiguity's tripping me up there. :) By "script" in that case, I'm referring to executable commands in a custom scripting language I made to give writers the ability to inject game logic through cutscenes. A "cutscene" in my system is just a big JSON file, and each line of the file has a series of lines of dialogue (which are displayed in a text window), accompanied by an optional series of scripting commands that are embedded into each line as metadata. A pretty typical script looks like this (italicized text are executable 'scripts'):

    Hero: Hello there! #PlayAnimation(Hero, Wave), LookAt((Peasant, Hero)
    Peasant: Meh, whaddaya want? #PlayAnimation(Peasant,Grumpy)
    Hero: Nothing, I just- oh no, look out! A bear! #SpawnCharacter(Bear, Spawnpoint_NextToHaypile), FaceCamera(Spawnpoint_NextToHaypile, CameraCut.SmoothPan, 2.0f)

    In the example I gave with the context table, I just have SetFlag(key, value) and GetFlat(key) commands that can be executed in the same way. ^^


    Yep, your examples are pretty much spot-on! The container I'm referring to as a context table is just a list of KeyValuePair<string, bool>, each NPC has a list of cutscenes they can play paired with the conditions that must be true/false to play them, and a separate scriptableobject or CSV stores an identical table for cutscenes that need to trigger based on advances to the world state/main story state.


    The style of cutscenes we're using are text-heavy and scripting/logic light, so I write/edit them in Inkle, export each cutscene as a JSON file that gets imported into Unity, then use a tiny cutscene database class whose only goal in life is to generate a dictionary<cutsceneID,cutscene file> at runtime which can be consulted to determine if we have and can play a cutscene given some arbitrary ID.

    And hmm, making an editor tool specifically for debugging is something I never considered- whenever I've thought about editor customization I end up focused on the creative aspect, which inevitably degrades into considering giant drag & drop/visual graph-oriented workflows to actually manage the story state. ^^
     
  5. TonyLi

    TonyLi

    Joined:
    Apr 10, 2012
    Posts:
    12,698
    Me too. ;)

    But in this case I bet a simple, filterable, clickable list for debugging would probably be a lifesaver for your writers. You could use a ReorderableList (they don't actually have to support reordering) or just a bunch of GUI.Buttons laid out vertically in a GUILayout.ScrollView.
     
  6. Sendatsu_Yoshimitsu

    Sendatsu_Yoshimitsu

    Joined:
    May 19, 2014
    Posts:
    691
    Hmkay, so you don't see any architectural problem with running the entire story flow local to individual character/story flags, instead of using an explicitly hardcoded node graph or another centralized container?
     
  7. TonyLi

    TonyLi

    Joined:
    Apr 10, 2012
    Posts:
    12,698
    The cool thing about your solution is that, since everything's global, a character can check all kinds of diverse flags spanning the whole game world.

    Does it scale up to meet your needs? I'd be concerned about (1) managing content, (2) debugging, and (3) performance.

    Providing an efficient workflow for your writers to iterate (write, revise, and debug) is probably the top priority. The old adage "writing is rewriting" is just as true for interactive writing. They're going to want to constantly change things up to the very last minute. Are they going to be writing in Inkle? How will they keep track of which flags they've added, deleted, and are in use? What if one writer defines a flag "Likes_Whisky" and another defines a flag "likesWhiskey"? How will they keep track of which cutscenes have been added or deleted? How will they use version control? How will they test it in-game?

    Anyway, as long as they have good answers to those questions and they're happy managing content in this format, I don't see any problem as long as the runtime performance scales up to your full game size. You should definitely test it at something like 110% of your expected full content to make sure your code stays within its established CPU and memory budgets. Making it cache-friendly, perhaps using BitArrays, can help.

    If you can give them the tools to debug their game logic on their own, it'll free you up for lower-level programming tasks, and I bet it'll even make them feel empowered to get more creative with how they use the system.