Part of these game systems:
beginner Dialogue

Dialogue Trigger

Simple NPC dialogue with sequential messages, typewriter effect, and input to advance. No branching — just clean text sequences.

Unity 2022.3+ · 2.5 KB · DialogueTrigger.cs

How to Use

1

Create a Canvas with a dialogue panel (panel, name text, message text)

2

Attach DialogueTrigger to your NPC or sign object

3

Fill in the speaker name and messages array

4

Assign the UI references in inspector

5

Call dialogueTrigger.StartDialogue() from your interaction system

6

Press Space or Click to advance through messages

7

For branching dialogue, use the full Dialogue System instead

Features

  • Sequential message display with configurable speaker name
  • Typewriter text effect with adjustable character speed
  • Click or keypress to skip typewriter or advance to next message
  • UnityEvents for dialogue start, end, and message change
  • Force-close method for interrupting dialogue externally
  • UI panel auto-show/hide on dialogue start and end

When to Use This

Great for RPGs, adventure games, and visual novels that need simple NPC conversations or sign readouts. Use for linear dialogue sequences — tutorial popups, story beats, or item descriptions. For branching choices and conditions, upgrade to the full Dialogue System.

Common Mistakes

You must assign the dialoguePanel, nameText, and messageText references in the Inspector — null references will silently skip display. The script uses TextMeshProUGUI, so make sure you import the TMP Essentials package. Call StartDialogue() from your interaction system; the script doesn't auto-trigger on proximity.

Source Code

DialogueTrigger.cs
C#
using UnityEngine;
using UnityEngine.Events;
using UnityEngine.UI;
using TMPro;
using System.Collections;

/// <summary>
/// Simple sequential dialogue system with typewriter effect.
/// Trigger from NPCs, signs, or any interactable object.
/// </summary>
public class DialogueTrigger : MonoBehaviour
{
    [Header("Dialogue Content")]
    [SerializeField] private string speakerName = "NPC";
    [SerializeField] [TextArea(2, 5)] private string[] messages;

    [Header("UI References")]
    [SerializeField] private GameObject dialoguePanel;
    [SerializeField] private TextMeshProUGUI nameText;
    [SerializeField] private TextMeshProUGUI messageText;

    [Header("Typewriter")]
    [SerializeField] private float typeSpeed = 0.03f;
    [SerializeField] private bool useTypewriter = true;

    [Header("Input")]
    [SerializeField] private KeyCode advanceKey = KeyCode.Space;

    [Header("Events")]
    public UnityEvent OnDialogueStart;
    public UnityEvent OnDialogueEnd;
    public UnityEvent<int> OnMessageChanged;

    private int currentMessageIndex;
    private bool isDialogueActive;
    private bool isTyping;
    private Coroutine typeCoroutine;

    /// <summary>Is a dialogue currently playing?</summary>
    public bool IsActive => isDialogueActive;

    /// <summary>
    /// Start the dialogue sequence.
    /// </summary>
    public void StartDialogue()
    {
        if (isDialogueActive || messages == null || messages.Length == 0) return;

        isDialogueActive = true;
        currentMessageIndex = 0;

        if (dialoguePanel != null)
            dialoguePanel.SetActive(true);

        if (nameText != null)
            nameText.text = speakerName;

        OnDialogueStart?.Invoke();
        ShowMessage(currentMessageIndex);
    }

    private void Update()
    {
        if (!isDialogueActive) return;

        if (Input.GetKeyDown(advanceKey) || Input.GetMouseButtonDown(0))
        {
            if (isTyping)
            {
                // Skip typewriter, show full message
                if (typeCoroutine != null)
                    StopCoroutine(typeCoroutine);
                messageText.text = messages[currentMessageIndex];
                isTyping = false;
            }
            else
            {
                // Advance to next message
                currentMessageIndex++;
                if (currentMessageIndex < messages.Length)
                {
                    ShowMessage(currentMessageIndex);
                }
                else
                {
                    EndDialogue();
                }
            }
        }
    }

    private void ShowMessage(int index)
    {
        OnMessageChanged?.Invoke(index);

        if (useTypewriter)
        {
            if (typeCoroutine != null)
                StopCoroutine(typeCoroutine);
            typeCoroutine = StartCoroutine(TypeMessage(messages[index]));
        }
        else
        {
            messageText.text = messages[index];
        }
    }

    private IEnumerator TypeMessage(string message)
    {
        isTyping = true;
        messageText.text = "";

        foreach (char c in message)
        {
            messageText.text += c;
            yield return new WaitForSecondsRealtime(typeSpeed);
        }

        isTyping = false;
    }

    private void EndDialogue()
    {
        isDialogueActive = false;

        if (dialoguePanel != null)
            dialoguePanel.SetActive(false);

        OnDialogueEnd?.Invoke();
    }

    /// <summary>
    /// Force-close the dialogue.
    /// </summary>
    public void ForceClose()
    {
        if (typeCoroutine != null)
            StopCoroutine(typeCoroutine);
        EndDialogue();
    }
}