tutoriales.com

Creando un Sistema de Diálogos Ramificados en Unity 🗣️ RPGs y Aventuras Narrativas

Este tutorial te guiará paso a paso en la creación de un sistema de diálogos ramificados robusto y flexible en Unity. Descubre cómo manejar nodos de conversación, opciones de respuesta, variables de juego y la integración con la UI para potenciar la narrativa de tus RPGs y juegos de aventura.

Intermedio25 min de lectura3 views20 de marzo de 2026Reportar error

¡Hola, intrépido desarrollador de videojuegos! 👋 ¿Alguna vez has querido que tus personajes no solo hablen, sino que sus palabras tengan peso y sus conversaciones ofrezcan múltiples caminos? En este tutorial, nos sumergiremos en el fascinante mundo de los sistemas de diálogos ramificados en Unity.

Un buen sistema de diálogo es el corazón de muchos géneros, desde los intensos RPGs hasta las emotivas novelas visuales. No solo permite que tus personajes se comuniquen, sino que también ofrece al jugador la oportunidad de influir en la historia, tomar decisiones significativas y sentirse verdaderamente inmerso en tu mundo. Prepárate para darle voz a tus creaciones y dotarlas de interacciones memorables.

🎯 ¿Qué aprenderemos en este tutorial?

Al finalizar este tutorial, serás capaz de:

  • Diseñar la estructura lógica de un sistema de diálogos ramificados.
  • Implementar un editor visual básico para crear y conectar nodos de diálogo.
  • Integrar el sistema con la interfaz de usuario de Unity (UI).
  • Manejar la lógica de ramificación y las opciones de respuesta del jugador.
  • Incorporar variables y condiciones para diálogos dinámicos.
  • Entender cómo guardar y cargar el progreso del diálogo.

📖 Conceptos Fundamentales de los Sistemas de Diálogo 🧠

Antes de sumergirnos en el código, es crucial entender los componentes básicos de un sistema de diálogo ramificado.

Nodos de Diálogo (Dialogue Nodes)

Imagina que cada segmento de texto que dice un personaje es un nodo. Cada nodo puede tener un personaje asociado, el texto en sí y, opcionalmente, una serie de opciones de respuesta que el jugador puede elegir.

💡 Consejo: Piensa en los nodos como "párrafos" o "frases" que se presentan al jugador.

Opciones de Respuesta (Player Choices)

Estas son las selecciones que el jugador puede hacer. Cada opción de respuesta tiene su propio texto y, crucialmente, apunta a otro nodo de diálogo. Aquí es donde entra la ramificación.

Conexiones (Connections/Edges)

Las conexiones son las flechas que unen los nodos. Representan el flujo de la conversación, desde un nodo de diálogo a una opción de respuesta, y de una opción de respuesta a otro nodo de diálogo.

Nodo A (Personaje: Texto) Opción 1 Opción 2 Nodo B (Personaje: Texto) Nodo C (Personaje: Texto) Nodo D (Personaje: Texto) Fin

Variables y Condiciones (Variables & Conditions)

Para diálogos realmente dinámicos, necesitamos variables (por ejemplo, karma, reputacionConPersonaje, itemObtenido) que se puedan modificar durante la conversación y condiciones que determinen si una opción de diálogo o un camino está disponible para el jugador.

🔥 Importante: Las variables permiten que tus diálogos "recuerden" las acciones del jugador, haciendo la narrativa más inmersiva y reactiva.

🛠️ Configurando el Entorno en Unity

Antes de empezar a codificar, vamos a preparar un nuevo proyecto de Unity.

  1. Crear un Nuevo Proyecto: Abre Unity Hub y crea un nuevo proyecto 3D o 2D (el sistema funciona en ambos). Nómbralo SistemaDialogoUnity.
  2. Configurar la UI: Necesitaremos un Canvas para mostrar el texto y las opciones de diálogo. Crea un nuevo Canvas (GameObject > UI > Canvas). Configura su Render Mode a Screen Space - Camera y arrastra tu Main Camera al slot. Ajusta UI Scale Mode a Scale With Screen Size con una resolución de referencia (e.g., 1920x1080).
  3. Elementos de UI Necesarios: Dentro del Canvas, crea los siguientes elementos:
    • Un Panel (para el fondo del cuadro de diálogo).
    • Un Text - TextMeshPro (para el texto del diálogo del personaje). Importa los recursos de TMP si te lo pide.
    • Un Vertical Layout Group (para contener las opciones de respuesta).
    • Un Button - TextMeshPro (como prefab para cada opción de respuesta). Este botón será instanciado dinámicamente.
