5. Delegados y Eventos

Hoy vamos a conocer en que consisten los “Delegados y Eventos” en C# y Unity.

Cuando comienzas a desarrollar videojuegos en Unity, llega un punto en el que necesitas que ciertos objetos respondan a lo que sucede en otros objetos. Aquí es donde entran los delegados y eventos en C#.

En este post, aprenderás:

  • Qué son los delegados y para qué sirven
  • Cómo se relacionan con los eventos
  • Cómo usarlos en Unity con ejemplos fáciles y prácticos
Tutorial de Unity Nivel: Principiante.

5.1 ¿Qué es un delegado en C#?

Un delegado es un tipo de variable que almacena una referencia a un método. Es como una “variable que apunta a métodos”.

Los delegados permiten llamar métodos de forma flexible, incluso si no sabes cuál exactamente se usará hasta el momento de la ejecución.

Sintaxis básica de un delegado:

public delegate void MiDelegado();

Esto declara un nuevo tipo de delegado llamado MiDelegado, que apunta a métodos que no reciben parámetros y no devuelven nada (void).

Ejemplo 1: Uso simple de un delegado

Código base “DelegadoEjemplo.cs” sin MonoBehaviour

public class DelegadoEjemplo
{
    public delegate void MensajeDelegate();

    public MensajeDelegate mensaje;

    public void Ejecutar()
    {
        if (mensaje != null)
            mensaje();
    }
}

Ahora vamos a ejecutar el delegado desde un código distinto que si usa MonoBehaviour

void Start()
{
    DelegadoEjemplo ejemplo = new DelegadoEjemplo();
    ejemplo.mensaje = MostrarMensaje;
    ejemplo.Ejecutar();
}

void MostrarMensaje()
{
    Debug.Log("¡Hola desde el delegado!");
}

Resultado: se imprime el mensaje al ejecutar Ejecutar().

Los delegados en C# son multicast, es decir, pueden apuntar a varios métodos

// Declaración del tipo de delegado
public delegate void MiDelegado();              // Sin parámetros, sin retorno

void MetodoA()
{
    Debug.Log("Método A");
}

void MetodoB()
{
    Debug.Log("Método B");
}

void Start()
{
    MiDelegado d = MetodoA;
    d += MetodoB;   // Agrega MétodoB
    d();            // Ejecuta MétodoA y después MétodoB
}

  • += añade un método.

  • -= quita un método.

Delagados genéricos: Action y Func

C# ofrece delegados predefinidos para simplificar:

  • Action<T1, T2, …>: métodos que no retornan valor.

  • Func<T1, T2, …, TResult>: métodos que retornan TResult.

Action<string> mostrarMensaje = msg => Debug.Log(msg);
Func<int, int, int> sumar = (a, b) => a + b;

void Start()
{
    mostrarMensaje("Delegado Action");  // Muestra en consola
    int resultado = sumar(3, 4);        // 7
    Debug.Log("3+4=" + resultado);
}

Para estos casos es bueno:

  1. Comprobar null antes de invocar con miDelegado?.Invoke();
  2. Desuscribirse de delegados para evitar referencias colgadas (memory leaks) con miDelegado -= Metodo;

Ejemplo 1: Callback de Animación

Primero el código base “Animador.cs”:

using UnityEngine;
using System;

public class Animador : MonoBehaviour
{
    public delegate void OnAnimacionComplete();
    public OnAnimacionComplete AnimComplete;

    public void ReproducirAnimacion()
    {
        // Imagina aquí tu lógica de animación...
        StartCoroutine(AnimCoroutine());
    }

    private IEnumerator AnimCoroutine()
    {
        // Simula duración de animación
        yield return new WaitForSeconds(1.5f);
        AnimComplete?.Invoke();  // Llama al callback si no es null
    }
}

Ahora el script que lo manda llamar “ControlJefe.cs”

using UnityEngine;
using System;

public class ControlJefe : MonoBehaviour
{
    public Animador animador;

    void Start()
    {
        animador.AnimComplete += AtacarAlJugador;
        animador.ReproducirAnimacion();
    }

    void AtacarAlJugador()
    {
        Debug.Log("¡Jefe ataca tras animación!");
    }
}

Ejemplo 2: Sistema de Salud con Notificación

Primero el código base “HealthSystem.cs”

public class HealthSystem
{
    public delegate void OnDead();
    public OnDead DeadCallback;

    private int salud;

    public HealthSystem(int saludInicial)
    {
        salud = saludInicial;
    }

    public void RecibirDaño(int d)
    {
        salud -= d;
        Debug.Log($"Daño: {d}, Vida actual: {salud}");
        if (salud <= 0)
            DeadCallback?.Invoke();
    }
}

Ahora el código que lo manda llamar “Player.cs”

using UnityEngine;
using System;

public class Player : MonoBehaviour
{
    private HealthSystem health;

    void Start()
    {
        health = new HealthSystem(50);
        health.DeadCallback += Morir;
    }

    void Update()
    {
        if (Input.GetKeyDown(KeyCode.Space))
            health.RecibirDaño(20);
    }

    void Morir()
    {
        Debug.Log("El jugador ha muerto.");
    }
}

Esto es un HealthSystem que avisa cuando la vida llega a cero

5.2 Eventos

