Skip to main content

StoryFlow Component

The StoryFlow Component is the core node that drives dialogue execution in your Godot project. It manages the runtime, processes node graphs, and emits signals your UI can respond to.

Overview

StoryFlowComponent extends Node, which means you can add it as a child of any node in your scene tree - an NPC, a dialogue manager, a player character, or any other node that needs to run dialogue.

Internally, the component manages the execution engine that walks the node graph, handles the call stack, manages script transitions, and produces dialogue state. You do not interact with the internal engine directly. The component exposes a clean GDScript API for starting dialogue, selecting options, reading variables, and listening to signals.

One Component Per Dialogue

Each StoryFlowComponent runs one dialogue at a time. If you need multiple concurrent dialogues (for example, an NPC conversation and a tutorial prompt), use separate components on separate nodes. All components share global state through the StoryFlowRuntime autoload.

Adding the Component

In the Editor

  1. Open the scene where you want to run dialogue (for example, your NPC scene).
  2. Select the parent node, then click Add Child Node (or press Ctrl+A).
  3. Search for "StoryFlowComponent" in the Create New Node dialog.
  4. Click Create. The component appears in your scene tree, ready to configure in the Inspector.

In GDScript

GDScript
# Create and add the component in code
var storyflow := StoryFlowComponent.new()
storyflow.script_path = "npcs/elder"
add_child(storyflow)

# Or get a reference to one already in the scene tree
@onready var storyflow: StoryFlowComponent = $StoryFlowComponent

Export Properties

All configuration properties are exposed as @export variables and appear in the Inspector when you select the component. They can also be set from GDScript.

General Settings

script_path String default: ""

Path to the script file relative to your StoryFlow project root, without the .json extension. For example, "npcs/elder" or "main".

language_code String default: "en"

The language code used for string table lookup. Determines which localized text is displayed in dialogue. Currently, the StoryFlow Editor only supports English ("en"). Multi-language localization is planned for a future release.

dialogue_ui_scene PackedScene default: null

Optional. If set, the component will automatically instantiate this scene when dialogue starts and remove it when dialogue ends. The scene should extend Control and implement the StoryFlowDialogueUI interface. Set this to the default UI scene at addons/storyflow/ui/storyflow_dialogue_ui.tscn or your own custom scene. Leave empty if you manage your own UI.

Default UI Scene

The plugin includes a ready-made dialogue UI at addons/storyflow/ui/storyflow_dialogue_ui.tscn. Set dialogue_ui_scene to this scene to get a complete dialogue display with zero custom code. See Displaying Dialogue for details on the default UI.

Audio Settings

These properties control how dialogue audio is played. They appear in the Inspector under the audio section.

stop_audio_on_dialogue_end bool default: true

When enabled, all audio spawned by this component is stopped when the dialogue ends. Disable this if you want audio to continue playing after the conversation finishes (for example, ambient music triggered by dialogue).

dialogue_audio_bus StringName default: &"Master"

The audio bus to use for dialogue audio playback. Route dialogue audio to a specific bus (for example, &"Voice" or &"Dialogue") to control volume independently from music and sound effects in your audio bus layout.

dialogue_volume_db float default: 0.0

Volume offset in decibels applied to all dialogue audio. Use this for per-component volume adjustments without changing the audio bus mix.

Signals

The component broadcasts events through typed signals. Connect to these signals from GDScript or via the editor's Node > Signals panel to react to dialogue state changes.

Signal Parameters Description
dialogue_started None Emitted when dialogue execution begins. Use this to show your dialogue UI, disable player movement, or enter a dialogue camera mode.
dialogue_updated state: StoryFlowDialogueState The primary UI update signal. Emitted every time the dialogue state changes - new text, new options, character change, or variable interpolation update. The StoryFlowDialogueState object contains everything needed to render the current dialogue.
dialogue_ended None Emitted when dialogue execution completes (reaching an End node or calling stop_dialogue()). Use this to hide your UI, re-enable player input, or trigger post-dialogue logic.
variable_changed info: StoryFlowVariableChangeInfo Emitted whenever a variable is modified during execution. The info object contains id, name, value, and is_global. Useful for updating HUD elements, triggering animations, or logging state changes.
script_started script_path_name: String Emitted when execution enters a new script via a runScript node. Provides the path of the script being entered.
script_ended script_path_name: String Emitted when execution returns from a script. Provides the path of the script that just finished.
error_occurred message: String Emitted when the runtime encounters an error (for example, a missing script, invalid node connection, or stack overflow). Use this for logging or showing error feedback during development.
background_image_changed image_path: String Emitted when a setBackgroundImage node is executed. Provides the path to the new background image. Use this to update scene backgrounds or full-screen visual novel imagery.
audio_play_requested audio_path: String, loop: bool Emitted when a playAudio node is executed. Provides the audio asset path and whether it should loop. Use this if you want to handle audio playback yourself instead of relying on the component's built-in audio system.

