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 in your Unreal Engine 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. Export Your Project

Before you can use your story in Unreal Engine, you need to export it from the StoryFlow Editor as a JSON build.

  1. Open your project in StoryFlow Editor
  2. Go to File > Export > JSON Export
  3. Choose a destination folder and click Export

The exporter creates a build directory at your chosen location with the following structure:

  • project.json — Project metadata including name, language, characters, and a manifest of all scripts
  • scripts/ — One .json file per script containing nodes, edges, and variables
  • media/ — Any images and audio files referenced by your project (textures, sound effects, etc.)

Keep the build directory intact

The importer reads all files relative to the build directory root. Do not rename or rearrange the exported files — pass the entire build directory path to the importer and it will handle the rest.

2. Import into Unreal

There are two ways to import your exported project into Unreal Engine, depending on whether you prefer Blueprint or C++.

Option A: Blueprint Library

Call the static function ImportProject from UStoryFlowImporter. This is the easiest approach and works from any Blueprint graph.

C++
// BuildDirectory: absolute path to the exported build folder
// ContentPath: where assets will be created in your Content Browser (optional)
UStoryFlowImporter::ImportProject(
    TEXT("C:/MyGame/StoryFlowExport/build"),
    TEXT("/Game/StoryFlow")  // defaults to "/Game/StoryFlow" if omitted
);

The ContentPath parameter controls where the imported assets are placed inside your Unreal project's Content directory. If you omit it or pass an empty string, assets are created under /Game/StoryFlow by default.

Option B: Editor Subsystem

If you prefer to use the Editor Subsystem (useful for custom editor tools or automation), you can call ImportProject directly:

C++
UStoryFlowEditorSubsystem* Subsystem =
    GEditor->GetEditorSubsystem<UStoryFlowEditorSubsystem>();

Subsystem->ImportProject(TEXT("C:/MyGame/StoryFlowExport/build"));

What Gets Created

After import, the following assets appear in your Content Browser:

  • SF_Project (UStoryFlowProjectAsset) — The root project asset containing metadata and references to all scripts and characters
  • One UStoryFlowScriptAsset per script — Each script file from your StoryFlow Editor project becomes its own asset
  • One UStoryFlowCharacterAsset per character — Character definitions including name and portrait reference
  • Media assets — Images are imported as UTexture2D and audio files as USoundWave

Audio format note

For best results, export audio from StoryFlow Editor in WAV format. MP3 files are imported as FileMediaSource rather than USoundWave, which limits playback options. WAV files import directly as USoundWave and work with all Unreal audio APIs.

3. Add the Component

The UStoryFlowComponent is the runtime that drives dialogue execution. Add it to any actor that needs to run a StoryFlow script — typically your player character, a dialogue manager actor, or an NPC.

  1. Select your actor in the level or open its Blueprint
  2. Click Add Component and search for "StoryFlow"
  3. Add the StoryFlow Component

In the Details panel, configure these properties:

  • Script — Select which script to run from the dropdown. This list is populated by GetAvailableScripts and shows all scripts in the project asset.
  • LanguageCode — The language to use for dialogue text. Defaults to "en". Set this to match the language code used in your StoryFlow Editor project.

You can also set these in C++:

C++
// In your actor's constructor or BeginPlay
UStoryFlowComponent* StoryFlow = CreateDefaultSubobject<UStoryFlowComponent>(TEXT("StoryFlow"));

// Or find it on an existing actor
UStoryFlowComponent* StoryFlow = Actor->FindComponentByClass<UStoryFlowComponent>();

4. Create a Basic UI

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

Option A: Extend the Built-in Widget (Recommended)

Create a new Widget Blueprint that extends UStoryFlowDialogueWidget. This base class provides built-in hooks for dialogue updates so you only need to design the visual layout.

  1. In the Content Browser, right-click and choose User Interface > Widget Blueprint
  2. When prompted for a parent class, search for and select StoryFlowDialogueWidget
  3. Design your dialogue UI — add text blocks for the speaker name, dialogue text, and a vertical box for option buttons
  4. On your UStoryFlowComponent, set the DialogueWidgetClass property to your new widget

When dialogue starts, the component automatically creates an instance of your widget and adds it to the viewport. When dialogue ends, the widget is removed.

Option B: Manual Delegate Binding

For full control, skip the widget class and bind directly to the component's delegates:

C++
// Bind to the OnDialogueUpdated delegate
StoryFlowComponent->OnDialogueUpdated.AddDynamic(
    this, &AMyActor::HandleDialogueUpdated);

// Your handler receives the current dialogue state
void AMyActor::HandleDialogueUpdated(const FStoryFlowDialogueState& State)
{
    // Update your custom UI here
    MyWidget->SetDialogueText(State.Text);
    MyWidget->SetSpeakerName(State.Character.Name);
}

Which approach should I use?

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

5. Bind and Display

