Pular para o conteúdo
Baixar

4. Inimigos

  1. Crie o objeto Enemy (Novo objeto… na pasta objects). No Inspetor, escolha o sprite inimigo. No painel Camadas, crie mais uma camada de Instâncias, chamada Inimigos, e arraste para baixo de Tiros. Depois clique de novo em Instancias.

  2. Crie o objeto Spawner e deixe sem sprite. Ele não aparece no jogo: o trabalho dele é só criar inimigos.

  3. Arraste o Spawner para a room Main. Pode ser em qualquer lugar; no editor ele aparece como um quadradinho. O Enemy não vai na room: quem cria os inimigos é o Spawner.

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

objects/Enemy.hpp
#pragma once
#include <brasa/brasa.hpp>
// Inimigo que cai do topo da tela.
class Enemy : public brasa::Entity {
public:
BRASA_VARS(Enemy)
void onSpawn() override;
void onUpdate() override;
};

E o Enemy.cpp:

objects/Enemy.cpp
#include "Enemy.hpp"
using namespace brasa;
void Enemy::onSpawn() {
animSpeed = 0.1f;
// random(a, b) sorteia um número entre a e b: cada inimigo cai numa velocidade diferente.
vspeed = random(0.8f, 1.6f);
// Quando um Bullet encosta em mim, o tiro e eu somos destruídos.
// Os outros objetos (Bullet, Player...) já são conhecidos aqui, sem #include.
onCollide<Bullet>([this](Bullet& bullet) {
bullet.destroy();
destroy();
});
}
void Enemy::onUpdate() {
// roomHeight() é a altura da tela; passou dela (com folga), o inimigo escapou.
if (y > static_cast<float>(roomHeight()) + 16.0f) destroy();
}
BRASA_ENTITY(Enemy);
  • random(0.8f, 1.6f) sorteia um número entre 0,8 e 1,6. Cada inimigo cai numa velocidade diferente, o que deixa o jogo menos previsível.
  • onCollide<Bullet>(...) diz: “quando eu encostar num Bullet, faça isto”. A Brasa confere as caixas de colisão a cada passo e chama a lambda. O tiro que acertou chega como bullet, e os dois são destruídos.
  • No onUpdate, o inimigo que passou do fim da tela é destruído.

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

objects/Spawner.hpp
#pragma once
#include <brasa/brasa.hpp>
// Cria inimigos no topo da tela, cada vez mais rápido.
class Spawner : public brasa::Entity {
public:
BRASA_VARS(Spawner)
void onSpawn() override;
private:
void scheduleNext(); // agenda o próximo inimigo
int spawned_ = 0;
};

O Spawner tem uma função só dele, scheduleNext, e conta quantos inimigos já criou em spawned_.

Agora o Spawner.cpp:

objects/Spawner.cpp
#include "Spawner.hpp"
#include <algorithm>
using namespace brasa;
// O Spawner não precisa de onUpdate: ele agenda o próximo inimigo com after(),
// que chama a função depois de N steps (a 60 steps por segundo).
void Spawner::onSpawn() { scheduleNext(); }
void Spawner::scheduleNext() {
// O intervalo encurta aos poucos (de 1 s até no mínimo 0,25 s): o jogo fica mais difícil.
const int interval = std::max(15, 60 - spawned_ * 2);
after(interval, [this] {
++spawned_;
const float margin = 16.0f;
spawn<Enemy>(random(margin, static_cast<float>(roomWidth()) - margin), -16.0f, "Inimigos");
scheduleNext(); // agenda o seguinte
});
}
BRASA_ENTITY(Spawner);

Como ele funciona:

  1. Quando o Spawner nasce, onSpawn chama scheduleNext().
  2. scheduleNext calcula um intervalo: 60 passos (1 segundo) no começo, 2 passos a menos a cada inimigo, até o mínimo de 15. std::max(15, ...) garante esse mínimo.
  3. after(interval, ...) espera esse tempo e cria um Enemy numa posição x sorteada, logo acima da tela (y = -16).
  4. Depois chama scheduleNext() de novo, e o ciclo continua para sempre.

Aperte Play. Os inimigos começam a cair, cada vez mais rápido. Atire neles: tiro e inimigo somem juntos. Por enquanto, encostar na nave não faz nada; isso vem na próxima parte.

  • Um objeto sem sprite pode ter um trabalho invisível, como o Spawner.
  • random(a, b) sorteia números. Veja Sorteio.
  • onCollide<Objeto>(...) reage quando dois objetos se encostam. Veja Colisões.
  • Uma função que se agenda de novo com after cria uma repetição com intervalo variável.

Próxima parte: 5. Pontos e vidas →