Pular para o conteúdo
Baixar

3. Tiros

  1. Em Recursos, botão direito na pasta objects › Novo objeto…. Dê o nome Bullet.

  2. No Inspetor, escolha o sprite tiro.

  3. No painel Camadas (à esquerda, embaixo de Recursos), clique em + Camada › Instâncias. Dê dois cliques no nome e chame de Tiros. Arraste essa camada para baixo de Instancias: a camada de cima fica na frente, então os tiros vão aparecer atrás da nave. Depois clique de novo em Instancias, para ela voltar a ser a camada ativa. Veja Camadas.

Desta vez não coloque o Bullet na room: os tiros vão ser criados pelo código, na hora em que a nave atirar.

Abra o Bullet.hpp, apague os exemplos e escreva:

objects/Bullet.hpp
#pragma once
#include <brasa/brasa.hpp>
// O tiro da nave: sobe até sair da tela.
class Bullet : public brasa::Entity {
public:
BRASA_VARS(Bullet)
void onSpawn() override;
void onUpdate() override;
};

Depois o Bullet.cpp:

objects/Bullet.cpp
#include "Bullet.hpp"
using namespace brasa;
// onSpawn roda uma vez, quando o tiro nasce. vspeed negativo = sobe na tela.
// A engine move o tiro sozinha a cada step, sem precisar de código no onUpdate.
void Bullet::onSpawn() { vspeed = -5.0f; }
void Bullet::onUpdate() {
// Saiu pelo topo? Destruímos para não acumular tiros invisíveis.
if (y < -8.0f) destroy();
}
BRASA_ENTITY(Bullet);
  • vspeed é a velocidade vertical, em pixels por passo. A Brasa soma vspeed ao y sozinha a cada passo. Na tela, o y cresce para baixo, então -5 faz o tiro subir.
  • Quando o tiro passa do topo da tela (y < -8), destroy() remove essa instância do jogo. Sem isso, os tiros continuariam existindo para sempre, fora da tela.

O projeto já tem as ações de direção. Falta uma para o tiro:

  1. Abra Projeto › Configurações do projeto… e vá até Ações de entrada.

  2. Clique em + Ação e dê o nome atirar.

  3. Na linha dela, clique no +, escolha Apertar a tecla… e aperte a barra de espaço.

  4. Se quiser atirar também com o controle, clique no + de novo e escolha Botão do controle › Pad:A.

  5. Clique em Salvar.

Sem uma pausa, a nave soltaria 60 tiros por segundo. Crie mais uma variável para controlar isso: no Inspetor do Player, + Variável › inteiro, com o nome intervaloTiro e o valor 10. São 10 passos entre um tiro e outro.

No Player.hpp, acrescente a parte private, com uma variável que diz se a nave pode atirar. O arquivo fica assim:

objects/Player.hpp
#pragma once
#include <brasa/brasa.hpp>
// A nave. velocidade e intervaloTiro são variáveis do objeto: mude no Inspetor, sem mexer no código.
class Player : public brasa::Entity {
public:
BRASA_VARS(Player)
void onSpawn() override;
void onUpdate() override;
private:
bool canShoot_ = true; // vira false por intervaloTiro steps depois de cada tiro
};

canShoot_ pertence à nave. Ela começa true (pode atirar) e vai virar false por um instante depois de cada tiro. O private: diz que só o próprio Player mexe nela.

No Player.cpp, acrescente o bloco do tiro no fim do onUpdate. O arquivo inteiro fica assim:

objects/Player.cpp
#include "Player.hpp"
#include <algorithm>
using namespace brasa;
void Player::onSpawn() {
animSpeed = 0.2f; // a chama do motor alterna entre os 2 frames
}
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
canShoot_ = false;
after(intervaloTiro, [this] { canShoot_ = true; }); // só atira de novo daqui a alguns steps
}
}
BRASA_ENTITY(Player);

O bloco novo, linha por linha:

  • held("atirar") && canShoot_: a ação atirar está segurada e pode atirar.
  • spawn<Bullet>(x, y - 8.0f, "Tiros") cria um tiro na posição da nave, um pouco acima (8 pixels), na camada Tiros.
  • canShoot_ = false bloqueia novos tiros.
  • after(intervaloTiro, ...) agenda algo para daqui a intervaloTiro passos (10 passos, um sexto de segundo). Quando chegar a hora, canShoot_ volta a ser true.

Repare que o Player.cpp usa o Bullet sem nenhum #include a mais: na Brasa, todo objeto enxerga todos os outros.

Aperte Play. Segure a barra de espaço: a nave dispara uma rajada de tiros que sobem pela tela.

Experimente mudar o intervaloTiro no Inspetor para 3 ou para 30 e veja a diferença.

  • Objetos podem ser criados pelo código com spawn<Objeto>(x, y, "Camada"). Veja Atirar.
  • hspeed e vspeed movem o objeto sozinhos a cada passo.
  • destroy() remove a instância do jogo.
  • Você cria as suas próprias ações nas Configurações do projeto e pergunta por elas com held.
  • after(passos, ...) executa algo mais tarde. Veja Esperar e repetir.

Próxima parte: 4. Inimigos →