Café y Código

4. UI y GameManager: El Estado Global del Juego 📊🖥️

Introducción a la Interfaz de Usuario (UI) en Unity

Para que un videojuego se sienta como tal, necesita comunicarse con el jugador. Esto se logra a través del HUD (Heads-Up Display) o Interfaz de Usuario en pantalla.

En Unity, toda interfaz visual requiere un objeto contenedor especial llamado Canvas (Lienzo).

  1. Haz clic derecho en la ventana Hierarchy y selecciona UI -> Canvas.
  2. Verás que se crea automáticamente el Canvas junto con un objeto llamado EventSystem (necesario para detectar clicks en botones).
  3. Haz clic derecho sobre el Canvas y crea tres textos seleccionando UI -> Text - TextMeshPro:
    • Nombra al primero como ScoreText (ubicado arriba a la izquierda).
    • Nombra al segundo como TimerText (ubicado arriba a la derecha).
    • Nombra al tercero como WinLoseText (ubicado al centro de la pantalla, con tamaño de fuente grande).
Importante: Si es la primera vez que usas TextMeshPro, aparecerá una ventana emergente pidiéndote importar los assets básicos. Haz clic en el botón "Import TMP Essentials" para que los textos puedan renderizarse correctamente.

El Controlador Global: GameManager

El GameManager es un script centralizado encargado de controlar las reglas del juego: contar los puntos, llevar el reloj de tiempo y determinar si el jugador gana o pierde.

Para que cualquier script pueda acceder al GameManager de forma directa y limpia, utilizaremos el Patrón de Diseño Singleton.

  1. Crea un script llamado GameManager y ábrelo en tu editor de código.
  2. Reemplaza su contenido con el siguiente código:
GameManager.cs
CSHARP
1 using UnityEngine;
2 using TMPro; // Libreria necesaria para manejar TextMeshPro
3
4 public class GameManager : MonoBehaviour
5 {
6 // Instancia unica (Singleton) accesible por cualquier script
7 public static GameManager Instance { get; private set; }
8
9 [Header("UI References")]
10 [SerializeField] private TextMeshProUGUI scoreText;
11 [SerializeField] private TextMeshProUGUI timerText;
12 [SerializeField] private TextMeshProUGUI winLoseText;
13
14 [Header("Game Settings")]
15 [SerializeField] private int totalCoinsToWin = 5;
16 [SerializeField] private float timeLimit = 30f;
17
18 private int score = 0;
19 private float timeRemaining;
20 private bool isGameOver = false;
21
22 void Awake()
23 {
24 // Configuracion del patron Singleton
25 if (Instance == null) { Instance = this; }
26 else { Destroy(gameObject); }
27 }
28
29 void Start()
30 {
31 timeRemaining = timeLimit;
32 UpdateUI();
33 winLoseText.text = ""; // Limpiamos el texto al iniciar
34 }
35
36 void Update()
37 {
38 if (isGameOver) return;
39
40 // Cuenta regresiva del tiempo
41 if (timeRemaining > 0)
42 {
43 timeRemaining -= Time.deltaTime;
44 UpdateUI();
45 }
46 else
47 {
48 GameOver(false); // Perder por tiempo
49 }
50 }
51
52 public void CollectCoin()
53 {
54 if (isGameOver) return;
55
56 score++;
57 UpdateUI();
58
59 if (score >= totalCoinsToWin)
60 {
61 GameOver(true); // Ganar el juego
62 }
63 }
64
65 public void HitObstacle()
66 {
67 if (isGameOver) return;
68
69 GameOver(false); // Perder por chocar contra un peligro
70 }
71
72 private void UpdateUI()
73 {
74 scoreText.text = "Monedas: " + score + " / " + totalCoinsToWin;
75 timerText.text = "Tiempo: " + Mathf.Max(0, Mathf.CeilToInt(timeRemaining)) + "s";
76 }
77
78 private void GameOver(bool won)
79 {
80 isGameOver = true;
81 if (won)
82 {
83 winLoseText.text = "¡VICTORIA! 🎉";
84 winLoseText.color = Color.green;
85 }
86 else
87 {
88 winLoseText.text = "GAME OVER 💀";
89 winLoseText.color = Color.red;
90 }
91 }
92 }

Conectando los Componentes en el Editor

Para que el GameManager pueda actualizar los textos de la interfaz, debemos enlazar los objetos en el Inspector:

  1. Crea un GameObject vacío en tu jerarquía (clic derecho -> Create Empty) y llámalo _GameManager.
  2. Arrástrale el script GameManager.cs que acabas de programar.
  3. Selecciona _GameManager. Verás en el Inspector los campos vacíos de UI: Score Text, Timer Text y Win Lose Text.
  4. Arrastra tus objetos correspondientes de la jerarquía (ScoreText, TimerText, etc.) a cada uno de estos campos.

Enlazando el Jugador con el GameManager

Ahora debemos actualizar las funciones de interacción en el script PlayerController.cs para que le reporten los eventos a la instancia Singleton del GameManager:

PlayerController.cs (Actualizado)
CSHARP
1 // Modificacion dentro de PlayerController.cs al interactuar con objetos
2
3 private void OnTriggerEnter(Collider other)
4 {
5 if (other.CompareTag("Coin"))
6 {
7 other.gameObject.SetActive(false);
8
9 // Reportamos al GameManager la recoleccion usando la instancia Singleton
10 GameManager.Instance.CollectCoin();
11 }
12 }
13
14 private void OnCollisionEnter(Collision collision)
15 {
16 if (collision.gameObject.CompareTag("Obstacle"))
17 {
18 // Reportamos al GameManager que chocamos contra un peligro
19 GameManager.Instance.HitObstacle();
20 }
21 }

Al hacer GameManager.Instance.CollectCoin(), estamos llamando directamente al método de la única copia del GameManager que existe en memoria, sumando un punto de forma inmediata y actualizando el texto en pantalla.

Ponte a prueba

Comprueba si has asimilado la lógica del flujo de datos entre el jugador, el GameManager y el Canvas de la interfaz gráfica.

Ko-fi
Donaciones
Apoyá cafeycodigo con un café en Ko-fi. Colaboradores: insignia, muro y zona exclusiva.
🎮 CONSOLA DE SKIN
Comandos: matrix, minecraft, pacman, default
>