Handling Choices
Dialogue nodes can present multiple choices with once-only behavior and visibility conditions. This guide covers displaying those choices, handling selection and connecting your own input controls to story variables.
Options Overview
When a dialogue node has options, they are delivered through the StoryFlowDialogueState that your UI receives on every dialogue update. Each option is represented by a StoryFlowOption with the following fields:
- Id (
string) - A unique identifier for this option, used when callingSelectOption() - Text (
string) - The display text, already fully resolved with variable interpolation - IsOnceOnly (
bool) - Whether this option disappears after being selected - IsSelected (
bool) - Reserved field for tracking whether this option has been previously selected. Currently not populated by the runtime (alwaysfalse). - InputType (
string) - A compatibility field for older dialogue input data. Empty for current button options - DefaultValue (
string) - The input default from older dialogue data. Not populated by current exports
You read options from the Options list on StoryFlowDialogueState:
// Inside your dialogue UI
public void HandleDialogueUpdated(StoryFlowDialogueState state)
{
// Clear previous option buttons
foreach (Transform child in optionsContainer)
Destroy(child.gameObject);
// Create a button for each available option
foreach (var option in state.Options)
{
var buttonObj = Instantiate(optionButtonPrefab, optionsContainer);
var label = buttonObj.GetComponentInChildren<TextMeshProUGUI>();
label.text = option.Text;
string capturedId = option.Id;
buttonObj.GetComponent<Button>().onClick.AddListener(
() => OnOptionClicked(capturedId));
}
} Pre-Filtered Options
The Options list 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 by the runtime. Your UI simply iterates the list without any additional filtering.
Selecting Options
When the player clicks a choice, call SelectOption() with the option's Id. You can call this on the component directly or use the convenience method from a StoryFlowDialogueUI subclass:
// Option 1: Call directly on the StoryFlowComponent
storyFlowComponent.SelectOption(optionId);
// Option 2: Call from a StoryFlowDialogueUI 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 StoryFlowOption.
Here is a complete example wiring a button click to option selection:
private void OnOptionClicked(string optionId)
{
// Tell the runtime the player picked this option
storyFlowComponent.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 Nodes
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:
state.CanAdvanceistruestate.Options.Countis0
public void HandleDialogueUpdated(StoryFlowDialogueState state)
{
// Update speaker name, dialogue text, portrait, etc.
UpdateDialogueDisplay(state);
if (state.Options.Count == 0 && state.CanAdvance)
{
// No options - show a "Continue" button
continueButton.gameObject.SetActive(true);
optionsContainer.gameObject.SetActive(false);
}
else
{
// Has options - show the option buttons
continueButton.gameObject.SetActive(false);
optionsContainer.gameObject.SetActive(true);
PopulateOptions(state.Options);
}
}
private void OnContinueClicked()
{
// Advance uses the dialogue node's header output edge
storyFlowComponent.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 the StoryFlowManager's internal UsedOnceOnlyOptions set, where each entry is keyed as nodeId-optionId. You do not need to manage this tracking yourself - it is handled automatically when SelectOption() is called.
// Once-only tracking is automatic. When SelectOption() is called
// for an option where IsOnceOnly is true, the runtime records
// the selection and excludes it from future renders.
// To reset once-only tracking (e.g., for New Game+),
// call ResetAllState which clears globals, characters, AND once-only options:
StoryFlowManager.Instance.ResetAllState(); Once-Only Persistence
Once-only option tracking persists across dialogue sessions within the same game session and survives save/load cycles. When you call StoryFlowManager.Instance.SaveToSlot(), the used once-only options are included in the serialized data. If you need to reset once-only tracking (for example, when starting a new game), call ResetAllState() or clear the set before beginning the new session.
Player Input
Current StoryFlow Editor dialogue nodes provide choice buttons. To collect a player name, number or other input in Unity, create the control in your engine UI and pass its value to a typed variable setter on the StoryFlowComponent, such as SetStringVariable. See Variables for the available setters and local or global scope.
For games played in StoryFlow Editor or exported as HTML or Desktop Apps, use the editor's User Interface input elements. Those elements are separate from dialogue options and are not imported as engine UI by the plugin.
// Define a global String variable named playerName in StoryFlow Editor.
// Call this when the player confirms the name in your Unity UI.
storyFlowComponent.SetStringVariable("playerName", inputField.text, true);
The plugin retains InputType, DefaultValue and InputChanged for older dialogue input data. Current exports do not create those input options.
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 every time the dialogue is rendered. Options that fail their visibility check are excluded from the Options list before it reaches your UI.
// You do NOT need to check visibility yourself.
// The Options list is already filtered:
foreach (var option in state.Options)
{
// Every option here has passed its visibility check.
// Hidden options are simply not in the list.
CreateOptionButton(option);
}
// Example scenario:
// - "Buy Sword (50 gold)" only visible when player has >= 50 gold
// - "Enter VIP Room" only visible when hasVIPPass is true
// - "Ask about the map" hidden after it has been selected (once-only)
// All of this is handled before Options reaches your UI. 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.