🎮 UnityDocs

Sistema de diálogo (con efecto máquina de escribir)

Un sistema de diálogo para NPCs: líneas definidas como datos, un panel de UI con TextMeshPro, el clásico efecto de texto que aparece letra a letra y avanzar con una tecla. Con corrutina.

🍳 RecetaIntermedio✅ RevisadoUnity 6.0 LTSActualizado 2026-07-15
DificultadConceptual Código Matemáticas
Tiempo📖 Lectura 7 min · ⌨️ Práctica 30 min

El objetivo#

Un NPC con el que hablas: aparece un panel, el texto se escribe letra a letra, y avanzas por las líneas pulsando una tecla. Con la primera pulsación completas la línea de golpe (impaciencia), con la segunda pasas a la siguiente.

Paso 1 — El diálogo como dato#

Lo más simple, un array serializable; para conversaciones grandes, un ScriptableObject por conversación:

C#
[System.Serializable]
public class Linea
{
    public string interlocutor;
    [TextArea] public string texto;
}
C#
using System.Collections;
using UnityEngine;
using TMPro;

public class DialogoUI : MonoBehaviour
{
    [SerializeField] private GameObject panel;
    [SerializeField] private TextMeshProUGUI textoNombre;
    [SerializeField] private TextMeshProUGUI textoLinea;
    [SerializeField] private float velocidad = 0.03f;   // segundos por letra

    private Linea[] lineas;
    private int indice;
    private bool escribiendo;
    private Coroutine rutina;

    public void Empezar(Linea[] lineas)
    {
        this.lineas = lineas;
        indice = 0;
        panel.SetActive(true);
        MostrarLinea();
    }

    void Update()
    {
        if (!panel.activeSelf) return;

        if (Input.GetKeyDown(KeyCode.Space) || Input.GetMouseButtonDown(0))
        {
            if (escribiendo) CompletarLinea();   // 1.ª pulsación: completa
            else Avanzar();                       // 2.ª: siguiente línea
        }
    }

    void MostrarLinea()
    {
        Linea l = lineas[indice];
        textoNombre.text = l.interlocutor;
        rutina = StartCoroutine(Escribir(l.texto));
    }

    IEnumerator Escribir(string texto)
    {
        escribiendo = true;
        textoLinea.text = "";
        foreach (char c in texto)
        {
            textoLinea.text += c;
            yield return new WaitForSeconds(velocidad);
        }
        escribiendo = false;
    }

    void CompletarLinea()
    {
        if (rutina != null) StopCoroutine(rutina);
        textoLinea.text = lineas[indice].texto;   // muestra todo de golpe
        escribiendo = false;
    }

    void Avanzar()
    {
        indice++;
        if (indice < lineas.Length) MostrarLinea();
        else Cerrar();
    }

    void Cerrar() => panel.SetActive(false);
}

Paso 3 — Disparar el diálogo desde un NPC#

Con un trigger al acercarte, o al pulsar "usar":

C#
public class NPC : MonoBehaviour
{
    [SerializeField] private DialogoUI dialogoUI;
    [SerializeField] private Linea[] conversacion;

    void OnTriggerEnter2D(Collider2D other)
    {
        if (other.CompareTag("Player"))
            dialogoUI.Empezar(conversacion);
    }
}
El detalle que marca la diferencia: completar de golpe

Poder saltar la animación letra a letra con la primera pulsación es lo que separa un diálogo agradable de uno frustrante. Nadie quiere esperar a que se escriba un párrafo largo. Por eso la primera pulsación completa la línea y la segunda avanza.

Extras habituales#

  • Tags de formato: TextMeshPro admite etiquetas (<color>, <b>, <size>) dentro del texto para dar énfasis.
  • Sonido de escritura: un "bip" corto por cada pocas letras (típico de los RPG).
  • Retratos: mostrar la cara del interlocutor.
  • Ramas de decisión: opciones que llevan a distintas líneas (aquí un ScriptableObject con referencias entre nodos ayuda mucho).
  • Pausar el juego durante el diálogo si procede (ver menú de pausa).

Errores frecuentes#

  • No poder saltar el efecto de escritura (frustra al jugador).
  • No parar la corrutina anterior al avanzar y ver letras "mezclándose".
  • Leer el avance en FixedUpdate en vez de Update.
  • Bloquear el movimiento del jugador y olvidar reactivarlo al cerrar el diálogo.

Resumen#

Define las líneas como datos, muéstralas en un panel con TextMeshPro y escríbelas letra a letra con una corrutina. Permite completar la línea con la primera pulsación y avanzar con la siguiente. Dispáralo desde un NPC con un trigger. Amplíalo con retratos, sonido, formato y ramas de decisión (con ScriptableObjects).

Fuentes y para profundizar