Ir al contenido
Descargar

4. Enemigos

  1. Crea el objeto Enemy (Novo objeto…, nuevo objeto, en la carpeta objects). En el Inspetor (Inspector), elige el sprite inimigo. En el panel Camadas (capas), crea una capa más de Instâncias, llamada Inimigos, y arrástrala debajo de Tiros. Después haz clic de nuevo en Instancias.

  2. Crea el objeto Spawner y déjalo sin sprite. No aparece en el juego: su trabajo es solo crear enemigos.

  3. Arrastra el Spawner a la room Main. Puede ser en cualquier lugar; en el editor aparece como un cuadradito. El Enemy no va en la room: quien crea los enemigos es el Spawner.

Abre el Enemy.hpp, borra los ejemplos y escribe:

objects/Enemy.hpp
#pragma once
#include <brasa/brasa.hpp>
// Enemigo que cae desde arriba de la pantalla.
class Enemy : public brasa::Entity {
public:
BRASA_VARS(Enemy)
void onSpawn() override;
void onUpdate() override;
};

Y el Enemy.cpp:

objects/Enemy.cpp
#include "Enemy.hpp"
using namespace brasa;
void Enemy::onSpawn() {
animSpeed = 0.1f;
// random(a, b) sortea un número entre a y b: cada enemigo cae a una velocidad distinta.
vspeed = random(0.8f, 1.6f);
// Cuando un Bullet me toca, el disparo y yo somos destruidos.
// Los demás objetos (Bullet, Player...) ya son conocidos aquí, sin #include.
onCollide<Bullet>([this](Bullet& bullet) {
bullet.destroy();
destroy();
});
}
void Enemy::onUpdate() {
// roomHeight() es la altura de la pantalla; si la pasó (con margen), el enemigo escapó.
if (y > static_cast<float>(roomHeight()) + 16.0f) destroy();
}
BRASA_ENTITY(Enemy);
  • random(0.8f, 1.6f) sortea un número entre 0,8 y 1,6. Cada enemigo cae a una velocidad distinta, lo que hace el juego menos predecible.
  • onCollide<Bullet>(...) dice: “cuando toque un Bullet, haz esto”. Brasa revisa las cajas de colisión en cada paso y llama a la lambda. El disparo que acertó llega como bullet, y los dos son destruidos.
  • En el onUpdate, el enemigo que pasó el final de la pantalla es destruido.

Abre el Spawner.hpp, borra los ejemplos y escribe:

objects/Spawner.hpp
#pragma once
#include <brasa/brasa.hpp>
// Crea enemigos en la parte de arriba de la pantalla, cada vez más rápido.
class Spawner : public brasa::Entity {
public:
BRASA_VARS(Spawner)
void onSpawn() override;
private:
void scheduleNext(); // programa el próximo enemigo
int spawned_ = 0;
};

El Spawner tiene una función solo suya, scheduleNext, y cuenta cuántos enemigos ya creó en spawned_.

Ahora el Spawner.cpp:

objects/Spawner.cpp
#include "Spawner.hpp"
#include <algorithm>
using namespace brasa;
// El Spawner no necesita onUpdate: programa el próximo enemigo con after(),
// que llama a la función después de N steps (a 60 steps por segundo).
void Spawner::onSpawn() { scheduleNext(); }
void Spawner::scheduleNext() {
// El intervalo se acorta poco a poco (de 1 s hasta un mínimo de 0,25 s): el juego se vuelve más 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(); // programa el siguiente
});
}
BRASA_ENTITY(Spawner);

Cómo funciona:

  1. Cuando nace el Spawner, onSpawn llama a scheduleNext().
  2. scheduleNext calcula un intervalo: 60 pasos (1 segundo) al principio, 2 pasos menos con cada enemigo, hasta el mínimo de 15. std::max(15, ...) garantiza ese mínimo.
  3. after(interval, ...) espera ese tiempo y crea un Enemy en una posición x sorteada, justo encima de la pantalla (y = -16).
  4. Después llama a scheduleNext() de nuevo, y el ciclo sigue para siempre.

Pulsa Play. Los enemigos empiezan a caer, cada vez más rápido. Dispárales: disparo y enemigo desaparecen juntos. Por ahora, tocar la nave no hace nada; eso viene en la siguiente parte.

  • Un objeto sin sprite puede tener un trabajo invisible, como el Spawner.
  • random(a, b) sortea números. Mira Números al azar.
  • onCollide<Objeto>(...) reacciona cuando dos objetos se tocan. Mira Colisiones.
  • Una función que se programa de nuevo con after crea una repetición con intervalo variable.

Siguiente parte: 5. Puntos y vidas →