Skip to main content

Characters

Characters bring your dialogue to life. This guide covers how character data flows from the StoryFlow Editor into Unity, how to display character information in your UI, and how to work with per-character variables at runtime.

Character Data

Characters are defined in the StoryFlow Editor and exported as part of the JSON project in a characters.json file. At runtime, the plugin resolves character data into a StoryFlowCharacterData object that contains everything your UI needs:

C#
public class StoryFlowCharacterData
{
    // Resolved character name
    public string Name;

    // Asset key for the portrait image (before resolution)
    public string ImageAssetKey;

    // Character portrait (can be null if no image assigned)
    public Sprite Image;

    // Mutable character variables - quick-lookup dictionary
    public Dictionary<string, StoryFlowVariant> Variables;

    // Deep-copied list of character variables for mutation and save/load
    public List<StoryFlowVariable> VariablesList;
}

The Name field contains the resolved display string. The Image field is a loaded Sprite ready to display in a UI Image component. The Variables dictionary holds the current runtime values of all per-character variables as StoryFlowVariant values.

Runtime Data is a Deep Copy

When the StoryFlowManager initializes, it creates deep copies of all character data from the imported StoryFlowCharacterAsset ScriptableObjects into StoryFlowCharacterData objects. These runtime copies are what GetCharacterVariable, SetCharacterVariable, and the dialogue system operate on. The original asset data is never mutated, so re-importing or resetting always returns to clean defaults.

Accessing Character Info

From Dialogue State

When the StoryFlowComponent broadcasts a dialogue update, the StoryFlowDialogueState includes the resolved character data for the current dialogue node. Access it through the Character field:

C#
void OnDialogueUpdated(StoryFlowDialogueState state)
{
    bool hasCharacter = state.Character != null
        && !string.IsNullOrEmpty(state.Character.Name);

    if (hasCharacter)
    {
        // Character name
        nameText.text = state.Character.Name;

        // Character portrait (may be null)
        if (state.Character.Image != null)
        {
            portrait.sprite = state.Character.Image;
            portrait.gameObject.SetActive(true);
        }
        else
        {
            portrait.gameObject.SetActive(false);
        }
    }
    else
    {
        // No character assigned to this dialogue node
        nameText.text = "";
        portrait.gameObject.SetActive(false);
    }
}

Subscribe to the OnDialogueUpdated event to receive the state:

C#
void Start()
{
    var sf = GetComponent<StoryFlowComponent>();
    sf.OnDialogueUpdated += OnDialogueUpdated;
}

void OnDestroy()
{
    var sf = GetComponent<StoryFlowComponent>();
    if (sf != null)
        sf.OnDialogueUpdated -= OnDialogueUpdated;
}

Character Portraits

The Character.Image field is a Sprite that can be used directly with Unity UI Image components. Keep in mind:

  • The image can be null if no portrait was assigned to the character in the StoryFlow Editor. Always check before using it.
  • Portrait sprites are loaded during character resolution, so they are ready to display immediately when the dialogue state is broadcast.
  • If the same character appears across multiple dialogues, the same sprite reference is reused - there is no redundant loading.

Character Interpolation

StoryFlow supports inserting character data directly into dialogue text using curly brace syntax. This allows you to write dialogue that dynamically includes the speaker's name or custom variable values.

  • {Character.Name} - Inserts the current character's resolved name.
  • {Character.VarName} - Inserts the value of a character variable (e.g., {Character.mood} or {Character.title}).

For example, a dialogue node with the text:

Code
My name is {Character.Name} and I'm feeling {Character.mood} today.

Would render as something like: "My name is Elena and I'm feeling cheerful today."

Resolution Order Matters

Character interpolation depends on a specific internal resolution order. The plugin processes each dialogue node in this exact sequence:

  1. 1. Resolve character - Look up the character data, load the name, load the portrait sprite.
  2. 2. Update CurrentDialogueState.Character - Store the resolved character data on the dialogue state.
  3. 3. Interpolate text - Process {Character.Name} and other interpolation tokens, reading from the now-populated character data.