Whether you use the built-in widget or a manual binding, you read dialogue data from the FStoryFlowDialogueState struct. Here are the fields you will work with:

  • Title (FString) — The dialogue node's title, typically used as a header or scene label
  • Text (FString) — The main dialogue text, already interpolated with any variable values
  • Character (FStoryFlowCharacterData) — The speaking character, containing:
    • Name (FString) — The character's display name
    • Image (UTexture2D*) — The character's portrait texture (may be null)
  • Options (TArray<FStoryFlowDialogueOption>) — The available choices, each containing:
    • Id (FString) — Unique identifier to pass back when the player selects this option
    • Text (FString) — The display text for the option button
  • bCanAdvance (bool) — Whether the dialogue can advance without selecting an option (narrative-only nodes)

Here is a complete example of populating a dialogue widget:

C++
void UMyDialogueWidget::UpdateDialogue(const FStoryFlowDialogueState& State)
{
    // Display character info
    if (SpeakerNameText)
    {
        SpeakerNameText->SetText(FText::FromString(State.Character.Name));
    }
    if (CharacterPortrait && State.Character.Image)
    {
        CharacterPortrait->SetBrushFromTexture(State.Character.Image);
        CharacterPortrait->SetVisibility(ESlateVisibility::Visible);
    }

    // Display dialogue text
    if (DialogueText)
    {
        DialogueText->SetText(FText::FromString(State.Text));
    }

    // Build option buttons
    if (OptionsContainer)
    {
        OptionsContainer->ClearChildren();

        for (const FStoryFlowDialogueOption& Option : State.Options)
        {
            UButton* Button = CreateOptionButton(Option.Text, Option.Id);
            OptionsContainer->AddChild(Button);
        }
    }

    // Show a "Continue" prompt for narrative-only nodes
    if (ContinueButton)
    {
        bool bShowContinue = State.bCanAdvance && State.Options.Num() == 0;
        ContinueButton->SetVisibility(
            bShowContinue ? ESlateVisibility::Visible : ESlateVisibility::Collapsed);
    }
}

6. 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 SelectOption with the option's Id:

C++
// Called when the player clicks an option button
void UMyDialogueWidget::OnOptionClicked(const FString& OptionId)
{
    UStoryFlowComponent* StoryFlow =
        GetOwningPlayerPawn()->FindComponentByClass<UStoryFlowComponent>();

    if (StoryFlow)
    {
        StoryFlow->SelectOption(OptionId);
    }
}

Advancing Narrative Nodes

Some dialogue nodes have no options — they are purely narrative text that the player reads and then continues. When bCanAdvance is true and Options is empty, call AdvanceDialogue:

C++
// Called when the player clicks "Continue" or presses a key
void UMyDialogueWidget::OnContinueClicked()
{
    UStoryFlowComponent* StoryFlow =
        GetOwningPlayerPawn()->FindComponentByClass<UStoryFlowComponent>();

    if (StoryFlow)
    {
        StoryFlow->AdvanceDialogue();
    }
}

Input tip

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

7. Run It

Everything is wired up. Now start the dialogue by calling StartDialogue on the component:

C++
// Start the dialogue (e.g., when the player interacts with an NPC)
StoryFlowComponent->StartDialogue();

The component fires events in this order as the script executes:

  1. OnDialogueStarted — Fired once when StartDialogue is called. Use this to show your dialogue UI, pause gameplay, or set up camera angles.
  2. OnDialogueUpdated — Fired each time the runtime reaches a dialogue node. The FStoryFlowDialogueState parameter contains all the data you need to update your UI.
  3. OnDialogueEnded — Fired 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 binding all three events:

C++
void AMyNPC::BeginPlay()
{
    Super::BeginPlay();

    UStoryFlowComponent* StoryFlow = FindComponentByClass<UStoryFlowComponent>();
    if (!StoryFlow) return;

    StoryFlow->OnDialogueStarted.AddDynamic(this, &AMyNPC::OnDialogueStarted);
    StoryFlow->OnDialogueUpdated.AddDynamic(this, &AMyNPC::OnDialogueUpdated);
    StoryFlow->OnDialogueEnded.AddDynamic(this, &AMyNPC::OnDialogueEnded);
}

void AMyNPC::OnDialogueStarted()
{
    // Show dialogue UI, disable player movement, etc.
    UE_LOG(LogTemp, Log, TEXT("Dialogue started"));
}

void AMyNPC::OnDialogueUpdated(const FStoryFlowDialogueState& State)
{
    // Update the dialogue widget with new state
    if (DialogueWidget)
    {
        DialogueWidget->UpdateDialogue(State);
    }
}

void AMyNPC::OnDialogueEnded()
{
    // Hide dialogue UI, re-enable player movement, etc.
    UE_LOG(LogTemp, Log, TEXT("Dialogue ended"));
}

You're up and running!

You now have a complete dialogue system in Unreal Engine powered by StoryFlow. Edit your scripts in the StoryFlow Editor, re-export, re-import, and your in-game dialogue updates automatically.

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