El patrón Observer es un patrón de comportamiento que permite que un objeto (observador) se suscriba a otro objeto (observado) para recibir notificaciones de eventos. En el contexto de los videojuegos, este patrón es muy útil para implementar sistemas de eventos y notificaciones, lo que permite una comunicación asincrónica entre diferentes partes del juego.
📝 Ventajas
- Comunicación asincrónica: Permite que objetos se comuniquen sin depender directamente el uno del otro, lo que mejora la flexibilidad del código.
- Modularidad: Facilita la creación de sistemas modulares y decoupled, lo que hace que el código sea más fácil de mantener y expandir.
- Reutilización: Permite reutilizar comportamientos y acciones en diferentes partes del juego sin duplicar el código.
- Desacoplamiento: Reduce la dependencia entre componentes del juego, lo que mejora la escalabilidad y la legibilidad del código.
🚧 Desventajas
- Mantener referencias: Puede ser complejo mantener referencias y evitar fugas de memoria, especialmente en entornos de larga duración como videojuegos.
- Dependencia de la plataforma: El patrón Observer puede depender de la implementación de la plataforma, lo que puede dificultar la portabilidad del código.
- Dificultad en debugear: Los sistemas complejos basados en eventos pueden ser difíciles de depurar y entender debido a la naturaleza dispersa de las comunicaciones.
🎮 Uso en videojuegos
- Eventos del juego: Notificar eventos como muertes de enemigos, recolección de objetos, cambios en el estado del jugador, etc.
- Sistema de UI: Actualizar la interfaz de usuario automáticamente en respuesta a eventos del juego.
- Gestión de niveles: Notificar al sistema de niveles cuando un nivel se completa para cargar el siguiente nivel.
- Sistema de sonido y efectos visuales: Reproducir sonidos y efectos visuales en respuesta a eventos del juego.
🤔 Preguntas para evaluar si es el correcto
- ¿Es necesario un sistema de comunicación asincrónico entre diferentes partes del juego?
- ¿Los componentes del juego pueden separarse de manera que no tengan dependencias directas entre sí?
- ¿Hay eventos que necesiten ser notificados de manera genérica y sin dependencias específicas?
- ¿La lógica del juego se beneficia de la modularidad y la reutilización de código?
- ¿Es posible que la complejidad de los eventos y sus suscriptores sea manejable?
💡 Sugerencias adicionales
- Desacoplamiento: Asegúrate de que los objetos no estén demasiado acoplados, permitiendo una comunicación más limpia y modular.
- Optimización: Ten en cuenta la eficiencia y la optimización del sistema de eventos, especialmente en juegos con muchos eventos y suscriptores.
- Extensibilidad: Asegúrate de que el sistema de eventos sea lo suficientemente flexible para agregar nuevos eventos y suscriptores en el futuro sin requerir cambios significativos en el código existente.

🖥️ Código fuente
La interfaz IObserver define el método OnNotify que los suscriptores deben implementar para recibir notificaciones.
public interface IObserver
{
void OnNotify(object data);
}
La clase Subject será la clase base para cualquier objeto que pueda notificar a otros objetos (Observers) sobre cambios o eventos.
using System.Collections.Generic;
using UnityEngine;
public class Subject
{
private readonly List<IObserver> _observers = new List<IObserver>();
public void Attach(IObserver observer)
{
if (!_observers.Contains(observer))
{
_observers.Add(observer);
}
}
public void Detach(IObserver observer)
{
_observers.Remove(observer);
}
public void Notify(object data)
{
foreach (var observer in _observers)
{
observer.OnNotify(data);
}
}
}
🕹️ Ejemplo
- Interfaz
IObserver: Define el métodoOnNotifyque los observadores deben implementar para recibir notificaciones. - Clase
Subject: Gestiona la lista de observadores y proporciona métodos para agregar, quitar y notificar a los observadores. - Clase
GameEventManager: Implementa el patrón Observer y gestiona los eventos del juego. - Clase
Player: Se suscribe a eventos y reacciona cuando se notifica la muerte de un enemigo. - Clase
Enemy: Lanza un evento cuando muere.
Clase GameEventManager
Esta clase implementa el patrón Observer y gestiona los eventos del juego.
using UnityEngine;
public class GameEventManager : Subject
{
// Método para notificar la muerte de un enemigo
public void NotifyEnemyDeath(string enemyName)
{
Notify(new EnemyDeathEvent(enemyName));
}
}
public class EnemyDeathEvent
{
public string enemyName;
public EnemyDeathEvent(string name)
{
enemyName = name;
}
}
Clase Player
Esta clase se suscribe a los eventos del GameEventManager y reacciona cuando se notifica la muerte de un enemigo.
using UnityEngine;
public class Player : MonoBehaviour, IObserver
{
private GameEventManager _gameEventManager;
void Start()
{
_gameEventManager = FindAnyObjectByType <GameEventManager>();
_gameEventManager.Attach(this);
}
void OnNotify(object data)
{
var eventInfo = data as EnemyDeathEvent;
if (eventInfo != null)
{
Debug.Log($"Enemy {eventInfo.enemyName} has died!");
}
}
void OnDestroy()
{
_gameEventManager.Detach(this);
}
}
Clase Enemy
using UnityEngine;
public class Enemy : MonoBehaviour
{
private GameEventManager _gameEventManager;
void Start()
{
_gameEventManager = FindAnyObjectByType <GameEventManager>();
}
void Die()
{
_gameEventManager.NotifyEnemyDeath(name);
Destroy(gameObject);
}
void OnCollisionEnter(Collision collision)
{
if (collision.gameObject.CompareTag("Bullet"))
{
Die();
}
}
}