Módulo 6 — Vida del jugador (15 min)¶
¿Qué vamos a lograr?¶
Que la nave tenga 3 puntos de vida, visibles en pantalla, y conectar de una vez los tres peligros que ya construimos:
- Meteorito: mata de un solo golpe (instantáneo).
- Bala enemiga: quita 1 punto de vida.
- Enemigo que se aproxima: si llega a chocar con la nave, quita 1 punto de vida.
La idea de diseño es: los peligros (meteorito, bala enemiga, enemigo) solo avisan el
golpe llamando a un método de PlayerController. Quien decide qué pasa con la nave (bajar
la vida, morir) es siempre PlayerController, nunca cada peligro por su cuenta. Es la
misma idea que ya usamos con el puntaje: cada cosa se encarga de una sola responsabilidad.
6.1 Agregar el marcador de vida en pantalla¶
- En la Hierarchy, selecciona
TextoPuntaje(dentro delCanvas) y duplícalo (Ctrl+D o clic derecho > Duplicate). - Renombra la copia a
TextoVida, y ubícala justo debajo deTextoPuntajeen el Rect Transform (por ejemplo, bajándole la posición Y). - Cambia su texto de prueba a
Vida: 3/3.
6.2 Actualizar el GameManager¶
using UnityEngine;
using TMPro;
// Administra el estado general de la partida: puntaje y vida.
public class GameManager : MonoBehaviour
{
public static GameManager instancia;
[Header("Puntaje")]
public TMP_Text textoPuntaje;
[Header("Vida")]
public TMP_Text textoVida;
private int puntaje = 0;
void Awake()
{
instancia = this;
}
void Start()
{
ActualizarTextoPuntaje();
}
public void SumarPuntos(int cantidad)
{
puntaje += cantidad;
ActualizarTextoPuntaje();
}
void ActualizarTextoPuntaje()
{
textoPuntaje.text = "Puntaje: " + puntaje;
}
// PlayerController llama a este método cada vez que su vida cambia, para que el
// GameManager (que es quien tiene la referencia a la UI) actualice el texto.
public void ActualizarVida(int vidaActual, int vidaMaxima)
{
textoVida.text = "Vida: " + vidaActual + "/" + vidaMaxima;
}
public void TerminarJuego()
{
// Todavía no armamos la pantalla de Game Over con el puntaje final y el botón
// Reintentar (eso es el Módulo 7). Por ahora, solo detenemos el juego:
// Time.timeScale = 0 congela todo lo que dependa de Time.deltaTime o de la
// física (el movimiento de meteoritos y enemigos incluido).
Time.timeScale = 0f;
Debug.Log("Fin del juego");
}
}
- Selecciona el GameObject
GameManagery arrastraTextoVidaal nuevo campo Texto Vida.
6.3 Darle vida a la nave¶
using UnityEngine;
public class PlayerController : MonoBehaviour
{
[Header("Movimiento")]
public float moveSpeed = 8f;
public float edgePadding = 0.4f;
[Header("Disparo")]
public GameObject bulletPrefab;
public Transform firePoint;
public float fireRate = 0.3f;
[Header("Vida")]
public int vidaMaxima = 3;
private float minX, maxX, minY, maxY;
private float proximoDisparoDisponible;
// Cuánta vida le queda a la nave en este momento (empieza igual a vidaMaxima).
private int vidaActual;
void Start()
{
CalcularLimitesDePantalla();
vidaActual = vidaMaxima;
GameManager.instancia.ActualizarVida(vidaActual, vidaMaxima);
}
// ... CalcularLimitesDePantalla(), Update(), Mover() y Disparar() siguen igual
// que en los Módulos 1 y 2 ...
// Los objetos que dañan a la nave (meteoritos, balas enemigas, enemigos que chocan
// de frente) llaman a este método público desde su propio OnTriggerEnter2D,
// pasándole cuánta vida deben quitar.
public void RecibirDano(int cantidad)
{
vidaActual -= cantidad;
vidaActual = Mathf.Max(vidaActual, 0); // nunca dejamos que la vida baje de 0
GameManager.instancia.ActualizarVida(vidaActual, vidaMaxima);
if (vidaActual <= 0)
{
Morir();
}
}
// Algunos peligros (como los meteoritos) no hacen daño parcial: matan de una sola vez.
// En vez de duplicar la lógica de "matar", reutilizamos RecibirDano quitando toda la vida.
public void MorirInstantaneamente()
{
RecibirDano(vidaMaxima);
}
void Morir()
{
// Todavía no armamos la pantalla de Game Over (eso es el Módulo 7).
// Por ahora, avisamos al GameManager y apagamos la nave.
GameManager.instancia.TerminarJuego();
gameObject.SetActive(false);
}
}
Note
El bloque de CalcularLimitesDePantalla(), Update(), Mover() y Disparar() no
cambió nada desde el Módulo 2 — el archivo real
PlayerController.cs lo tiene completo, acá lo
omitimos para no repetir código que ya vimos.
6.4 Conectar cada peligro al sistema de vida¶
Meteorito — reemplazamos el Destroy directo por el aviso a PlayerController:
void OnTriggerEnter2D(Collider2D other)
{
if (other.CompareTag("Player"))
{
// Un meteorito mata de un solo golpe, sin importar cuánta vida quede.
// Quien decide qué pasa con la nave es PlayerController, no nosotros:
// nosotros solo avisamos el golpe.
PlayerController jugador = other.GetComponent<PlayerController>();
if (jugador != null)
{
jugador.MorirInstantaneamente();
}
Destroy(gameObject);
}
}
Bala enemiga — primero abre el prefab EnemyBullet (Assets/Prefabs/EnemyBullet) y
agrégale Add Component > Circle Collider 2D, con Is Trigger activado (en el
Módulo 4 lo dejamos pendiente a propósito). Después, el script:
void OnTriggerEnter2D(Collider2D other)
{
if (other.CompareTag("Player"))
{
PlayerController jugador = other.GetComponent<PlayerController>();
if (jugador != null)
{
jugador.RecibirDano(1);
}
Destroy(gameObject);
}
}
Enemigo que se aproxima — ya tenía Collider2D y Rigidbody2D desde el Módulo 4, así
que solo agregamos el método:
void OnTriggerEnter2D(Collider2D other)
{
if (other.CompareTag("Player"))
{
PlayerController jugador = other.GetComponent<PlayerController>();
if (jugador != null)
{
jugador.RecibirDano(1);
}
Destroy(gameObject);
}
}
6.5 Probar todo junto¶
Presiona Play y confirma que:
- Al recibir una bala enemiga o chocar con un
EnemyChaser, la vida baja de a 1 y el marcador se actualiza. - Al chocar con un meteorito, la nave muere al instante sin importar la vida que tuviera.
- Al llegar la vida a 0, el juego se congela (por ahora) y en la consola aparece "Fin del juego" — es el comportamiento temporal que vamos a reemplazar en el Módulo 7.
Checkpoint ✅¶
Al terminar este módulo, la nave debería tener 3 puntos de vida visibles en pantalla, que
bajan de a 1 con las balas enemigas y el choque directo de un EnemyChaser, y que se
vacían de una sola vez con un meteorito. Al llegar a 0, el juego se detiene.



