How to Implement a Simple Dialogue System in Unity
You want NPCs that talk. A full narrative engine can wait; a simple dialogue system gets you lines on screen, a bit of pacing, and optional choices so players feel like they are having a conversation. This guide walks you through a minimal Unity dialogue system using ScriptableObjects, a typewriter effect, and basic choice handling you can ship in a weekend.
What You Will Build
- A dialogue data model (ScriptableObject or simple class) to hold lines and optional choices
- A dialogue UI that shows the current line and advances on input
- An optional typewriter effect so text appears character by character
- Optional choices that branch to the next line or trigger events
- A trigger (collider or script) that starts dialogue when the player interacts
No external packages required; everything uses built-in Unity and C#.
Step 1: Define Your Dialogue Data
You need a way to store lines and, if you want choices, what comes next. Two common approaches:
Option A: ScriptableObject (recommended)
Create a script that defines a dialogue "asset" (a sequence of lines). Each line has text and an optional list of choices; each choice has label and a reference to the next dialogue or a simple index.
Option B: Serialized class in a MonoBehaviour
Store an array of lines (and choices) on a component. Easier to edit in the Inspector for one-off conversations; ScriptableObjects are better when you have many dialogues and want to reuse or reference them.
Example structure for a single line with optional choices:
[System.Serializable]
public class DialogueLine
{
[TextArea(2, 4)]
public string text;
public string speakerName; // optional
public DialogueChoice[] choices; // empty = single "Continue" flow
}
[System.Serializable]
public class DialogueChoice
{
public string label;
public int nextLineIndex; // or reference to another ScriptableObject
}
Create a ScriptableObject that holds an array of DialogueLine. In the Unity menu: Assets > Create > YourGame > Dialogue. This becomes your dialogue asset (e.g. GuardGreeting).
Pro tip: Keep the first version without choices. Get "show one line, advance to next" working, then add choices so you do not debug two things at once.
Step 2: Create the Dialogue UI
Add a Canvas with a panel that holds:
- A Text (or TextMeshPro) for the speaker name (optional)
- A Text (or TextMeshPro) for the line content
- A Button or prompt like "Press E to continue" (or rely on key/click)
Position the panel where you want dialogue (e.g. bottom of screen, or a speech bubble). Disable the panel by default; your dialogue manager will enable it when dialogue starts and update the text.
Pro tip: Use a single dialogue panel and one manager in the scene. Avoid one panel per NPC so you do not have to manage multiple canvases.
Step 3: Dialogue Manager Script
Create a script (e.g. DialogueManager) that:
- Holds a reference to your dialogue UI (the panel and text fields)
- Has a method like
StartDialogue(YourDialogueScriptableObject dialogue)that receives the dialogue asset, sets the current line index to 0, enables the panel, and shows the first line - Has a method like
ShowNextLine()that increments the index, reads the next line from the asset, and updates the UI text. If there is no next line, close the panel and optionally fire an event so the game can resume (e.g. disable a "in dialogue" flag) - Listens for input (e.g. key press or button click) to call
ShowNextLine()
Keep state in the manager: current dialogue asset, current line index. When the index is past the last line, end the dialogue.
Common mistake: Forgetting to disable the panel or reset state when dialogue ends. Always disable the panel and clear or reset the current dialogue so the next trigger works.
Step 4: Typewriter Effect (Optional)
Instead of showing the full line at once, reveal it character by character for a nicer feel. In your manager (or a separate component on the text element):
- When you set a new line, start a coroutine that appends one character (or a few) to the visible text every frame or on a timer.
- Store the "full" line and the "displayed so far" length. When the player presses continue before the line is complete, you can either finish the line instantly or advance to the next line; both are valid. Many games finish the line on early input.
- Use a yield return (e.g.
WaitForSeconds(0.02f)) or deltaTime so the speed is configurable.
Pro tip: Make the typewriter speed a public field or set it per line so designers can slow down for dramatic lines.
Step 5: Choices
When the current line has choices, do not show "Press to continue." Instead, show a set of buttons (one per choice). When the player clicks a choice:
- Read the
nextLineIndex(or next dialogue reference) for that choice - Set the current line index to that value (or load the referenced dialogue and set index to 0)
- Hide the choice buttons and show the next line as usual
If you use indices, "next line" can be the next in the same asset or a sentinel value that means "end dialogue" or "load another asset." Keep it simple at first: one asset, indices only.
Step 6: Triggering Dialogue
Use a trigger (e.g. OnTriggerEnter2D or OnTriggerEnter) or a raycast/interaction script. When the player interacts:
- Get the dialogue asset (e.g. from a field on the trigger GameObject, or from a component on the NPC)
- Call
DialogueManager.Instance.StartDialogue(dialogue)(or pass the manager reference) - Optionally disable player movement or input while dialogue is active; re-enable when the manager ends the dialogue (event or callback)
Pro tip: Use a simple event or delegate so the manager can notify "dialogue ended" and the game can re-enable controls or start a quest step.
Common Mistakes to Avoid
- No end condition: Always handle "no more lines" so the panel closes and state resets.
- Null references: If a choice points to a missing or wrong index, guard with a check or default to "end dialogue."
- UI blocks input: Ensure your continue button or key is not blocked by another UI element. Use correct raycast and event settings on the EventSystem.
Troubleshooting
Dialogue does not start
Check that the trigger or interaction script has a reference to the dialogue asset and that it calls the manager. Ensure the manager enables the panel and sets the text.
Text does not update
Confirm you are assigning to the correct Text component and that the dialogue asset has the expected lines. Add a debug log to verify the line index and line content.
Choices do not appear
Ensure the line has choices and that your UI instantiates or shows choice buttons from that data. Hook each button to the correct nextLineIndex or next dialogue.
Frequently Asked Questions
Do I need a plugin for dialogue in Unity?
No. A simple system with ScriptableObjects, a manager, and a UI is enough for many games. Plugins like Dialogue System for Unity or Yarn Spinner help when you need complex branching, localization, or integration with narrative tools.
Should I use Text or TextMeshPro?
TextMeshPro (TMP) gives better quality and performance for lots of text. Unity’s legacy Text is fine for a prototype. You can switch to TMP later without changing your data model.
How do I add voice or sound per line?
Add an optional AudioClip field to your line data. When showing a line, play the clip if present. Your manager or a separate audio component can handle playback.
How do I save which dialogue the player has seen?
Store a list of dialogue IDs or line indices in your save data. When loading a dialogue, skip lines or branches the player has already seen, or show a different asset for "already talked" state.
Next Steps
Once this is in place, you can extend with: multiple speakers (switch name and maybe portrait), conditional branches based on inventory or flags, and localization by storing string keys instead of final text and looking them up at runtime. For more Unity systems, see our Unity Game Engine guide and input system and UI posts.
Found this useful? Bookmark it for your next project or share it with your team.