dialogue_updated is Your Main Hook

In most projects, dialogue_updated is the only signal you need to build a complete dialogue UI. It fires whenever the dialogue state changes and provides the full StoryFlowDialogueState including text, character, options, images, and audio. See Displaying Dialogue for details on the state object.

Connecting signals in GDScript:

GDScript
@onready var storyflow: StoryFlowComponent = $StoryFlowComponent

func _ready():
    storyflow.dialogue_updated.connect(_on_dialogue_updated)
    storyflow.dialogue_started.connect(_on_dialogue_started)
    storyflow.dialogue_ended.connect(_on_dialogue_ended)

func _on_dialogue_updated(state: StoryFlowDialogueState):
    # Update your UI with the new dialogue state
    pass

func _on_dialogue_started():
    # Show your dialogue UI
    pass

func _on_dialogue_ended():
    # Hide your dialogue UI, re-enable player input
    pass

Connecting signals in the editor:

  1. Select the StoryFlowComponent in the scene tree.
  2. Go to the Node panel (next to Inspector) and click the Signals tab.
  3. Double-click the signal you want to connect (for example, dialogue_updated).
  4. Select the target node and method, then click Connect.

Control Functions

These functions control the dialogue flow. Call them from GDScript in response to player input or game logic.

Function Parameters Description
start_dialogue() None Begins dialogue execution using the script_path property configured on the component. Emits dialogue_started followed by the first dialogue_updated.
start_dialogue_with_script() path: String Begins dialogue execution with the specified script, overriding the component's script_path property. Useful for dynamically choosing which dialogue to run based on quest state or NPC mood.
select_option() option_id: String Selects a dialogue option by its ID. The option_id comes from the StoryFlowDialogueOption entries in the current dialogue state. Advances execution along the chosen path.
advance_dialogue() None Advances the dialogue when the current node is a narrative-only node (no options to select). Equivalent to the player clicking "Continue" or pressing a key to proceed.
stop_dialogue() None Immediately stops dialogue execution. Emits dialogue_ended. Use this when the player walks away, a cutscene interrupts, or you need to abort the conversation.
pause_dialogue() None Pauses dialogue execution. The dialogue state is preserved, but no further signals are emitted until resumed. Useful for pause menus or mid-dialogue gameplay sequences.
resume_dialogue() None Resumes a paused dialogue. Execution continues from exactly where it was paused.

Basic flow example:

GDScript
@onready var storyflow: StoryFlowComponent = $StoryFlowComponent

# Player interacts with NPC
func on_interact():
    if not storyflow.is_dialogue_active():
        storyflow.start_dialogue()

# Player presses "Continue" key
func _on_continue_pressed():
    if storyflow.is_dialogue_active() and storyflow.is_waiting_for_input():
        storyflow.advance_dialogue()

# UI button calls this when the player picks an option
func _on_option_selected(option_id: String):
    storyflow.select_option(option_id)

State Access

These functions let you query the current state of the dialogue without modifying it. They are safe to call from UI update logic or any read-only context.

Function Return Type Description
get_current_dialogue() StoryFlowDialogueState Returns the current dialogue state. Contains the full snapshot of text, character, options, images, audio, and text blocks. Returns an invalid state if no dialogue is active.
is_dialogue_active() bool Returns true if a dialogue is currently running (started but not ended). Useful for input gating and UI visibility checks.
is_waiting_for_input() bool Returns true when the dialogue is paused at a dialogue node, waiting for player input. Both select_option() and advance_dialogue() require this to be true.
is_paused() bool Returns true if the dialogue is currently paused via pause_dialogue().
get_manager() Node Returns a reference to the StoryFlowRuntime autoload singleton. Useful for accessing shared global state, the project resource, or performing save/load operations.

