Part of these game systems:
Dialogue Trigger
Simple NPC dialogue with sequential messages, typewriter effect, and input to advance. No branching — just clean text sequences.
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
Source Code
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();
}
}