📌 Nota: Asegúrate de que el Panel, el Text y el Vertical Layout Group estén bien anclados y dimensionados para que se vean bien en diferentes resoluciones.

✨ Estructura de Datos (Scriptable Objects) 📂

Usaremos Scriptable Objects para definir nuestros nodos de diálogo y sus conexiones. Esto nos permitirá crear diálogos directamente en el Inspector de Unity, sin necesidad de un editor visual complejo desde el principio.

DialogueNode.cs

Este Scriptable Object representará un único segmento de diálogo.

using UnityEngine; 
using System.Collections.Generic;

[CreateAssetMenu(fileName = "New Dialogue Node", menuName = "Dialogue/Dialogue Node")]
public class DialogueNode : ScriptableObject
{
    [TextArea(3, 10)]
    public string text;
    public string characterName; 
    public List<DialogueOption> options = new List<DialogueOption>();
    // Aquí podrías añadir un campo para el personaje que habla, si no es el mismo que 'characterName'
    // También podrías añadir un campo para un evento que se dispara al llegar a este nodo
}

[System.Serializable]
public class DialogueOption
{
    public string optionText; 
    public DialogueNode nextNode; 
    // Aquí podrías añadir condiciones para que la opción sea visible/interactuable
    // Por ejemplo: public List<Condition> conditions;
}

DialogueGraph.cs

Este Scriptable Object actuará como el contenedor principal para todo un árbol de diálogo. Si quisieras un editor visual más avanzado, podrías usarlo para almacenar un diccionario de nodos por ID.

using UnityEngine; 
using System.Collections.Generic;

[CreateAssetMenu(fileName = "New Dialogue Graph", menuName = "Dialogue/Dialogue Graph")]
public class DialogueGraph : ScriptableObject
{
    public DialogueNode startNode;
    // Puedes añadir una lista de todos los nodos si los quieres gestionar de forma global
    // public List<DialogueNode> allNodes;
}

🎮 El Gestor de Diálogos (DialogueManager.cs) 🖥️

Este es el script principal que controlará la lógica de visualización y ramificación del diálogo. Lo adjuntaremos a un GameObject en nuestra escena (por ejemplo, un DialogueManager vacío).

using UnityEngine; 
using UnityEngine.UI; 
using TMPro; 
using System.Collections;
using System.Collections.Generic;

public class DialogueManager : MonoBehaviour
{
    public DialogueGraph currentDialogueGraph;
    public DialogueNode currentNode;

    [Header("UI Elements")]
    public GameObject dialoguePanel;
    public TextMeshProUGUI characterNameText;
    public TextMeshProUGUI dialogueText;
    public Transform optionsContainer;
    public GameObject optionButtonPrefab;

    public float typingSpeed = 0.05f;
    private bool isTyping = false;

    void Start()
    {
        dialoguePanel.SetActive(false); // Ocultar al inicio
        if (currentDialogueGraph != null)
        {
            StartDialogue(currentDialogueGraph);
        }
    }

    public void StartDialogue(DialogueGraph graph)
    {
        currentDialogueGraph = graph;
        currentNode = currentDialogueGraph.startNode; // Empezar desde el nodo inicial
        dialoguePanel.SetActive(true);
        DisplayNode(currentNode);
    }

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

        currentNode = node;
        characterNameText.text = node.characterName;
        ClearOptions();
        StartCoroutine(TypeSentence(node.text));

