Skip to main content

Quick Start

This guide will get you from a finished StoryFlow Editor project to a working in-game dialogue system in just a few minutes. Each step builds on the last, so follow them in order.

Prerequisites

Make sure you have the StoryFlow plugin installed and enabled in your Godot project before continuing. See the Installation guide if you haven't done this yet. You will also need a StoryFlow Editor project with at least one script file.

1. Sync Your Project

The fastest way to get your story into Godot is Live Sync - a WebSocket connection between the StoryFlow Editor and the Godot Editor. Instead of manually exporting and importing files, you connect once and sync with a single click. Every time you make changes, just click Sync again.

Start the sync server in StoryFlow Editor

  1. Open your project in StoryFlow Editor.
  2. Click the three-dots button next to the Export button in the toolbar. In the dropdown menu, select WebSocket as the export format.
  3. The Export button now reads Start Server. Click it to start the WebSocket server on port 9000. The button changes to Sync once the server is running.

Connect from Godot

  1. In the Godot Editor, find the StoryFlow dock panel (it appears after enabling the plugin).
  2. In the Live Sync section, leave the port as 9000 (the default).
  3. Click Connect. The status label updates to Connected.
  4. Click Sync in the Godot dock (or click Sync in the StoryFlow Editor) to pull the full project.

That's it - your project is now imported into Godot. The plugin creates the following resources in the output directory (default: res://storyflow/):

  • A StoryFlowProject resource - The root project resource containing metadata and references to all scripts and characters
  • One resource per script - Each script file from your StoryFlow Editor project becomes its own StoryFlowScript resource
  • Character resources - Character definitions including name and portrait reference
  • Media assets - Images are imported as Texture2D and audio files as AudioStream

The StoryFlowRuntime autoload automatically picks up the imported project. You can verify by checking StoryFlowRuntime.has_project() in any script.

Iterating is instant

Once connected, your workflow is: edit in StoryFlow Editor, click Sync, and the changes are live in Godot. No file dialogs, no re-importing - just one click. You can also sync from the Godot dock or use the StoryFlow toolbar button. See the Live Sync page for details on reconnection, signals, and programmatic control.

2. Add the Component

The StoryFlowComponent is the runtime node that drives dialogue execution. Add it to any scene that needs to run a StoryFlow script - typically your player character, a dialogue manager, or an NPC.

  1. Open the scene where you want dialogue to run.
  2. Click Add Child Node (or press Ctrl+A).
  3. Search for "StoryFlowComponent" and add it to the scene tree.

In the Inspector panel, configure these properties:

  • Script Path - The path to the script file to run, relative to your StoryFlow project. For example, "npcs/elder" or "main".
  • Language Code - The language to use for dialogue text. Defaults to "en".
  • Dialogue UI Scene - Optional. A PackedScene for the dialogue UI. Set this to use the default UI or your own custom scene.
GDScript
# You can also configure the component in code
@onready var storyflow: StoryFlowComponent = $StoryFlowComponent

func _ready():
    storyflow.script_path = "npcs/elder"
    storyflow.language_code = "en"

3. Create a Basic UI

You need a UI to display dialogue text and options to the player. The plugin provides two approaches:

Option A: Use the Default Dialogue UI (Recommended)

The plugin ships with a ready-made StoryFlowDialogueUI scene that handles all the basics. To use it:

  1. Select your StoryFlowComponent in the scene tree.
  2. In the Inspector, set the Dialogue UI Scene property to the default UI scene located at addons/storyflow/ui/storyflow_dialogue_ui.tscn.

The default UI includes:

  • %TitleLabel - Displays the dialogue title
  • %TextLabel - A RichTextLabel with BBCode support for the dialogue body
  • %CharacterNameLabel - Displays the speaking character's name
  • %CharacterPortrait - Displays the character's portrait image
  • %OptionsContainer - A container that dynamically creates option buttons
  • %AdvanceButton - A "Continue" button for narrative-only dialogue nodes

When dialogue starts, the component automatically instantiates the UI scene and adds it to the scene tree. When dialogue ends, the UI is removed.

Option B: Manual Signal Binding

For full control, skip the dialogue_ui_scene property and connect the component's signals directly to your own UI:

GDScript
@onready var storyflow: StoryFlowComponent = $StoryFlowComponent

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

func _on_dialogue_started():
    # Show your custom dialogue UI
    dialogue_panel.visible = true

func _on_dialogue_updated(state: StoryFlowDialogueState):
    # Update your UI with the new dialogue state
    dialogue_text.text = state.text
    speaker_name.text = state.character.name if state.character else ""

func _on_dialogue_ended():
    # Hide your dialogue UI
    dialogue_panel.visible = false

Which approach should I use?

Use Option A if you want the fastest path to a working dialogue UI - the scene lifecycle is managed for you automatically. Use Option B if you need to integrate dialogue into an existing HUD or have custom scene management requirements.

