| Archivo | Acción | Propósito |
|---|---|---|
UI.java |
Nuevo | Controla todo el HUD (superpuesto al juego) |
PanelJuego.java |
Modificación | Instancia UI y llama a ui.draw() en el ciclo de renderizado |
Jugador.java |
Modificación | Notifica a la UI cuando ocurren eventos (recoger llave, abrir cofre, etc.) |
Sound.java |
Ya corregido ✅ | Usa dos instancias (musica, efectoSonido) para evitar conflictos entre loops y efectos puntuales |
💡 ¿Por qué dos instancias de Sound?
Es análogo a tener dos reproductores: uno dedicado exclusivamente a música de fondo (loop), otro para efectos cortos. Así evitas que un
stop()osetFramePosition(0)en un efecto interrumpa la música.
UI.javaCrea el archivo `src/main/UI.java`:
```java
package main;
import java.awt.Color;
import java.awt.Font;
import java.awt.Graphics2D;
import java.awt.image.BufferedImage;
import java.text.DecimalFormat;
import objetos.OBJ_llave;
/**
* Clase que maneja la Interfaz de Usuario (HUD).
* Dibuja información sobre el juego: llaves, tiempo, mensajes.
*/
public class UI {
PanelJuego pj;
// ⚠️ IMPORTANTE: Las fuentes se declaran AQUÍ, no dentro de draw()
// Esto es por rendimiento - draw() se ejecuta 60 veces por segundo
Font arial_40;
Font arial_80B; // B = Bold
// Imagen de la llave para el HUD
BufferedImage imagenLlave;
// Sistema de mensajes temporales
public boolean mensajeActivo = false;
public String mensaje = "";
int contadorMensaje = 0;
// Estado del juego
public boolean juegoTerminado = false;
// Tiempo de juego
double tiempoJuego;
DecimalFormat formatoDecimal = new DecimalFormat("#0.00");
/**
* Constructor: Inicializa fuentes y carga recursos.
*/
public UI(PanelJuego pj) {
this.pj = pj;
// Crear fuentes UNA sola vez (optimización)
arial_40 = new Font("Arial", Font.PLAIN, 40);
arial_80B = new Font("Arial", Font.BOLD, 80);
// Cargar imagen de llave para mostrar en el HUD
OBJ_llave llave = new OBJ_llave();
imagenLlave = llave.imagen;
}
/**
* Muestra un mensaje temporal en pantalla.
* El mensaje desaparece después de ~2 segundos.
*/
public void mostrarMensaje(String texto) {
mensaje = texto;
mensajeActivo = true;
}
/**
* Método principal de dibujado del HUD.
* Se llama desde paintComponent() de PanelJuego.
*/
public void draw(Graphics2D g2) {
if (juegoTerminado == true) {
// Pantalla de victoria
dibujarPantallaFin(g2);
} else {
// HUD normal durante el juego
dibujarHUD(g2);
}
}
/**
* Dibuja el HUD normal: llaves, tiempo, mensajes.
*/
private void dibujarHUD(Graphics2D g2) {
g2.setFont(arial_40);
g2.setColor(Color.WHITE);
// === CONTADOR DE LLAVES ===
// Dibujar icono de llave
g2.drawImage(imagenLlave, pj.tamanioTile / 2, pj.tamanioTile / 2,
pj.tamanioTile, pj.tamanioTile, null);
// Dibujar número de llaves (x2, x3, etc.)
g2.drawString("x " + pj.jugador.numeroLlaves, 74, 65);
// === TIEMPO DE JUEGO ===
tiempoJuego += (double) 1 / 60; // Incrementar cada frame
g2.drawString("Tiempo: " + formatoDecimal.format(tiempoJuego),
pj.tamanioTile * 11, 65);
// === MENSAJES TEMPORALES ===
if (mensajeActivo == true) {
g2.setFont(g2.getFont().deriveFont(30F));
g2.drawString(mensaje, pj.tamanioTile / 2, pj.tamanioTile * 5);
contadorMensaje++;
// Después de 120 frames (~2 segundos), ocultar mensaje
if (contadorMensaje > 120) {
contadorMensaje = 0;
mensajeActivo = false;
}
}
}
/**
* Dibuja la pantalla de fin del juego.
*/
private void dibujarPantallaFin(Graphics2D g2) {
g2.setFont(arial_40);
g2.setColor(Color.WHITE);
String texto;
int x, y;
int longitudTexto;
// === MENSAJE DE FELICITACIONES ===
texto = "¡Encontraste el tesoro!";
g2.setFont(arial_80B);
// Centrar texto horizontalmente
longitudTexto = (int) g2.getFontMetrics().getStringBounds(texto, g2).getWidth();
x = (pj.anchoPantalla / 2) - (longitudTexto / 2);
y = pj.altoPantalla / 2 - (pj.tamanioTile * 2);
// Sombra del texto (efecto visual)
g2.setColor(Color.BLACK);
g2.drawString(texto, x + 5, y + 5);
// Texto principal
g2.setColor(Color.YELLOW);
g2.drawString(texto, x, y);
// === TIEMPO FINAL ===
g2.setFont(arial_40);
g2.setColor(Color.WHITE);
texto = "Tu tiempo fue: " + formatoDecimal.format(tiempoJuego) + " segundos";
longitudTexto = (int) g2.getFontMetrics().getStringBounds(texto, g2).getWidth();
x = (pj.anchoPantalla / 2) - (longitudTexto / 2);
y = pj.altoPantalla / 2 + (pj.tamanioTile * 2);
g2.drawString(texto, x, y);
// === DETENER EL JUEGO ===
pj.threadJuego = null;
}
/**
* Método utilitario para obtener la posición X centrada de un texto.
*/
public int obtenerXCentrado(String texto, Graphics2D g2) {
int longitudTexto = (int) g2.getFontMetrics().getStringBounds(texto, g2).getWidth();
return (pj.anchoPantalla / 2) - (longitudTexto / 2);
}
}
### 🔍 Lógica clave:
- **Fuentes se inicializan una sola vez** en el constructor → evita creación repetida (60×/segundo), lo cual afecta rendimiento.
- **Mensajes temporales** usan un contador de frames (`contadorMensaje++`).
Como el juego corre a **60 FPS**, `120 frames = 2 segundos` → tiempo de visualización estándar.
- **Estado `juegoTerminado`** controla si se muestra el HUD normal o la pantalla de victoria.
- **`DecimalFormat("#0.00")`** garantiza `45.33` en lugar de `45.3333333`.
---
### 2️⃣ Modificar `PanelJuego.java`