7. Fin del juego
La room de Game Over
Sección titulada «La room de Game Over»-
Haz clic derecho en la carpeta
roomsy elige Nova room… (nueva room). Ponle el nombreGameOver. -
Crea el objeto
GameOverScreen, sin sprite, y arrástralo a la roomGameOver. -
La room
Mainsigue siendo la primera del juego. En el panel Recursos, aparece como Main (inicial). Eso se cambia en Projeto › Configurações do projeto… › Room inicial (Proyecto › Configuración del proyecto).
Dos acciones para el menú
Sección titulada «Dos acciones para el menú»En Projeto › Configurações do projeto…, en Ações de entrada (acciones de entrada), crea dos acciones más, igual que creaste atirar:
| Acción | Tecla | Botón del mando |
|---|---|---|
confirmar |
Enter | Pad:Start |
sair (salir) |
Esc (elige Escape en la lista Tecla) | Pad:Back |
El Esc cancela Apertar a tecla… (pulsar la tecla), por eso se elige en la lista.
Funciones de apoyo
Sección titulada «Funciones de apoyo»En el game.hpp, agrega dos funciones de apoyo: centeredX y newGame. Esta es la versión final del archivo:
#pragma once#include <string_view>
// Estado que vive entre las rooms: puntos y vidas.struct GameState { int score = 0; int lives = 3;};GameState& state();
// X que centra un texto en pantalla (la fuente del drawText tiene 8 píxeles por letra).float centeredX(std::string_view text, float screenWidth);// Pone el estado en cero y abre la room Main.void newGame();Y el game.cpp gana las dos funciones:
#include "game.hpp"
#include <brasa/brasa.hpp>
GameState& state() { static GameState current; return current;}
float centeredX(std::string_view text, float screenWidth) { return (screenWidth - static_cast<float>(text.size()) * 8.0f) / 2.0f;}
void newGame() { state() = GameState{}; brasa::goToScene("Main");}centeredXcalcula laxque deja un texto centrado. Cada letra deldrawTexttiene 8 píxeles de ancho, así que el texto ocupaletras × 8, y el resto de la pantalla se reparte entre los dos lados.newGamepone el estado en cero (GameState{}vuelve a 0 puntos y 3 vidas) y abre la roomMain. ComonewGameno está dentro de un objeto, usabrasa::goToScene, la versión “de fuera” delgoTo.
La nave abre el Game Over
Sección titulada «La nave abre el Game Over»En el Player.cpp, cuando se acaban las vidas, la nave va a la room GameOver antes de desaparecer. Este es el Player.cpp final:
#include "Player.hpp"
#include "src/game.hpp"
#include <algorithm>
using namespace brasa;
void Player::onSpawn() { animSpeed = 0.2f; // la llama del motor alterna entre los 2 frames // Tocar un Enemy cuesta una vida (y el enemigo desaparece también). onCollide<Enemy>([this](Enemy& enemy) { if (hurt()) enemy.destroy(); });}
void Player::onUpdate() { // Las acciones están en la configuración del proyecto (teclado y mando). // axis devuelve de -1 a 1: -1 = la primera acción, 1 = la segunda. float dx = axis("esquerda", "direita"); float dy = axis("cima", "baixo"); if (dx != 0.0f && dy != 0.0f) { // en diagonal, avanza a la misma velocidad dx *= 0.7071f; dy *= 0.7071f; }
// clamp mantiene la nave dentro de la pantalla (la nave mide 16 px, así que el margen es 8). const float maxX = static_cast<float>(roomWidth()) - 8.0f; const float maxY = static_cast<float>(roomHeight()) - 8.0f; x = std::clamp(x + dx * velocidade, 8.0f, maxX); y = std::clamp(y + dy * velocidade, 8.0f, maxY);
// held(acción) es verdadero mientras la tecla o el botón están pulsados. if (held("atirar") && canShoot_) { spawn<Bullet>(x, y - 8.0f, "Tiros"); // el disparo nace en la capa Tiros, detrás de la nave Audio::play("tiro", {.volume = 0.4f}); canShoot_ = false; after(intervaloTiro, [this] { canShoot_ = true; }); // solo dispara de nuevo dentro de algunos steps } if (invincible_ > 0) --invincible_;}
void Player::onRender() { // Parpadea mientras es invencible: se salta el dibujo en algunos steps. if (invincible_ > 0 && (invincible_ / 4) % 2 == 0) return; drawSelf();}
bool Player::hurt() { if (invincible_ > 0 || isDestroyed()) return false; --state().lives; Audio::play("dano"); if (state().lives <= 0) { goTo("GameOver"); destroy(); } else { invincible_ = 90; // 1,5 s a 60 steps por segundo } return true;}
BRASA_ENTITY(Player);goTo("GameOver") cambia de room: todo lo de la room actual desaparece y empieza la room GameOver, con el GameOverScreen dentro.
La pantalla de fin del juego
Sección titulada «La pantalla de fin del juego»Abre el GameOverScreen.hpp, borra los ejemplos y escribe:
#pragma once#include <brasa/brasa.hpp>
// Pantalla de fin del juego: muestra los puntos y espera "confirmar" (jugar de nuevo) o "sair" (salir).class GameOverScreen : public brasa::Entity {public: BRASA_VARS(GameOverScreen)
void onUpdate() override; void onRenderGui() override;};Y el GameOverScreen.cpp:
#include "GameOverScreen.hpp"
#include "src/game.hpp"
#include <format>#include <string>#include <string_view>
using namespace brasa;
void GameOverScreen::onUpdate() { // pressed(acción) es verdadero solo en el step en que se pulsó la tecla o el botón. if (pressed("confirmar")) newGame(); if (pressed("sair")) quit();}
void GameOverScreen::onRenderGui() { const float screenW = static_cast<float>(roomWidth()); constexpr std::string_view kTitle = "FIM DE JOGO"; constexpr std::string_view kRetry = "ENTER JOGAR DE NOVO"; constexpr std::string_view kQuit = "ESC SAIR"; const std::string score = std::format("PONTOS {}", state().score); drawText(kTitle, centeredX(kTitle, screenW), 60.0f, colors::red); drawText(score, centeredX(score, screenW), 85.0f); drawText(kRetry, centeredX(kRetry, screenW), 120.0f); drawText(kQuit, centeredX(kQuit, screenW), 135.0f, Color{150, 150, 150, 255});}
BRASA_ENTITY(GameOverScreen);pressed("confirmar")es verdadero solo en el paso en que la acción se pulsó, a diferencia delheld, que vale mientras está mantenida. Para menús,pressedes lo correcto: un toque, una acción.quit()cierra el juego.- En el
onRenderGui, cuatro textos centrados concenteredX.Color{150, 150, 150, 255}es un gris: rojo, verde, azul y opacidad, de 0 a 255.
Pruébalo ahora
Sección titulada «Pruébalo ahora»Pulsa Play y juega hasta perder las tres vidas. La pantalla de fin del juego muestra tus puntos. Enter vuelve a empezar desde cero y Esc cierra.
¡Listo: tu primer juego está completo! 🎉
Lo que aprendiste
Sección titulada «Lo que aprendiste»- Un juego puede tener varias rooms, y
goTo("Nombre")cambia de una a otra. Mira Cambiar de room. pressedvale solo en el paso en que se pulsó la acción, yheldmientras está mantenida. Mira Entrada.quit()cierra el juego.- Las funciones de apoyo, como
centeredXynewGame, pueden quedar en elgame.cppy servir a todos los objetos.
¿Y ahora?
Sección titulada «¿Y ahora?»- Cambia los números en el Inspector y mira qué pasa: la
velocidadede la nave, elintervaloTiro, lospontosde cada enemigo. - Marca la casilla de una variable en una instancia (en Valores próprios, valores propios) para darle un valor solo suyo.
- Haz un enemigo nuevo que se mueva en zigzag (pista: cambia el
hspeedconevery). - Haz que la cámara siga a la nave en una room más grande: mira Cámara siguiendo al jugador.
- Consulta las guías de Cómo hacer y la Referencia.
