Modo Un Jugador - vykymoon/rpg-character-creator GitHub Wiki

Desde la galería, Jugar ▶ abre un mapa donde el personaje elegido se mueve libremente y puede entrar en un encuentro. Paquete: com.proyecto.rpg.game + ui/PlayController.

Controles

Tecla Acción
W / A / S / D Mover arriba / izquierda / abajo / derecha
E Abrir/cerrar el inventario (mientras está abierto no hay movimiento)

Componentes

Clase Rol
PlayController Controlador de play.fxml: crea el contexto, el loop y el renderizado
PlayContext Agrupa Player, GameMap y si el inventario está abierto
Player Posición (x, y) en píxeles, tamaño 40 px, velocidad 180 px/s
game.GameMap Límites del mapa; clampX/clampY impiden salirse
InputHandler Teclas pulsadas → Command (patrón Command)
Command / MoveCommand MoveCommand(dx, dy) mueve velocidad × Δt píxeles
MapRenderer Dibuja /sprites/map/background.jpg (o limpia el lienzo si falta)
EncounterZone Zona elíptica que dispara el encuentro
InventoryOverlay Panel de inventario estilo pergamino

Game loop

PlayController usa un AnimationTimer. En cada frame:

Δt = (now - lastNanos) / 1e9
inputHandler.tick(context, Δt)   // ejecuta MoveCommand por cada tecla pulsada
render()                         // fondo + zona + avatar del jugador
checkEncounter()                 // ¿el centro del jugador está en la zona?

El movimiento usa delta time, por lo que la velocidad es la misma sin importar los FPS.

Inicialización

  • El mapa tiene el tamaño del Canvas.
  • El jugador aparece en el centro del mapa.
  • La EncounterZone (90×70 px) se coloca centrada abajo, a 20 px del borde inferior, como "salida" al final del camino.
  • El avatar del jugador es la foto de su raza (RaceAvatarLoader).

Inventario

InventoryOverlay es un VBox (máx. 340×440) superpuesto sobre el mapa con:

  • Cabecera: foto de la raza, nombre y "Raza · Clase".
  • Grilla VESTUARIO con un slot de 56 px por pieza equipada (sprite vía SpriteLoader) y tooltip. Si no hay piezas: "Sin vestuario equipado".

Encuentro y combate

Cuando el centro del jugador entra en la EncounterZone, el loop se detiene y se navega a battle.fxml con BattleController.

  • Muestra "¡Un enemigo apareció!", la imagen del enemigo y su nombre.
  • El enemigo se define solo por el nombre de su PNG en /sprites/battle/ (por defecto enemy.png). El nombre visible sale del archivo: lobo_negro.png → "Un lobo negro". Si falta la imagen, se oculta sin dejar hueco.
  • Para otro enemigo: new BattleController(character, "lobo_negro.png").
  • Huir vuelve a la galería.
  • Combatir es un placeholder (TODO): la lógica de turnos no está implementada.

Salir

El botón "atrás" (onBack) detiene el loop y vuelve a la galería.