Documentation menu

Design patterns and best practices

Description: Common patterns for Narratyr projects

Variables vs. Functions

There are two ways to share state between Narratyr and your Game -- either through variables or functions. Which should you use and when?

A common example: Inventory

Lets say you want to know about the player's inventory in a quest. A common idea here might be to check if the player has retrieved a requested item, or customize dialogue based on the presence or absence of an item. One way to represent this would be through a variable:

An example inventory variable using set<entity_reference<item>> An example quest

In this example, the inventory is a set of Item entities (a set is like a list, but where duplicate entries are not allowed.) entity_reference<Item> is used here to allow Narratyr to know as much about the items as possible, but this isn't required. You could just as easily use a list<string> if you wished for more flexibility (duplicates and arbitrary strings)

What are the advantages to this?

  • Narratyr can handle save-game serialization for you.
  • Narratyr also has engine debuggers to display variables, so you can see the state in an editor window when debugging.
  • Quests and Dialogues always know what's happening

The disadvantages?

  • Narratyr doesn't support storing structures (outside of entities) in variables, so if your inventory items require a lot of metadata, you likely don't want to use Narratyr as your inventory system.
  • If your inventory is large and complex, you may need more complicated data structures than a list or set.

The alternate approach here would be to use functions:

Function example Gate example

In this example, there's: addInventoryItem(itemId, count), removeInventoryItem(itemId, count) and hasInventoryItem(itemId).

What are the advantages of this?

  • You can use any underlying data structure you want for your inventory, as long as you provide a set of functions to interact with it.
  • If you already have systems setup before you decided to use Narratyr, this can create a natural bridge.

What are the disadvantages?

  • More complex to build -- you need to write these functions in your game, then let Narratyr know about them. If the function changes in the game, you need to remember to update the definition in Narratyr.
  • Debugging becomes more complex. Because a function can do anything and Narratyr has no way to look into it, debugging becomes more challenging.
  • You need to let Narratyr know that you modified state, if you use functions in gates or event listeners, so those nodes know to re-evaluate their conditions. On the other hand, variables in Narratyr handle this automatically.
    • For example, if you were to update the inventory in the game, you need to let Narratyr know so it can rerun any pending hasInventoryItem calls. This is one function call so it's simple, but it's also easy to forget.
  • You'll need to handle save games yourself

Recommendation

Use variables when you can, because they're much simpler to manage and debug. Use functions when the underlying system is more complicated than is representable in Narratyr.

Dialogue Routing

Often, a character may be revisited multiple times in a game, and the conversation with them will evolve over time as the game evolves. It may be temping to create a dialogue graph for each individual interaction. This is completely reasonable, but it does leave it up to your game to choose the correct dialogue graph based on the current game state, which may move logic outside the dialogue system which belongs in the dialogue system.

An alternative approach is to make Dialogue graphs which are character-centric instead of interaction-centric. In this model, you would use a Hub node in order to route to the correct sub-graph based on Narratyr state. Here's an example:

Hub based on Objective State

In the above example, the conversation with Sandra changes based off the status of objectives and variables. This is admittedly a bit complicated looking though. One additional pattern you can use here is to use Start Dialogue to divide into sub-graphs.

Hub with Sub Graphs

Entity References can save you from bugs

Narratyr variables can store something called an Entity Reference, with a subtype of either any or a defined entity type (Character, Item, etc.). You can use these to reference an entity defined in the Narratyr project. The underlying type for this is simply a string, which is what your game code will see. The id string is something called a proquint, which is a random id that's designed to be pronounceable. If you have a character named Sally, and a variable that stores an entity reference, the value in the variable won't be the literal string Sally, it will be a unique id like mufob-kogoh-hafal-jatun (you can see this id in the entity editor page).

Why store an entity reference instead of a string containing the name? Both are valid approaches, the main advantage of an Entity Reference is that Narratyr understands it so it can survive renames and the validator will give you better checks (for instance, if you reference an entity that doesn't exist)