Get Your Personalized Game Dev Plan Tailored tips, tools, and next steps - just for you.

This page may contain affiliate links.

Create Dynamic Dialogue Systems in Unity with Scriptable Objects

Posted by Gemma Ellison
./
July 21, 2025

Are you tired of the same old rigid, hard-coded dialogue systems in your games? Do you dream of crafting intricate branching narratives that react dynamically to player choices? There’s a better way – one that leverages the power and flexibility of Scriptable Objects in Unity. Let’s dive into creating a robust and adaptable dialogue system that will breathe life into your game’s characters and stories.

The Power of Scriptable Objects for Dialogue

Why Scriptable Objects? Because they offer unparalleled flexibility. Instead of burying your dialogue directly in your character scripts (a recipe for disaster!), Scriptable Objects allow you to define dialogue nodes as independent, reusable data containers. This separation of data and logic makes your system incredibly easy to modify, extend, and maintain. Think of them as the building blocks of your narrative.

Creating the Dialogue Node

Let’s start by defining our DialogueNode Scriptable Object. This will hold all the information for a single piece of dialogue: the text to display, the speaker, and any possible responses.

using UnityEngine;
using System.Collections.Generic;

[CreateAssetMenu(fileName = "New Dialogue Node", menuName = "Dialogue/Dialogue Node")]
public class DialogueNode : ScriptableObject
{
    [TextArea(3, 10)] // Allows for multi-line text in the Inspector
    public string dialogueText;
    public string speakerName;
    public List<DialogueResponse> responses;
}

[System.Serializable]
public class DialogueResponse
{
    public string responseText;
    public DialogueNode nextNode;
}

Pitfall: Forgetting the [CreateAssetMenu] attribute. Without this, you won’t be able to easily create new Dialogue Nodes in the Unity editor.

Actionable Insight: Use the TextArea attribute on your dialogueText field for better readability in the Inspector. This is far better than a single-line string.

Building the Dialogue Runner

The DialogueRunner script is responsible for displaying the current dialogue node and handling player input. This script will be attached to a GameObject in your scene (typically your dialogue UI).

using UnityEngine;
using UnityEngine.UI;
using System.Collections.Generic;

public class DialogueRunner : MonoBehaviour
{
    public Text speakerNameText;
    public Text dialogueText;
    public GameObject responseButtonPrefab;
    public Transform responseButtonContainer;

    private DialogueNode currentDialogueNode;

    public void StartDialogue(DialogueNode startingNode)
    {
        currentDialogueNode = startingNode;
        DisplayDialogue();
    }

    private void DisplayDialogue()
    {
        speakerNameText.text = currentDialogueNode.speakerName;
        dialogueText.text = currentDialogueNode.dialogueText;

        // Clear existing response buttons
        foreach (Transform child in responseButtonContainer)
        {
            Destroy(child.gameObject);
        }

        // Create new response buttons
        foreach (DialogueResponse response in currentDialogueNode.responses)
        {
            GameObject button = Instantiate(responseButtonPrefab, responseButtonContainer);
            button.GetComponentInChildren<Text>().text = response.responseText;
            button.GetComponent<Button>().onClick.AddListener(() => ChooseResponse(response.nextNode));
        }
    }

    private void ChooseResponse(DialogueNode nextNode)
    {
        currentDialogueNode = nextNode;
        DisplayDialogue();
    }
}

Common Mistake: Neglecting to clear the existing response buttons before creating new ones. This leads to a cluttered UI and incorrect functionality.

Value Added: Use a ScrollRect for the responseButtonContainer if you anticipate having a large number of responses. This prevents the UI from overflowing.

Implementing Branching Logic

The key to a compelling dialogue system is branching. By adding multiple DialogueResponse options to each DialogueNode, you create different conversation paths based on player choices. Each DialogueResponse contains the text displayed on the button and a reference to the nextNode to transition to when selected.

In the DialogueRunner script, the ChooseResponse function updates the currentDialogueNode and calls DisplayDialogue to show the new dialogue.

Challenge: Managing complex branching scenarios. As your dialogue trees grow, visualizing the connections can become difficult.

Solution: Consider using a visual scripting tool like Bolt or a custom editor script to visualize your dialogue graph. This makes it easier to understand and maintain the flow of conversation.

Connecting the Pieces

  1. Create several DialogueNode Scriptable Objects, each with different text and response options.
  2. Create a DialogueRunner GameObject in your scene and assign the UI elements (speaker name text, dialogue text, response button prefab, and response button container).
  3. Create a script (e.g., InteractionScript) on your NPC that triggers the dialogue when the player interacts with them. This script needs a public DialogueNode field to store the starting node for the dialogue.
  4. In the InteractionScript, assign the starting DialogueNode in the Inspector.
  5. In the InteractionScript, when the player interacts, call the StartDialogue function on the DialogueRunner, passing in the starting DialogueNode.

Real-World Application: Imagine creating a detective game. Each piece of dialogue could unlock new clues, leading the player down different investigation paths. The branching logic allows for multiple endings depending on the choices the player makes.

Beyond the Basics: Expanding Your Dialogue System

This is just the foundation. You can significantly enhance this system by adding:

  • Conditions: Dialogue options that are only available under certain conditions (e.g., based on player stats, inventory items, or quest progress).
  • Effects: Actions that are triggered when a dialogue node is reached (e.g., giving the player an item, starting a quest, or changing the NPC’s disposition).
  • Animations: Trigger animations based on the currently displayed dialogue.
  • Localization: Support for multiple languages.

By using Scriptable Objects and a well-structured DialogueRunner, you can create a flexible and powerful dialogue system that adapts to the needs of your game. Embrace the power of data-driven design and watch your game’s narratives come alive!