This order is critical. If text interpolation happened before character resolution, {Character.Name} would resolve to an empty string. The plugin handles this order internally, so you do not need to manage it yourself. This note is for understanding why the order matters if you encounter debugging scenarios.

Per-Character Variables

Characters can have custom variables attached to them, defined in the StoryFlow Editor. These are ideal for per-character state like mood, relationship level, outfit, or any other property that should travel with the character rather than live as a global variable.

In the StoryFlow Editor, character variables are defined on the character file and manipulated with getCharacterVar and setCharacterVar nodes. At runtime in Unity, these become entries in the character's Variables dictionary.

Reading character variables:

C#
StoryFlowComponent sf = GetComponent<StoryFlowComponent>();

// Read a character variable by character path and variable name
StoryFlowVariant trustValue = sf.GetCharacterVariable(
    "characters/elder",  // Character path
    "trustLevel"         // Variable name
);

// Use the value (StoryFlowVariant can hold string, int, float, bool, etc.)
int trust = trustValue?.GetInt() ?? 0;

Writing character variables:

C#
StoryFlowComponent sf = GetComponent<StoryFlowComponent>();

// Set a character variable
sf.SetCharacterVariable(
    "characters/elder",            // Character path
    "trustLevel",                  // Variable name
    StoryFlowVariant.Int(5)        // New value
);

Changes made with SetCharacterVariable take effect immediately. The next time dialogue text is interpolated or a getCharacterVar node runs, it will see the updated value.

Character Variables vs Global Variables

Use character variables for state that conceptually belongs to a character (mood, relationship, outfit, health). Use global variables for state that belongs to the story or world (quest progress, time of day, world flags). This separation keeps your data model clean and makes it easy to reason about where state lives.

Character variable state at runtime is managed by the StoryFlowManager:

  • Mutable runtime copies: When the manager initializes, it creates mutable copies of all character variables in StoryFlowManager.RuntimeCharacters. These copies are what GetCharacterVariable and SetCharacterVariable operate on.
  • Cross-dialogue persistence: Changes to character variables persist across different dialogue sessions and script transitions within the same game session. If the elder's trust is set to 5 in one dialogue, it remains 5 when another dialogue references that character.
  • Resetting state: Call ResetRuntimeCharacters() on the manager to restore all character variables to their original imported values. This is useful when starting a new game or resetting story state.
  • Save/Load integration: Runtime character variable state is included when using the plugin's Save & Load system. Saved character state is fully restored when loading a save slot.
C#
// Reset all character variables to their imported defaults
StoryFlowManager.Instance.ResetRuntimeCharacters();

Character Assets

StoryFlowCharacterAsset is a ScriptableObject that stores the imported character definition. Each character in your project becomes a separate asset in the Unity project.

C#
public class StoryFlowCharacterAsset : ScriptableObject
{
    // Display name
    public string CharacterName;

    // Normalized path used for lookups
    public string CharacterPath;

    // Default portrait image key (resolved during import)
    public string ImageAssetKey;

    // Resolved portrait sprite
    public Sprite ResolvedImage;

    // Per-character variables
    public List<StoryFlowVariable> Variables;
}

Characters are imported automatically when you import your StoryFlow project. You do not need to import characters separately. The import process reads characters.json from your exported project and creates or updates a StoryFlowCharacterAsset for each character.

Path Normalization

Character paths are normalized internally for consistent lookups. The normalization rule converts the path to lowercase and replaces backslashes with forward slashes. Both the import process and the runtime lookup apply the same normalization. If you are accessing characters directly by path from C# code, the plugin handles normalization for you in GetCharacterVariable and SetCharacterVariable.

If you bypass the public API and access RuntimeCharacters directly, you must normalize paths yourself to avoid lookup failures that result in empty character data.

Need Help?

Join our Discord community to ask questions, share your projects, report bugs, and get support from the team and other users.

Join Discord