Resumen

Implementar un sistema de puntaje reactivo en un videojuego requiere que múltiples objetos respondan automáticamente cuando algo cambia. El patrón Observer resuelve exactamente este problema: permite que varios "espectadores" se notifiquen cuando un sujeto sufre una modificación, ejecutando métodos específicos en respuesta. Si ya trabajaste con events y delegates, este concepto te resultará familiar, porque el Observer es su evolución natural aplicada a la arquitectura del juego.

¿Cómo configurar el comportamiento de las balas con Rigidbody2D?

El primer paso es crear un script llamado Bullet Behavior dentro de la carpeta Bullets del proyecto [0:29]. Este script controla cómo se mueve cada bala una vez instanciada.

  • Se utiliza [RequireComponent(typeof(Rigidbody2D))] para garantizar que el prefab siempre tenga un componente de física 2D.
  • Se declara una variable private Rigidbody2D rb que se inicializa en el Start con GetComponent<Rigidbody2D>().
  • Se expone una variable public float speed para ajustar la rapidez desde el inspector.
  • En el FixedUpdate, la velocidad del Rigidbody2D se iguala a transform.up * speed, lo que hace que la bala siempre avance en la dirección local del eje Y [1:22].

Para que la bala apunte correctamente, en el Player Controller se asigna la rotación actual del avión al momento de instanciar el proyectil: transform.rotation = transform.rotation [2:08]. Esto asegura que cada bala hereda la orientación del jugador al disparar.

¿Cómo hacer que los enemigos reaccionen al impacto de bala?

Dentro del script Enemy Life, se añade una comparación usando CompareTag("Bullet") [2:40]. Cuando un enemigo colisiona con algo que tenga el tag "Bullet", se ejecuta la lógica de desactivación. En lugar de destruir el GameObject, se desactiva con gameObject.SetActive(false), lo que permite reutilizarlo dentro del sistema de Object Pooling implementado anteriormente [2:55].

Este mismo principio aplica a explosiones, nubes y otros elementos visuales: cualquier objeto recurrente puede integrarse al pool para mejorar el rendimiento.

¿Qué es un Action y cómo simplifica el patrón Observer?

El patrón Observer se implementa dentro del Game Manager utilizando un public static Action llamado OnUpdateScore [3:35]. Un Action en C# comprime un delegate y un evento en una sola línea, eliminando la necesidad de declarar ambos por separado. Para utilizarlo, se requiere el namespace using System.

csharp using System;

public static Action OnUpdateScore;

Con esta única línea, cualquier objeto del juego puede suscribirse o desuscribirse del evento de actualización de puntaje.

¿Cómo se suscriben y desuscriben los enemigos al evento?

Dentro de Enemy Life se crean dos métodos del ciclo de vida de Unity [4:15]:

  • OnEnable: cuando el enemigo se activa en escena, se suscribe al evento con GameManager.OnUpdateScore += Deactivate.
  • OnDisable: cuando se desactiva, se desuscribe con GameManager.OnUpdateScore -= Deactivate.

El método Deactivate se encarga de ejecutar gameObject.SetActive(false). Además, justo antes de desactivarse, el enemigo invoca el evento con GameManager.OnUpdateScore.Invoke() [5:55], notificando a todos los suscriptores que el puntaje debe actualizarse.

¿Cómo responde el Game Manager al cambio de puntaje?

Dentro del propio Game Manager se crea un método public void UpdateScoreUI que contendrá la lógica para modificar la interfaz de usuario [5:10]. Este método también se suscribe al Action en el OnEnable:

csharp OnUpdateScore += UpdateScoreUI;

Durante las pruebas, al disparar y eliminar enemigos, la consola muestra el mensaje "Score actualizado", confirmando que el patrón Observer funciona correctamente [6:25].

¿Cuál es el reto para consolidar estos patrones de diseño?

Tres patrones fundamentales del desarrollo de videojuegos se cubrieron en este módulo: Singleton, Object Pooling y Observer. El reto propuesto tiene dos partes:

  • Crear un Singleton de UI que reciba el evento del Observer y actualice el puntaje en pantalla.
  • Convertir las explosiones que generan los enemigos al ser destruidos en objetos reutilizables dentro del sistema de Object Pooling [7:00].

Si ya implementaste estos tres patrones, tu arquitectura de juego será mucho más eficiente y escalable. ¿Cuál de estos patrones te resultó más útil en tu proyecto? Comparte tu experiencia en los comentarios.