Dialogue Trigger
Simple NPC dialogue with sequential messages, typewriter effect, and input to advance. No branching — just clean text sequences.
How to Use
Create a Canvas with a dialogue panel (panel, name text, message text)
Attach DialogueTrigger to your NPC or sign object
Fill in the speaker name and messages array
Assign the UI references in inspector
Call dialogueTrigger.StartDialogue() from your interaction system
Press Space or Click to advance through messages
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
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();
}
}