Variable Access

These functions let you read and write StoryFlow variables from game code. Variables are accessed by their display name as shown in the StoryFlow Editor. Each variable type has its own getter and setter pair.

Function Parameters Return Type
get_bool_variable() name: String bool
set_bool_variable() name: String, value: bool void
get_int_variable() name: String int
set_int_variable() name: String, value: int void
get_float_variable() name: String float
set_float_variable() name: String, value: float void
get_string_variable() name: String String
set_string_variable() name: String, value: String void
get_enum_variable() name: String String
set_enum_variable() name: String, value: String void
get_character_variable() character_path: String, variable_name: String StoryFlowVariant
set_character_variable() character_path: String, variable_name: String, value: StoryFlowVariant void
reset_variables() None void
get_localized_string() key: String String

Example - Checking and setting variables:

GDScript
# Check if the player has completed a quest
var quest_done := storyflow.get_bool_variable("mainQuestCompleted")

# Give the player gold
var current_gold := storyflow.get_int_variable("gold")
storyflow.set_int_variable("gold", current_gold + 100)

# Set a character-specific variable
storyflow.set_character_variable(
    "characters/elder",
    "trustLevel",
    StoryFlowVariant.from_int(5)
)

# Reset all local variables to their default values
storyflow.reset_variables()

reset_variables() Scope

reset_variables() only resets local script variables to their default values. Global variables are not affected. To reset global variables, use the StoryFlowRuntime autoload directly via StoryFlowRuntime.reset_global_variables().

Lifecycle

The component follows a straightforward lifecycle tied to its position in the scene tree:

  • _ready() - The component creates its internal execution context, text interpolator, audio controller, and node dispatch table. It does not contact the StoryFlowRuntime autoload at this stage - the manager is accessed later when you call start_dialogue().
  • During Play - You call start_dialogue() to begin execution. The component walks the node graph, emits signals, and waits for player input. You can pause, resume, or stop at any time.
  • _exit_tree() - The component silently resets its execution context and releases audio resources without emitting signals or accessing the manager (which may already be freed during tree teardown). Global state in the manager is not affected by a single component's removal.
GDScript
# Simplified lifecycle overview (internal behavior)
func _ready():
    # Create execution context and helpers
    _context = StoryFlowExecutionContext.new()
    _text = StoryFlowTextInterpolator.new()
    _audio = StoryFlowAudioController.new()
    _build_dispatch_table()

func _exit_tree():
    # Silently clean up without emitting signals
    if _audio:
        _audio.stop()
    if _context and _context.is_executing:
        _context.reset()

Relationship with StoryFlowManager

The StoryFlowRuntime autoload (an instance of StoryFlowManager) is a singleton that lives for the entire game session. It acts as the central authority for shared state, while each StoryFlowComponent operates as an independent dialogue runner.

What the manager handles:

  • Global Variables - A single global variable store shared by all components. When one component's dialogue sets a global variable, every other component sees the updated value immediately.
  • Runtime Characters - Working copies of character data loaded from the project resource. Character variables modified during gameplay are shared across all components.
  • Once-Only Options - Tracks which "show once" dialogue options have already been selected. This tracking persists across different components and script files within the same session.
  • Project Resource - Holds the imported StoryFlow project with all scripts, characters, and resolved media assets.
  • Save & Load - Provides slot-based persistence for the entire dialogue state.

What each component manages independently:

  • Local Variables - Each component has its own local variable scope for the currently executing script.
  • Execution State - Call stack, current node position, and flow state are per-component.
  • Audio Playback - Each component manages its own audio playback independently.
  • Signals - Signals are emitted per-component, so each node's UI only receives updates for its own dialogue.

Multiple NPCs, Shared World

This architecture means you can have dozens of NPCs running independent dialogues while sharing the same world state. A merchant NPC can check a global mainQuestCompleted variable that was set by a quest-giver NPC's dialogue, and both share the same character relationship data - all without any manual synchronization.

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