Skip to content
Development
Skill

/dialogue-system

Dialogue tree patterns — ScriptableObject graph, node types (text, choice, condition, event), typewriter effect, localization-ready. Load when implementing NPC conversations.

From plugin
everything-claude-unity
2442 skills20 agents27 commands
Install
$ npx -y skills add XeldarAlz/everything-claude-unity --skill dialogue-system --agent claude-code

How it fires

How this skill gets triggered: by you, by Claude, or both.

  • Fires itselfAuto-invocation. Claude auto-loads it when your prompt matches the work.Auto-invocation is when the right skill fires by itself at the right moment, driven by a FLOW.md router and a hook, instead of you invoking it by name. It is the difference between a skill being installed and a skill actually getting used.Read the full definition →
  • You can call itInvoke it directly when you want it.
  • Slash command/dialogue-system

Context preview

The summary Claude sees to decide when to auto-load this skill.

Dialogue tree patterns — ScriptableObject graph, node types (text, choice, condition, event), typewriter effect, localization-ready. Load when implementing NPC conversations.

SKILL.md

dialogue-system.SKILL.md
name: dialogue-system
description: "Dialogue tree patterns — ScriptableObject graph, node types (text, choice, condition, event), typewriter effect, localization-ready. Load when implementing NPC conversations."
globs: ["**/Dialogue*.cs", "**/Conversation*.cs", "**/NPC*.cs"]

Dialogue System

Patterns for building a node-based dialogue tree system: define conversations as ScriptableObject graphs, process them at runtime with a DialogueRunner, display text with a typewriter effect, and integrate with quest/state systems through condition and event nodes.

Node Architecture

Dialogue is a directed graph of nodes. Each node has a unique ID and a type that determines its behavior.

Base Node

using UnityEngine;

public enum DialogueNodeType
{
    Text,
    Choice,
    Condition,
    Event
}

[System.Serializable]
public class DialogueNode
{
    public string nodeId;
    public DialogueNodeType nodeType;

    // Text node fields
    public string speakerName;
    public string speakerKey;       // Localization key for speaker name
    public Sprite speakerPortrait;
    public string text;
    public string textKey;          // Localization key: "dialogue.npc_greeting.001"
    public string nextNodeId;

    // Choice node fields
    public DialogueChoice[] choices;

    // Condition node fields
    public string conditionKey;     // Game state variable to check
    public string trueNodeId;
    public string falseNodeId;

    // Event node fields
    public string eventName;        // Event to trigger
    public string eventParameter;
    public string eventNextNodeId;
}

Choice Data

[System.Serializable]
public class DialogueChoice
{
    public string choiceText;
    public string choiceKey;        // Localization key
    public string nextNodeId;

    // Optional: conditions for showing this choice
    public string requiredConditionKey;
    public bool hideIfUnavailable;  // false = show grayed out; true = hide entirely
}

Dialogue Tree (ScriptableObject)

using System.Collections.Generic;
using UnityEngine;

[CreateAssetMenu(fileName = "New Dialogue", menuName = "Dialogue/Dialogue Tree")]
public class DialogueTree : ScriptableObject
{
    public string dialogueId;
    public string entryNodeId;
    public List<DialogueNode> nodes = new();

    private Dictionary<string, DialogueNode> _lookup;

    public DialogueNode GetNode(string nodeId)
    {
        if (_lookup == null) BuildLookup();
        _lookup.TryGetValue(nodeId, out var node);
        return node;
    }

    public DialogueNode GetEntryNode()
    {
        return GetNode(entryNodeId);
    }

    private void BuildLookup()
    {
        _lookup = new Dictionary<string, DialogueNode>();
        foreach (var node in nodes)
        {
            if (string.IsNullOrEmpty(node.nodeId)) continue;
            _lookup[node.nodeId] = node;
        }
    }

    private void OnEnable()
    {
        _lookup = null; // Force rebuild on load
    }
}

---

Speaker Definition

Keep speaker data (name, portrait variations, voice settings) in a separate ScriptableObject so multiple dialogues can share the same speaker.

using UnityEngine;

[CreateAssetMenu(fileName = "New Speaker", menuName = "Dialogue/Speaker")]
public class SpeakerDefinition : ScriptableObject
{
    public string speakerId;
    public string displayName;
    public string nameLocKey;
    public Sprite defaultPortrait;
    public Sprite[] emotionPortraits;   // Index by enum: Happy, Sad, Angry, etc.
    public Color nameColor = Color.white;

    [Header("Voice")]
    public AudioClip talkSound;         // Blip sound per character
    public float talkPitch = 1f;
}

public enum SpeakerEmotion
{
    Neutral,
    Happy,
    Sad,
    Angry,
    Surprised,
    Thinking
}

---

Dialogue Runner

The runtime component that processes the dialogue tree node by node. It mediates between the data (DialogueTree) and the presentation (DialogueUI).

using System;
using System.Collections;
using UnityEngine;

public class DialogueRunner : MonoBehaviour
{
    public static DialogueRunner Instance { get; private set; }

    [SerializeField] private DialogueUI dialogueUI;

    private DialogueTree _currentTree;
    private DialogueNode _currentNode;
    private bool _isRunning;

    public bool IsRunning => _isRunning;

    public event Action OnDialogueStarted;
    public event Action OnDialogueEnded;

    private void Awake()
    {
        Instance = this;
    }

    /// <summary>
    /// Start a conversation from the beginning of the given tree.
    /// </summary>
    public void StartDialogue(DialogueTree tree)
    {
        if (_isRunning) return;

        _currentTree = tree;
        _isRunning = true;

        OnDialogueStarted?.Invoke();
        dialogueUI.Show();

        ProcessNode(_currentTree.GetEntryNode());
    }

    private void ProcessNode(DialogueNode node)
    {
        if (node == null)
        {
            EndDialogue();
            return;
        }

        _currentNode = node;

        switch (node.nodeType)
        {
            case DialogueNodeType.Text:
                ProcessTextNode(node);
                break;
            case DialogueNodeType.Choice:
                ProcessChoiceNode(node);
                break;
            case DialogueNodeType.Condition:
                ProcessConditionNode(node);
                break;
            case DialogueNodeType.Event:
                ProcessEventNode(node);
                break;
        }
    }

    private void ProcessTextNode(DialogueNode node)
    {
        string displayText = GetLocalizedText(node.textKey, node.text);
        string displayName = GetLocalizedText(node.speakerKey, node.speakerName);

        dialogueUI.ShowText(displayName, displayText, node.speakerPortrait);
    }

    private void ProcessChoiceNode(DialogueNode node)
    {
        // Filter choices based on conditions
        var availableChoices
Read more
Ships witheverything-claude-unity

The ultimate Claude Code toolkit for Unity game development. A production-ready, plug-and-play system that gives Claude Code deep Unity expertise — from writing performant C# to building scenes, profiling performance, and triggering iOS/Android builds — all

Get the whole plugin

Other skills on everything-claude-unity.