Scriptable Objects en Unity (Parte 2): Sistema de Eventos Desacoplados (Arquitectura Limpia)
(Parte 2): Sistema de Eventos Desacoplados (Arquitectura Limpia)
En la Parte 1 de esta serie aprendimos a usar los ScriptableObjects para almacenar datos estadísticos de armas y personajes de forma modular. Hoy daremos un paso más allá: aprenderemos a usar Scriptable Objects como canales de eventos (Event Channels) para comunicar diferentes sistemas de tu juego sin que se conozcan entre sí.
- Por qué el acoplamiento directo entre scripts (como
GetComponent) arruina proyectos grandes. - Cómo crear un Event Channel basado en ScriptableObject.
- Cómo hacer que la UI o los efectos de sonido reaccionen al daño del jugador sin referencias directas.
1. El problema del acoplamiento en Unity
Imagina que tu jugador recibe daño. Habitualmente harías esto dentro del script de salud:
// ❌ MALA PRÁCTICA (Acoplamiento fuerte)
public class PlayerHealth : MonoBehaviour {
public UIManager uiManager;
public SoundManager soundManager;
public ParticleSystem bloodParticles;
public void TakeDamage(int damage) {
uiManager.UpdateHealthBar();
soundManager.PlayHurtSound();
bloodParticles.Play();
}
}
El problema: Tu script PlayerHealth necesita conocer la existencia de la UI, los sonidos y las partículas. Si borras o cambias la UI, el script de salud deja de funcionar. Con ScriptableObjects como Eventos, el jugador solo "avisa" que recibió daño, y cualquier sistema interesado escucha el mensaje.
2. Paso 1: Creando el Canal de Eventos (Game Event SO)
Crea un script en C# llamado GameEvent.cs. Este script actuará como el canal por donde se transmiten las señales:
using System.Collections.Generic;
using UnityEngine;
[CreateAssetMenu(fileName = "NewGameEvent", menuName = "ScriptableObjects/Events/GameEvent")]
public class GameEvent : ScriptableObject
{
private readonly List listeners = new List();
// Emitir la señal a todos los oyentes registrados
public void Raise()
{
for (int i = listeners.Count - 1; i >= 0; i--)
{
listeners[i].OnEventRaised();
}
}
// Registrar oyentes
public void RegisterListener(GameEventListener listener)
{
if (!listeners.Contains(listener))
listeners.Add(listener);
}
// Desregistrar oyentes
public void UnregisterListener(GameEventListener listener)
{
if (listeners.Contains(listener))
listeners.Remove(listener);
}
}
3. Paso 2: Creando el Oyente de Eventos (GameEventListener)
Ahora crea un script llamado GameEventListener.cs. Este componente se colocará en cualquier GameObject que quiera responder a un evento (como la interfaz de usuario o un reproductor de audio):
using UnityEngine;
using UnityEngine.Events;
public class GameEventListener : MonoBehaviour
{
[SerializeField] private GameEvent gameEvent;
[SerializeField] private UnityEvent response;
private void OnEnable()
{
if (gameEvent != null)
gameEvent.RegisterListener(this);
}
private void OnDisable()
{
if (gameEvent != null)
gameEvent.UnregisterListener(this);
}
public void OnEventRaised()
{
response?.Invoke();
}
}
4. Paso 3: Emitiendo el Evento desde el Jugador
Modifiquemos el script del jugador. Ahora solo necesita llamar al método Raise() de nuestro archivo ScriptableObject cuando ocurra la acción:
using UnityEngine;
public class PlayerHealth : MonoBehaviour
{
[SerializeField] private int currentHealth = 100;
[SerializeField] private GameEvent onPlayerDamaged; // ScriptableObject del evento
public void TakeDamage(int amount)
{
currentHealth -= amount;
Debug.Log($"Jugador dañando. Salud restante: {currentHealth}");
// Lanzamos el evento sin importar quién lo esté escuchando
if (onPlayerDamaged != null)
{
onPlayerDamaged.Raise();
}
}
}
5. Configuración en la interfaz de Unity
- En la ventana Project, crea el activo de evento haciendo clic derecho:
Create > ScriptableObjects > Events > GameEventy nómbraloOnPlayerDamaged. - Asigna este archivo
OnPlayerDamageden la casilla correspondiente del componentePlayerHealth. - En tu interfaz de usuario (por ejemplo, el texto de la vida o la barra de salud), añade el componente
GameEventListener. - Arrastra el archivo
OnPlayerDamageda la casilla **Game Event** del oyente, y en el evento **Response** define la función que actualizará la UI o ejecutará el efecto.
6. Conclusión y ventajas clave
Gracias a esta arquitectura basada en eventos con Scriptable Objects:
- Cero dependencias: Puedes eliminar o cambiar la UI sin romper el código del personaje.
- Fácil de probar: Puedes simular la recepción de daño haciendo clic en un botón del inspector en tiempo de ejecución.
- Reutilizable: Un mismo evento puede desencadenar múltiples acciones independientes (sonido, partículas, log de eventos, vibración) sin tocar una sola línea de código en C#.
¿Qué te parece esta técnica? ¿En qué sistema de tu juego planeas implementarla? ¡Déjame un comentario abajo con tus dudas o proyectos!

Deja un comentario