Handling Choices
Dialogue nodes can present the player with multiple choices, typed input fields, and conditionally visible options. This guide covers how to read, display, and respond to every kind of player interaction in a StoryFlow dialogue.
Choice Options
When a dialogue node has options, they are delivered through the FStoryFlowDialogueState that your UI receives on every dialogue update. Each option is represented by an FStoryFlowDialogueOption struct with two fields:
- Id (
FString) — A unique identifier for this option, used when callingSelectOption - Text (
FString) — The display text for the option, already fully resolved with variable interpolation
You read options from the Options array on FStoryFlowDialogueState:
// Inside your dialogue widget or HUD class
void UMyDialogueWidget::OnDialogueUpdated(const FStoryFlowDialogueState& State)
{
// Clear previous option buttons
OptionsContainer->ClearChildren();
// Create a button for each available option
for (const FStoryFlowDialogueOption& Option : State.Options)
{
UMyOptionButton* Button = CreateWidget<UMyOptionButton>(this);
Button->SetOptionText(Option.Text);
Button->SetOptionId(Option.Id);
Button->OnClicked.AddDynamic(this, &UMyDialogueWidget::OnOptionClicked);
OptionsContainer->AddChild(Button);
}
} Pre-Filtered Options
The Options array only contains options the player should see right now. Hidden options (those that fail their visibility condition or have already been used as once-only) are automatically excluded. Your UI simply iterates the array without any additional filtering.
Selecting an Option
When the player clicks a choice, call SelectOption with the option's Id. You can call this from either the component or a dialogue widget:
// Option 1: Call directly on the StoryFlow component
StoryFlowComponent->SelectOption(OptionId);
// Option 2: Call from a UStoryFlowDialogueWidget subclass
// (internally forwards to the bound component)
SelectOption(OptionId);
When SelectOption is called, the runtime follows the dialogue node's output edge that matches the selected option. Internally, the source handle format is source-{nodeId}-{optionId}, but you never need to construct this yourself — just pass the Id from FStoryFlowDialogueOption.
Here is a complete example wiring a button click to option selection:
void UMyDialogueWidget::OnOptionClicked(const FString& OptionId)
{
// Tell the runtime the player picked this option
SelectOption(OptionId);
// The component will fire OnDialogueUpdated with the next
// dialogue state, or OnDialogueEnded if the story is over.
} Variable Re-rendering
If an option's edge leads to a Set* node (e.g., setBool, setInt) that has no outgoing edge, the runtime automatically returns to the current dialogue and re-renders it with the updated variable values. This enables live variable interpolation — for example, a toggle option that updates displayed text without leaving the dialogue.
Advancing Narrative-Only Dialogues
Not every dialogue presents choices. Narrative-only dialogues display text (and optionally a character, image, or audio) but have no selectable options. You can detect this state and show a "Continue" or "Next" button instead.
A dialogue is narrative-only when:
FStoryFlowDialogueState.bCanAdvanceistrueFStoryFlowDialogueState.Optionsis empty
void UMyDialogueWidget::OnDialogueUpdated(const FStoryFlowDialogueState& State)
{
// Update speaker name, dialogue text, portrait, etc.
UpdateDialogueDisplay(State);
if (State.Options.Num() == 0 && State.bCanAdvance)
{
// No options — show a "Continue" button
ContinueButton->SetVisibility(ESlateVisibility::Visible);
OptionsContainer->SetVisibility(ESlateVisibility::Collapsed);
}
else
{
// Has options — show the option buttons
ContinueButton->SetVisibility(ESlateVisibility::Collapsed);
OptionsContainer->SetVisibility(ESlateVisibility::Visible);
PopulateOptions(State.Options);
}
}
void UMyDialogueWidget::OnContinueClicked()
{
// Advance uses the dialogue node's header output edge
AdvanceDialogue();
} Once-Only Options
Options can be marked as once-only in the StoryFlow Editor. After a player selects a once-only option, it is permanently hidden from future displays of that dialogue node. This is useful for:
- One-time dialogue branches ("Ask about the artifact" disappears after asking)
- Unlockable conversations that are consumed on use
- First-encounter dialogue that should not repeat
- Exhaustible question lists where the player works through available topics
The runtime tracks used once-only options via UStoryFlowSubsystem::GetUsedOnceOnlyOptions(), a TSet<FString> where each entry is keyed as nodeId-optionId.
// Check if a specific once-only option has been used
UStoryFlowSubsystem* Subsystem = GetGameInstance()->GetSubsystem<UStoryFlowSubsystem>();
FString Key = FString::Printf(TEXT("%s-%s"), *NodeId, *OptionId);
bool bAlreadyUsed = Subsystem->GetUsedOnceOnlyOptions().Contains(Key);
// To reset a specific once-only option (e.g., for New Game+)
Subsystem->GetUsedOnceOnlyOptions().Remove(Key);
// To reset all once-only tracking
Subsystem->GetUsedOnceOnlyOptions().Empty(); Once-Only Persistence
Once-only option tracking persists across dialogue sessions and survives save/load cycles. When you call the save functions on UStoryFlowSubsystem, the UsedOnceOnlyOptions set is included in the serialized state. This means a player who saves after exhausting a dialogue option will not see it again when they load that save. If you need to reset once-only tracking (for example, when starting a new game), clear the set before beginning the new session.
Input Options
Beyond simple clickable choices, the StoryFlow Editor supports typed input fields on dialogue nodes — including string, integer, float, boolean, and enum inputs. These allow players to enter values directly rather than choosing from predefined options.
Not Yet Available in Unreal
Typed input options are a feature of the StoryFlow Editor's built-in runtime. Support for input options in the Unreal Engine plugin is planned for a future release. Currently, only standard button-style choice options are supported via SelectOption. If your StoryFlow project uses input options, you will need to implement custom UI handling and map user input to variable changes using the component's SetBoolVariable, SetIntVariable, SetStringVariable, and other typed setter functions.
Conditional Visibility
Options in the StoryFlow Editor can have visibility conditions connected to them. These are boolean node chains (using Get, And, Or, Not, and comparison nodes) that determine whether an option should appear at runtime.
The runtime evaluates these conditions automatically through EvaluateOptionVisibility() every time the dialogue is rendered. Options that fail their visibility check are excluded from the Options array before it reaches your UI.
// You do NOT need to check visibility yourself.
// The Options array is already filtered:
for (const FStoryFlowDialogueOption& Option : State.Options)
{
// Every option here has passed its visibility check.
// Hidden options are simply not in the array.
CreateOptionButton(Option);
}
// Example scenario:
// - "Buy Sword (50 gold)" only visible when player has >= 50 gold
// - "Enter VIP Room" only visible when bHasVIPPass is true
// - "Ask about the map" hidden after it's been selected (once-only)
// All of this is handled before Options reaches your widget. Because visibility conditions are re-evaluated on every render, they respond to variable changes in real time. If a Set* node updates a variable that a visibility condition depends on and then returns to the dialogue, the option list will reflect the new state immediately.
Next Steps
Now that you understand how to handle choices, learn how to work with Variables to create dynamic conditions and persistent state, or explore Characters to display speaker portraits and names alongside your dialogue options.