4. Bind and Display

Whether you use the default UI or a manual binding, you read dialogue data from the StoryFlowDialogueState object. Here are the fields you will work with:

  • title (String) - The dialogue node's title, typically used as a header or scene label
  • text (String) - The main dialogue text, already interpolated with any variable values
  • character (StoryFlowCharacterData) - The speaking character, containing:
    • name (String) - The character's display name
    • image (Texture2D) - The character's portrait texture (may be null)
  • options (Array[StoryFlowDialogueOption]) - The available choices, each containing:
    • id (String) - Unique identifier to pass back when the player selects this option
    • text (String) - The display text for the option button
  • can_advance (bool) - Whether the dialogue can advance without selecting an option (narrative-only nodes)

Here is a complete example of populating a custom dialogue UI:

GDScript
func _on_dialogue_updated(state: StoryFlowDialogueState):
    # Display character info
    if state.character and state.character.name != "":
        speaker_label.text = state.character.name
        speaker_label.visible = true
    else:
        speaker_label.visible = false

    if state.character and state.character.image:
        portrait.texture = state.character.image
        portrait.visible = true
    else:
        portrait.visible = false

    # Display dialogue text
    dialogue_label.text = state.text

    # Build option buttons
    for child in options_container.get_children():
        child.queue_free()

    for option in state.options:
        var button := Button.new()
        button.text = option.text
        button.pressed.connect(_on_option_pressed.bind(option.id))
        options_container.add_child(button)

    # Show "Continue" for narrative-only nodes
    continue_button.visible = state.can_advance and state.options.is_empty()

5. Handle Input

When the player interacts with your dialogue UI, you need to tell the StoryFlow component what happened. There are two functions to call depending on the situation:

Selecting a Choice

When the player clicks an option button, call select_option() with the option's id:

GDScript
func _on_option_pressed(option_id: String):
    storyflow.select_option(option_id)

Advancing Narrative Nodes

Some dialogue nodes have no options - they are purely narrative text that the player reads and then continues. When can_advance is true and options is empty, call advance_dialogue():

GDScript
func _on_continue_pressed():
    storyflow.advance_dialogue()

Input tip

You can bind advance_dialogue() to any input action - a key press, mouse click, or gamepad button. This lets players advance through narrative sections at their own pace.

GDScript
func _unhandled_input(event: InputEvent):
    if event.is_action_pressed("ui_accept") and storyflow.is_dialogue_active():
        var state := storyflow.get_current_dialogue()
        if state.can_advance and state.options.is_empty():
            storyflow.advance_dialogue()

6. Run It

Everything is wired up. Now start the dialogue by calling start_dialogue() on the component:

GDScript
# Start the dialogue (e.g., when the player interacts with an NPC)
storyflow.start_dialogue()

The component emits signals in this order as the script executes:

  1. dialogue_started - Emitted once when start_dialogue() is called. Use this to show your dialogue UI, pause gameplay, or set up camera angles.
  2. dialogue_updated - Emitted each time the runtime reaches a dialogue node. The StoryFlowDialogueState parameter contains all the data you need to update your UI.
  3. dialogue_ended - Emitted when the script reaches an End node. Use this to hide your dialogue UI, resume gameplay, and clean up any dialogue-related state.

Here is a complete example of connecting all three signals:

GDScript
extends Node2D

@onready var storyflow: StoryFlowComponent = $StoryFlowComponent

func _ready() -> void:
    storyflow.dialogue_ended.connect(_on_dialogue_ended)
    storyflow.error_occurred.connect(_on_error)
    storyflow.start_dialogue()

func _on_dialogue_ended() -> void:
    print("Dialogue finished!")

func _on_error(message: String) -> void:
    push_error("StoryFlow error: %s" % message)

You're up and running!

You now have a complete dialogue system in Godot powered by StoryFlow. From here, your workflow is simple: edit your scripts in the StoryFlow Editor, click Sync, and your in-game dialogue updates instantly.

Alternative: Manual Import

If you cannot use Live Sync - for example, if the StoryFlow Editor is running on a different machine without network access, or you are working with pre-exported build files from another team member - you can import manually instead.

  1. In the StoryFlow Editor, click the Export button, select JSON from the format dropdown, choose a destination folder, and click Export. This creates a build directory with the project data, scripts, and media assets.
  2. In the Godot Editor, open the StoryFlow dock panel.
  3. In the Import section, click the Build Directory browse button and select the build folder you just exported.
  4. Set the Output Directory to where you want the imported assets to live (for example, res://storyflow/).
  5. Click Import.

When to use manual import

Manual import creates the same resources as Live Sync. The only difference is the workflow: you export a build directory from the editor and point Godot at it, instead of syncing over WebSocket. For day-to-day development, Live Sync is strongly recommended as it eliminates the export-copy-import cycle entirely.

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