Un evento es una forma segura de usar delegados. Es una notificación que una clase puede emitir, y otras pueden suscribirse para reaccionar. Los eventos permiten establecer un sistema desacoplado, donde un objeto informa que algo pasó, y otros objetos responden.

Para tener un idea general de lo importante que son los Eventos; imagina que tienes un código que avisa cuando el jugador muere (un código que supervisa lo que sucede en tu juego y que generalmente se le llama “GameManager”). En lugar de que cada sistema (UI, audio, animación) tenga que preguntar todo el tiempo al GameManager, puedes hacer que estos reaccionen automáticamente al evento de muerte del jugador (que reaccionen automáticamente a un aviso general del GameManager).

Ejemplo 1: Evento básico en Unity

Paso 1: Declarar el evento

public class GameManager : MonoBehaviour
{
    public static event Action OnJugadorMuerto;

    public void MatarJugador()
    {
        Debug.Log("Jugador murió");
        OnJugadorMuerto?.Invoke();
    }
}

Notas:

  • Action es un delegado incorporado en C# (sin parámetros y sin retorno).

  • ?.Invoke() ejecuta el evento solo si hay suscriptores.

Paso 2: Suscribirse al evento

public class UIManager : MonoBehaviour
{
    void OnEnable()
    {
        GameManager.OnJugadorMuerto += MostrarPantallaGameOver;
    }

    void OnDisable()
    {
        GameManager.OnJugadorMuerto -= MostrarPantallaGameOver;
    }

    void MostrarPantallaGameOver()
    {
        Debug.Log("Mostrando pantalla de Game Over");
    }
}

Importante: Siempre desuscribirse en OnDisable() para evitar errores o referencias nulas.

Ejemplo 2: Evento básico con parámetros

Primero se crea el evento dentro de un script

public class Enemigo : MonoBehaviour
{
    public static event Action<int> OnRecibioDaño;

    public void RecibirDaño(int cantidad)
    {
        Debug.Log("Enemigo recibió daño: " + cantidad);
        OnRecibioDaño?.Invoke(cantidad);
    }
}

Y luego, suscribirse así:

void OnEnable()
{
    Enemigo.OnRecibioDaño += ActualizarBarraDeVida;
}

void ActualizarBarraDeVida(int daño)
{
    Debug.Log("Reduciendo barra de vida en " + daño);
}

Plantilla básica para usar eventos en Unity

// Emisor
public class EventoEjemplo : MonoBehaviour
{
    public static event Action OnAlgoPaso;

    public void EjecutarEvento()
    {
        OnAlgoPaso?.Invoke();
    }
}

// Receptor
public class EscuchaEjemplo : MonoBehaviour
{
    void OnEnable() => EventoEjemplo.OnAlgoPaso += Responder;
    void OnDisable() => EventoEjemplo.OnAlgoPaso -= Responder;

    void Responder() => Debug.Log("¡Evento recibido!");
}

Los eventos son muy útiles en los siguientes casos:

  • Notificar a la UI cuando cambia la vida o la puntuación
  • Enviar señales entre enemigos y sistemas de audio
  • Activar animaciones o efectos visuales cuando ocurre una acción
  • Crear sistemas de misión y logros

5.3 Conclusión

Delegados y eventos son herramientas fundamentales para escribir código profesional y mantenible en Unity. Permiten comunicar clases de forma eficiente y ordenada, eliminando la necesidad de referencias cruzadas innecesarias.

Recuerda:

  • Usa delegate si necesitas definir tu propio tipo de método y cuando requieras Callbacks de eventos locales sin necesidad de un sistema global de eventos.
  • Usa Action, Func<> para simplificar
  • Usa event para que otros scripts puedan escuchar, pero no modificar directamente
  • Delegados y eventos sirven para crear sistemas modulares y desacoplados
  • Delegados y eventos sirven para tener menos dependencia entre clases
  • Delegados y eventos te ayudan a tener códigos más limpios y fáciles de mantener
  • Delegados y eventos son ideales para sistemas de UI, combate, logros, audio, etc.

En Resumen

Concepto Qué es Ejemplo
Delegado Una referencia a un método delegate void Nombre();
Evento Una forma segura de exponer un delegado para suscripción event Action OnAlgo;
Action Delegado sin retorno (puede tener 0 o más parámetros) Action, Action<int>
Func<> Delegado con retorno Func<int>, Func<string, bool>

 

Ejercicios.

Para reforzar lo aprendido, es necesario practicarlo, por ello intenta realizar los siguientes ejercicios:

  1. Crea un código dónde uses Delegados para avisar que tu jugador ha obtenido un “power-up” y ahora debe ser invensible.
  2. Crea un código donde uses Eventos para avisar a otros código que el juego terminó (game over) y que ya no es necesario que se generen enemigos nuevos.

Este Tutorial de Unity termina aquí. Acompáñanos en el siguiente tutorial donde veremos “GameManager”.

Siguiente Tutorial de Unity: “6. GameManager y Control de Flujo

¿Te resultó útil este Tutorial?
¡¡Recuerda, los Anuncios nos Ayudan a Mantener este “Gran Sitio” 😀 !!

Comparte el Post
Posted in CSharpUnity.

Deja un comentario

Tu dirección de correo electrónico no será publicada. Los campos obligatorios están marcados con *

This site uses Akismet to reduce spam. Learn how your comment data is processed.