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
- Open the scene where you want to run dialogue (for example, your NPC scene).
- Select the parent node, then click Add Child Node (or press Ctrl+A).
- Search for "StoryFlowComponent" in the Create New Node dialog.
- Click Create. The component appears in your scene tree, ready to configure in the Inspector.
In 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:
@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:
- Select the
StoryFlowComponentin the scene tree. - Go to the Node panel (next to Inspector) and click the Signals tab.
- Double-click the signal you want to connect (for example,
dialogue_updated). - 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:
@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:
# 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
StoryFlowRuntimeautoload at this stage - the manager is accessed later when you callstart_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.
# 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.