viernes, 1 de agosto de 2025

Patrón Observer en Unity: eventos limpios sin acoplar objetos



Último post de la serie de patrones que hay que saberse para hacer videojuegos.

Cuándo usarlo: HUD, logros, misiones, sonido, spawns… cualquier reacción a “algo pasó”. Como principal ventaja es que sustituyes referencias directas por suscripciones, lo que facilita pruebas y reutilización (el emisor no sabe quién escucha).

Opción A — C# events (rápido y fuerte)

Event Hub

using System; public static class GameEvents { public static event Action<int> OnCoinCollected; public static event Action OnPlayerDied; public static void CoinCollected(int amount) => OnCoinCollected?.Invoke(amount); public static void PlayerDied() => OnPlayerDied?.Invoke(); }

Emisor

using UnityEngine; public class Coin : MonoBehaviour { public int amount = 1; void OnTriggerEnter(Collider other) { if (!other.CompareTag("Player")) return; GameEvents.CoinCollected(amount); Destroy(gameObject); } }

Observadores

using UnityEngine; using UnityEngine.UI; public class HudCoins : MonoBehaviour { public Text label; int total; void OnEnable() => GameEvents.OnCoinCollected += Handle; void OnDisable() => GameEvents.OnCoinCollected -= Handle; void Handle(int a){ total += a; label.text = total.ToString(); } } public class GameOverScreen : MonoBehaviour { public GameObject panel; void OnEnable() => GameEvents.OnPlayerDied += Show; void OnDisable() => GameEvents.OnPlayerDied -= Show; void Show(){ panel.SetActive(true); } }

Opción B — Event Channels con ScriptableObject (editor-friendly)

Canal

using UnityEngine; using UnityEngine.Events; [CreateAssetMenu(menuName="Events/Int Event Channel")] public class IntEventChannel : ScriptableObject { public UnityAction<int> OnRaised; public void Raise(int value) => OnRaised?.Invoke(value); }

Emisor/Listener

// Emisor (asignas el canal desde el inspector) public class CoinEmitter : MonoBehaviour { public IntEventChannel CoinChannel; public int amount = 1; void OnTriggerEnter(Collider c) { if (c.CompareTag("Player")) CoinChannel.Raise(amount); } } // Listener using UnityEngine; using UnityEngine.UI; public class CoinListener : MonoBehaviour { public IntEventChannel CoinChannel; public Text label; int total; void OnEnable() => CoinChannel.OnRaised += Add; void OnDisable() => CoinChannel.OnRaised -= Add; void Add(int a){ total += a; label.text = total.ToString(); } }

No hay comentarios:

Publicar un comentario