7. Fim de jogo
A room de Game Over
Seção intitulada “A room de Game Over”-
Clique com o botão direito na pasta
roomse escolha Nova room…. Dê o nomeGameOver. -
Crie o objeto
GameOverScreen, sem sprite, e arraste-o para a roomGameOver. -
A room
Maincontinua sendo a primeira do jogo. No painel Recursos, ela aparece como Main (inicial). Isso se muda em Projeto › Configurações do projeto… › Room inicial.
Duas ações para o menu
Seção intitulada “Duas ações para o menu”Em Projeto › Configurações do projeto…, em Ações de entrada, crie mais duas ações, do mesmo jeito que você criou a atirar:
| Ação | Tecla | Botão do controle |
|---|---|---|
confirmar |
Enter | Pad:Start |
sair |
Esc (escolha Escape na lista Tecla) | Pad:Back |
O Esc cancela o Apertar a tecla…, por isso ele é escolhido pela lista.
Funções de apoio
Seção intitulada “Funções de apoio”No game.hpp, acrescente duas funções de apoio: centeredX e newGame. Esta é a versão final do arquivo:
#pragma once#include <string_view>
// Estado que vive entre as rooms: pontos e vidas.struct GameState { int score = 0; int lives = 3;};GameState& state();
// X que centraliza um texto na tela (a fonte do drawText tem 8 pixels por letra).float centeredX(std::string_view text, float screenWidth);// Zera o estado e abre a room Main.void newGame();E o game.cpp ganha as duas funções:
#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 oxque deixa um texto centralizado. Cada letra dodrawTexttem 8 pixels de largura, então o texto ocupaletras × 8, e o resto da tela é dividido entre os dois lados.newGamezera o estado (GameState{}volta para 0 pontos e 3 vidas) e abre a roomMain. ComonewGamenão está dentro de um objeto, ela usabrasa::goToScene, a versão “de fora” dogoTo.
A nave abre o Game Over
Seção intitulada “A nave abre o Game Over”No Player.cpp, quando as vidas acabam, a nave vai para a room GameOver antes de sumir. Este é o Player.cpp final:
#include "Player.hpp"
#include "src/game.hpp"
#include <algorithm>
using namespace brasa;
void Player::onSpawn() { animSpeed = 0.2f; // a chama do motor alterna entre os 2 frames // Encostar num Enemy custa uma vida (e o inimigo some junto). onCollide<Enemy>([this](Enemy& enemy) { if (hurt()) enemy.destroy(); });}
void Player::onUpdate() { // As ações ficam em Configurações do projeto (teclado e controle). // axis devolve de -1 a 1: -1 = a primeira ação, 1 = a segunda. float dx = axis("esquerda", "direita"); float dy = axis("cima", "baixo"); if (dx != 0.0f && dy != 0.0f) { // na diagonal, anda com a mesma velocidade dx *= 0.7071f; dy *= 0.7071f; }
// clamp mantém a nave dentro da tela (a nave tem 16 px, então a margem é 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(ação) é verdadeiro enquanto a tecla ou o botão estão pressionados. if (held("atirar") && canShoot_) { spawn<Bullet>(x, y - 8.0f, "Tiros"); // o tiro nasce na camada Tiros, atrás da nave Audio::play("tiro", {.volume = 0.4f}); canShoot_ = false; after(intervaloTiro, [this] { canShoot_ = true; }); // só atira de novo daqui a alguns steps } if (invincible_ > 0) --invincible_;}
void Player::onRender() { // Pisca enquanto está invencível: pula o desenho em alguns 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") troca de room: tudo da room atual some e a room GameOver começa, com o GameOverScreen dentro.
A tela de fim de jogo
Seção intitulada “A tela de fim de jogo”Abra o GameOverScreen.hpp, apague os exemplos e escreva:
#pragma once#include <brasa/brasa.hpp>
// Tela de fim de jogo: mostra os pontos e espera "confirmar" (jogar de novo) ou "sair".class GameOverScreen : public brasa::Entity {public: BRASA_VARS(GameOverScreen)
void onUpdate() override; void onRenderGui() override;};E o GameOverScreen.cpp:
#include "GameOverScreen.hpp"
#include "src/game.hpp"
#include <format>#include <string>#include <string_view>
using namespace brasa;
void GameOverScreen::onUpdate() { // pressed(ação) é verdadeiro só no step em que a tecla ou o botão foram apertados. 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")é verdadeiro só no passo em que a ação foi apertada, diferente doheld, que vale enquanto ela está segurada. Para menus,pressedé o certo: um toque, uma ação.quit()fecha o jogo.- No
onRenderGui, quatro textos centralizados comcenteredX.Color{150, 150, 150, 255}é um cinza: vermelho, verde, azul e opacidade, de 0 a 255.
Teste agora
Seção intitulada “Teste agora”Aperte Play e jogue até perder as três vidas. A tela de fim de jogo mostra os seus pontos. Enter recomeça do zero e Esc fecha.
Pronto: o seu primeiro jogo está completo! 🎉
O que você aprendeu
Seção intitulada “O que você aprendeu”- Um jogo pode ter várias rooms, e
goTo("Nome")troca de uma para outra. Veja Trocar de room. pressedvale só no passo em que a ação foi apertada, eheldenquanto ela está segurada. Veja Entrada.quit()fecha o jogo.- Funções de apoio, como
centeredXenewGame, podem ficar nogame.cppe servir para todos os objetos.
E agora?
Seção intitulada “E agora?”- Mude os números no Inspetor e veja o que acontece: a
velocidadeda nave, ointervaloTiro, ospontosde cada inimigo. - Marque a caixinha de uma variável numa instância (em Valores próprios) para dar a ela um valor só dela.
- Faça um inimigo novo que anda em zigue-zague (dica: mexa no
hspeedcomevery). - Faça a câmera seguir a nave numa room maior: veja Câmera seguindo o jogador.
- Consulte os guias de Como fazer e a Referência.
