Friday, July 6, 2012

Rule Databases for Contextual Narrative... and spelling bees.

Valve's Elan Ruskin gave a fantastic talk at GDC 2012 on using "Rule Databases for Contextual Dialog and Game Logic" -- basically, the implementation behind the dialogue response system in Source games, most recently used in Left 4 Dead 2 and DOTA 2. I'm surprised more people haven't picked up on it because I think it presents some really effective research on procedural narrative systems.

A lot of game logic / narrative resembles a flowchart, especially with the advent of visual scripting systems like Unreal's Kismet or Twine -- resulting in this deeply entrenched concept of branching structure. Authoring and changing these individual branches is usually very expensive.

However, the branching narrative is just the mental model, the conceptual result, the imaginary line of causality that we want players to understand. That doesn't mean we actually have to author our branchy narrative as a branching structure, it just has to unfold like one or resemble one in the player's head.

Instead, maybe we can just write, like 100 different plot events that get triggered based on specific conditions that may or may not get fulfilled during gameplay: e.g. if ( > 0.5 && isNotPresident && ateShrimp) then (player.FoodPoisoning();) or something like that. Some plot events might have only a single condition (if the player is not president) or they might get more specific (if the player is not president and ate shrimp). The idea is that you choose the most specific plot event that fits -- and if none fit, then you can trigger a fallback that still fits but has less context.

In this way, different (but similar) piles of world states will lead to different (but similar AND specific) responses. It's a self-branching structure! (I'm glossing over a lot of details here -- I really recommend looking at Ruskin's slides.)

The one game that I've recognized as using this kind of implementation is Emily Short's Bee. She describes it as something between "stateful" and "quality-based." I'm not really sure what that means but it sounds right.

So in Bee, you choose one of three activities per month.

Some activities advance a particular storyline involving neighbors, a tutor, a love interest, etc. while others alter various world states like your spelling ability or whether you feel comfortable texting that boy you met, which in turn determine your available choices in other activities -- rarely studying for spelling means you lose a competition which means you fight with your parents which means you can fantasize about living with another family which means you feel isolated which means you text the boy next door.

Other events occur only at certain seasons or months -- so (I'm making this example up:) inviting Mrs. Perry for Christmas can only happen if you've gotten far enough in the Perry plotline AND if it's December AND if your parents aren't mad at you. if( PerryTimes > 4 && month == 12 && parentsEmotion == 2 ) then...

Generally, players seem to pick the rarest events with the most specific conditions, because doing "Ironing" or "German Root Words" events is usually static and boring; those are often available which means they have vague, easily-fulfilled conditions and they're worth less.

In a sense, the player is acting as the Source Engine's dialogue director; it is narrative as a mechanic.

I'm currently trying to implement a similar system in Unity to control my current project's plot system -- seeing as I'll probably only have a thousand rules at the very most, and maybe a hundred or so events, I think I'll only need a lightweight system that's "good enough" with none of the optimization that Ruskin described in his talk.

So I'm thinking a PlotDirector class that tracks world state with a dictionary, and a bunch of PlotRule prefabs in assets\Resources\rules\ so I can just grab them all with Resources.LoadAll(path), and each PlotRule (if its dictionary of conditions gets matched) instantiates a PlotEvent that does stuff.

What I realize now is that this is suddenly much more of a writing problem instead of a programming problem; if the self-branching seems amiss, it's because my conditions weren't well-authored or the event doesn't match the situation implied by the conditions.

And I think that's a good problem to have. Ruskin's intent was to "let writers write." This is what that does, and I think it's a strong attempt at proceduralizing narrative.