        // Mostrar opciones si existen y no estamos escribiendo
        if (node.options.Count > 0 && !isTyping)
        {
            foreach (DialogueOption option in node.options)
            {
                // TODO: Añadir lógica para comprobar condiciones de la opción
                CreateOptionButton(option);
            }
        }
    }

    IEnumerator TypeSentence(string sentence)
    {
        isTyping = true;
        dialogueText.text = "";
        foreach (char letter in sentence.ToCharArray())
        {
            dialogueText.text += letter;
            yield return new WaitForSeconds(typingSpeed);
        }
        isTyping = false;

        // Si no hay opciones y el texto ha terminado de escribir, podrías avanzar automáticamente
        // o esperar una interacción para cerrar el diálogo.
        if (currentNode.options.Count == 0)
        {
            // Podrías añadir un botón de "Continuar" o cerrar el diálogo automáticamente
            // Por ahora, solo termina si no hay opciones.
            // Invoke("EndDialogue", 2f); // Ejemplo: cerrar después de 2 segundos
        }
        else
        {   // Mostrar opciones después de que el texto termine de escribirse
            foreach (DialogueOption option in currentNode.options)
            {
                CreateOptionButton(option);
            }
        }
    }

    void ClearOptions()
    {
        foreach (Transform child in optionsContainer)
        {
            Destroy(child.gameObject);
        }
    }

    void CreateOptionButton(DialogueOption option)
    {
        GameObject buttonGO = Instantiate(optionButtonPrefab, optionsContainer);
        buttonGO.GetComponentInChildren<TextMeshProUGUI>().text = option.optionText;
        Button btn = buttonGO.GetComponent<Button>();
        btn.onClick.AddListener(() => OnOptionSelected(option));
    }

    public void OnOptionSelected(DialogueOption selectedOption)
    {
        if (isTyping) // Si el texto aún se está escribiendo, ignorar la selección
        {
            // Podrías acelerar el texto aquí en lugar de ignorar la selección
            StopAllCoroutines();
            dialogueText.text = currentNode.text;
            isTyping = false;
            ClearOptions(); // Necesario para regenerar opciones si se "saltó" el tecleo
            foreach (DialogueOption option in currentNode.options)
            {
                CreateOptionButton(option);
            }
            return;
        }

        // Avanzar al siguiente nodo de diálogo
        DisplayNode(selectedOption.nextNode);

        // TODO: Aquí puedes añadir lógica para afectar el juego (ej. cambiar variables)
    }

    void EndDialogue()
    {
        dialoguePanel.SetActive(false);
        Debug.Log("Diálogo terminado.");
        // Aquí puedes desencadenar eventos de finalización de diálogo
    }

    // Puedes añadir un método para avanzar manualmente si no hay opciones (ej. clic del ratón)
    public void AdvanceDialogue()
    {
        if (isTyping)
        {   // Acelerar el tecleo
            StopAllCoroutines();
            dialogueText.text = currentNode.text;
            isTyping = false;
            // Regenerar opciones si es necesario
            ClearOptions();
            if (currentNode.options.Count > 0)
            {
                foreach (DialogueOption option in currentNode.options)
                {
                    CreateOptionButton(option);
                }
            }
        }
        else if (currentNode.options.Count == 0)
        {   // Si no hay opciones, terminar el diálogo
            EndDialogue();
        } else {
             // Si hay opciones, se espera que el usuario haga clic en una
             // o se podría auto-seleccionar la primera opción si no queremos interacción
        }
    }
}

🎨 Conectando la UI y los Scriptable Objects 🔗

Ahora que tenemos los scripts, es hora de unirlos todo en el editor de Unity.

1. Preparar el Prefab del Botón de Opción

  • Selecciona tu Button - TextMeshPro en el Canvas (el que creaste como placeholder). Configúrale el texto del botón para que esté centrado y tenga un tamaño adecuado.
  • Arrastra este GameObject desde la jerarquía a la carpeta Assets (o una subcarpeta Prefabs) para crear un prefab.
  • Elimina la instancia del botón del Canvas, ya que solo usaremos el prefab.

2. Configurar el DialogueManager

  • Crea un GameObject vacío en tu escena y nómbralo DialogueManager.
  • Adjunta el script DialogueManager.cs a este GameObject.
  • Arrastra los elementos de UI a sus respectivos slots en el Inspector del DialogueManager:
    • Dialogue Panel -> El Panel de tu Canvas.
    • Character Name Text -> El componente TextMeshProUGUI del nombre del personaje.
    • Dialogue Text -> El componente TextMeshProUGUI del texto de diálogo.
    • Options Container -> El Vertical Layout Group donde irán los botones de opciones.
    • Option Button Prefab -> El prefab del botón que acabas de crear.
📌 Nota: Puedes ajustar el `Typing Speed` a tu gusto. Valores más bajos son más rápidos.

