Dialogue Views
A dialogue view controls how text is presented to the player. Assign a view to the engine's Dialogue View field, or leave it empty to run in headless mode.
Ibralogue ships with two built-in views. You can also create your own by subclassing DialogueViewBase.
Headless / Event-Driven Mode
The dialogue view is optional. When no view is assigned, the engine runs the full dialogue pipeline (parsing, variable resolution, invocations, events) without driving any UI. All typed events (OnLineDisplayed, OnChoicesPresented, OnChoiceSelected) still fire normally.
This is useful for:
- Driving custom UI through events (e.g., UI Toolkit)
- Non-visual dialogue consumers (analytics, quest tracking, testing)
- Systems that only need the data, not the presentation
In headless mode, lines complete instantly (no animation) and choices must be submitted programmatically:
dialogueEngine.OnChoicesPresented.AddListener((choices) =>
{
// Present choices in your own UI, then:
dialogueEngine.SelectChoice(choices[selectedIndex]);
});
TypewriterDialogueView
Reveals text one character at a time, like a typewriter.
| Field | Description |
|---|---|
Character Delay |
Seconds between each character reveal. Default: 0.03. |
Character Window |
Number of characters revealed per step. Default: 1. |
The OnTypewriterEffectUpdated event fires each time characters are revealed. This is useful for playing a typing sound effect.
The delay can also be changed at runtime:
typewriterView.SetCharacterDelay(0.01f);
PunchDialogueView
Reveals text one word at a time.
| Field | Description |
|---|---|
Word Delay |
Seconds between each word reveal. Default: 0.2. |
The OnPunchEffectUpdated event fires each time a new word becomes visible.
punchView.SetWordDelay(0.1f);
Skipping the Effect
Both built-in views support skipping the reveal animation. Call SkipViewEffect() to instantly show all text for the current line:
dialogueView.SkipViewEffect();
Shared Fields
All views inherit these fields from DialogueViewBase:
| Field | Description |
|---|---|
Name Text |
A TextMeshProUGUI component for displaying the speaker name. |
Sentence Text |
A TextMeshProUGUI component for displaying the dialogue text. |
Choice Button Holder |
A Transform that serves as the parent for instantiated choice buttons. |
Choice Button |
A prefab with a ChoiceButton component. Used when choices are displayed. |
The OnSetView event fires every time a new dialogue line is displayed. The OnLineComplete event fires when the view finishes its display effect.
Creating a Custom View
To create your own view, subclass DialogueViewBase and override the relevant methods. The SetView method receives a Line object containing the speaker, text, image, and metadata for the current line:
public class MyCustomView : DialogueViewBase
{
public override void SetView(Line line)
{
base.SetView(line);
// Your custom display logic here
}
public override bool IsStillDisplaying()
{
// Return true while your effect is still running
return false;
}
public override void SkipViewEffect()
{
// Instantly complete your effect
}
public override void ClearView()
{
base.ClearView();
// Any additional cleanup
}
}
The engine calls SetView when a line should be displayed, waits until IsStillDisplaying returns false, and then waits for the player to advance. ClearView is called before each new line and when the conversation ends.
Invocation Timing in Animated Views
When a view reveals text incrementally (like the typewriter or punch views), invocations placed inline in dialogue text fire at their character position as the text is revealed. For example, {{Audio(boom)}} placed mid-sentence fires when the view reaches that point in the text.
With views that display text instantly (like the base DialogueViewBase), all invocations fire immediately since the full text is visible from the start.
Custom views that implement incremental text reveal should update VisibleCharacterCount to enable position-based invocation timing:
public override int VisibleCharacterCount
{
get { return sentenceText.maxVisibleCharacters; }
}
Auto-Advance
Set the Auto-Advance Delay field on the engine to a value greater than zero to make lines advance automatically after the display effect finishes. The delay is in seconds. Choices still require player input.
This is useful for cutscenes or ambient dialogue where no player interaction is needed.
// Enable auto-advance from code
dialogueEngine.AutoAdvanceDelay = 2.0f;
// Disable it
dialogueEngine.AutoAdvanceDelay = 0f;
Player Input
Use Advance() as your single input handler for player clicks, key presses, or taps. It skips the current display effect if a line is still playing, or advances to the next line if idle:
void Update()
{
if (Input.GetKeyDown(KeyCode.Space))
dialogueEngine.Advance();
}
Next() is available if you want the old behavior of only advancing when the current line is finished (no skip).
Custom Views Without TextMeshPro
The base class nameText and sentenceText fields are optional. If your view uses a different text system (UI Toolkit, custom renderer), leave them unassigned and override SetView and ClearView:
public class UIToolkitView : DialogueViewBase
{
private Label _nameLabel;
private Label _textLabel;
public override void SetView(Line line)
{
_nameLabel.text = line.Speaker;
_textLabel.text = line.LineContent.Text;
OnSetView.Invoke();
}
public override void ClearView()
{
_nameLabel.text = "";
_textLabel.text = "";
}
}
Chat / Message UI Pattern
The engine calls ClearView before each new line. For a chat UI that accumulates messages, override ClearView to preserve previous messages:
public class ChatView : DialogueViewBase
{
[SerializeField] private Transform messageContainer;
[SerializeField] private GameObject messagePrefab;
public override void SetView(Line line)
{
var bubble = Instantiate(messagePrefab, messageContainer);
bubble.GetComponentInChildren<TMP_Text>().text =
$"{line.Speaker}: {line.LineContent.Text}";
OnSetView.Invoke();
}
public override void ClearView()
{
// Keep previous messages. Only override if you need
// to clean up transient UI (typing indicators, etc.)
}
}
Customizing the Engine Display Loop
If you need to customize what happens when a line is displayed (beyond what a view provides), you can subclass the engine and override OnDisplayLine:
public class MyEngine : SimpleDialogueEngine
{
protected override IEnumerator OnDisplayLine(Line line)
{
// Custom logic before display
Debug.Log($"{line.Speaker} says: {line.LineContent.Text}");
// Call base to use the standard view/plugin/function pipeline
yield return base.OnDisplayLine(line);
// Custom logic after display
}
}