3. Crear Diálogos con Scriptable Objects

Ahora viene la parte divertida: ¡crear la conversación!

  • En tu carpeta Assets, crea una nueva carpeta Dialogue.
  • Haz clic derecho en Assets/Dialogue > Create > Dialogue > Dialogue Node. Crea varios nodos (por ejemplo, NodoIntro, NodoOpcion1, NodoOpcion2, NodoFinal).
  • Haz clic derecho en Assets/Dialogue > Create > Dialogue > Dialogue Graph. Nómbralo MiPrimerDialogo.
  • Selecciona MiPrimerDialogo en el Inspector. Arrastra tu NodoIntro al campo Start Node.

Ejemplo de Configuración de Nodos:

Nodo: NodoIntro

  • Character Name: "Guardia"
  • Text: "¡Alto ahí, forastero! ¿Qué te trae a estas tierras?"
  • Options (Lista de DialogueOption):
    • Option 1:
      • Option Text: "Busco aventuras y fortuna."
      • Next Node: NodoOpcion1
    • Option 2:
      • Option Text: "Estoy perdido, ¿puedes ayudarme?"
      • Next Node: NodoOpcion2

Nodo: NodoOpcion1

  • Character Name: "Guardia"
  • Text: "¡Ja! Aquí solo encontrarás problemas. Sigue tu camino."
  • Options (vacía, esto termina el diálogo si no añades un nextNode)

Nodo: NodoOpcion2

  • Character Name: "Guardia"
  • Text: "Hmm, parece que has tenido un largo viaje. Ven conmigo al albergue."
  • Options (vacía)
💡 Consejo: Usa el Inspector para enlazar los nodos arrastrándolos de la carpeta `Assets` al campo `Next Node` de cada `DialogueOption`.

▶️ Probando el Sistema de Diálogos

  1. En el DialogueManager en tu escena, arrastra el DialogueGraph MiPrimerDialogo al campo Current Dialogue Graph.
  2. Asegúrate de que el Dialogue Panel esté inicialmente desactivado en el Inspector (desmarca la casilla). El script lo activará al iniciar.
  3. ¡Ejecuta el juego! Deberías ver el panel de diálogo aparecer con el texto del NodoIntro y las dos opciones. Al hacer clic en una opción, el diálogo avanzará al nodo correspondiente.
80% Funcionalidad Básica

🚀 Mejoras y Funcionalidades Avanzadas

Nuestro sistema básico ya funciona, pero hay muchas maneras de expandirlo para hacerlo más robusto y versátil.

1. Variables de Juego y Condiciones 🎲

Para que los diálogos reaccionen al estado del juego, necesitamos un sistema de variables. Podrías crear un GameVariablesManager (otro Scriptable Object o Singleton) que contenga variables booleanas, enteras o strings.

Luego, en DialogueOption.cs, podrías añadir una lista de conditions (clases o Scriptable Objects propios) que el DialogueManager verificará antes de mostrar una opción.

Ejemplo de DialogueOption con Condiciones:

// En DialogueOption.cs
[System.Serializable]
public class DialogueOption
{
    public string optionText;
    public DialogueNode nextNode;
    public List<Condition> conditions = new List<Condition>(); // Lista de condiciones
    public List<Action> actions = new List<Action>();         // Lista de acciones a ejecutar
}

// Clases de ejemplo para condiciones y acciones
[System.Serializable]
public abstract class Condition : ScriptableObject { public abstract bool Evaluate(); }
[System.Serializable]
public abstract class Action : ScriptableObject { public abstract void Execute(); }

// Ejemplo de implementación de una condición
[CreateAssetMenu(fileName = "New Item Condition", menuName = "Dialogue/Conditions/Has Item")]
public class HasItemCondition : Condition
{
    public string itemName;
    public override bool Evaluate()
    {
        // Implementa la lógica para comprobar si el jugador tiene el item
        // Por ejemplo: return InventoryManager.Instance.HasItem(itemName);
        Debug.Log("Evaluando condición: " + itemName); // Placeholder
        return true; 
    }
}

// Ejemplo de implementación de una acción
[CreateAssetMenu(fileName = "New Give Item Action", menuName = "Dialogue/Actions/Give Item")]
public class GiveItemAction : Action
{
    public string itemName;
    public override void Execute()
    {
        // Implementa la lógica para dar un item al jugador
        // Por ejemplo: InventoryManager.Instance.AddItem(itemName);
        Debug.Log("Ejecutando acción: Dar item " + itemName); // Placeholder
    }
}

Para integrarlo, modificarías el DialogueManager en CreateOptionButton y OnOptionSelected:

// Dentro de CreateOptionButton
// ...
// bool meetsConditions = true;
// foreach (Condition condition in option.conditions)
// {
//     if (!condition.Evaluate())
//     {
//         meetsConditions = false;
//         break;
//     }
// }
// if (meetsConditions)
// {
//     GameObject buttonGO = Instantiate(optionButtonPrefab, optionsContainer);
//     buttonGO.GetComponentInChildren<TextMeshProUGUI>().text = option.optionText;
//     Button btn = buttonGO.GetComponent<Button>();
//     btn.onClick.AddListener(() => OnOptionSelected(option));
// } else {
//     // Opcional: Desactivar o ocultar el botón si no cumple condiciones
// }

// Dentro de OnOptionSelected, antes de DisplayNode
// foreach (Action action in selectedOption.actions)
// {
//     action.Execute();
// }

2. Editor Visual de Nodos (GraphView) 🤯

Para sistemas de diálogo complejos, el Inspector se vuelve impráctico. Unity ofrece el sistema GraphView que permite crear editores gráficos personalizados.

Esto es un tema avanzado, pero la idea es:

  1. Crear una nueva ventana de Editor (EditorWindow).
  2. Usar GraphView para dibujar nodos y conexiones.
  3. Guardar y cargar la estructura del diálogo en tu DialogueGraph (por ejemplo, serializando los nodos y sus conexiones en JSON o binario, o referenciando los Scriptable Objects).
¿Por qué usar GraphView? GraphView proporciona una base sólida para construir herramientas visuales complejas en Unity. Permite arrastrar, soltar, conectar y organizar nodos de forma intuitiva, lo que es invaluable para diseñar árboles de diálogo extensos y complejos. Reduce errores y mejora la legibilidad del flujo de la conversación.

3. Animaciones y Sonido 🎶

  • Animaciones: Puedes disparar animaciones de personajes (gestos, expresiones) cuando se muestra un nodo específico. Añade un campo AnimationClip o string animationTrigger a DialogueNode.
  • Sonido: Reproducir clips de audio para las voces de los personajes o efectos de sonido ambientales. Añade un campo AudioClip a DialogueNode.

4. Guardar y Cargar Diálogo 💾

Para que los diálogos "recuerden" las decisiones del jugador en partidas futuras, necesitas serializar el estado del diálogo. Esto generalmente implica guardar:

  • El DialogueGraph actual.
  • El DialogueNode actual (o su ID).
  • El estado de todas las variables del juego que afectan al diálogo.
⚠️ Advertencia: Guardar Scriptable Objects directamente puede ser complicado. Es mejor guardar las referencias por nombre o ID, y las variables de juego en un formato serializable (JSON, binario).

✅ Conclusión y Próximos Pasos

¡Felicidades! 🎉 Has implementado un sistema de diálogos ramificados funcional en Unity. Desde la estructura de datos con Scriptable Objects hasta la integración con la UI y la lógica de ramificación, has cubierto los fundamentos esenciales.

Este es solo el comienzo. Te animo a que sigas experimentando y añadiendo funcionalidades:

  • Soporte Multi-idioma: Permite que tus diálogos se adapten a diferentes idiomas.
  • Tipos de Nodos: Introduce nodos para eventos especiales (dar objetos, iniciar misiones, activar cinemáticas).
  • Sistema de Reputación: Vincula las opciones de diálogo a un sistema de reputación con NPCs.
  • Transiciones suaves: Mejora las transiciones entre nodos y opciones con animaciones de UI.
  • Efectos de texto: Añade efectos como letras temblorosas, cambio de color, o imágenes inline en el texto.

El desarrollo de juegos es un viaje de aprendizaje continuo. Sigue explorando, creando y, sobre todo, divirtiéndote. ¡Tus personajes te lo agradecerán!

Tutoriales relacionados

Comentarios (0)

Aún no hay comentarios. ¡Sé el primero!