3. Shots
Create the Bullet object
Section titled “Create the Bullet object”-
In Recursos (Resources), right-click the
objectsfolder › Novo objeto… (new object). Name itBullet. -
In the Inspetor (Inspector), choose the sprite
tiro. -
In the Camadas (layers) panel (on the left, below Recursos), click + Camada › Instâncias. Double-click the name and call it
Tiros. Drag this layer belowInstancias: the layer on top is in front, so the shots will show up behind the ship. Then clickInstanciasagain, so it is the active layer once more. See Layers.
This time, do not place Bullet in the room: the shots will be created by the code, at the moment the ship shoots.
The code of the shot
Section titled “The code of the shot”Open Bullet.hpp, delete the examples and write:
#pragma once#include <brasa/brasa.hpp>
// The ship's shot: it goes up until it leaves the screen.class Bullet : public brasa::Entity {public: BRASA_VARS(Bullet)
void onSpawn() override; void onUpdate() override;};Then Bullet.cpp:
#include "Bullet.hpp"
using namespace brasa;
// onSpawn runs once, when the shot is created. Negative vspeed = goes up the screen.// The engine moves the shot by itself on every step, with no code needed in onUpdate.void Bullet::onSpawn() { vspeed = -5.0f; }
void Bullet::onUpdate() { // Left through the top? We destroy it so invisible shots do not pile up. if (y < -8.0f) destroy();}
BRASA_ENTITY(Bullet);vspeedis the vertical speed, in pixels per step. Brasa addsvspeedtoyby itself on every step. On screen,ygrows downward, so-5makes the shot go up.- When the shot passes the top of the screen (
y < -8),destroy()removes that instance from the game. Without it, the shots would keep existing forever, off screen.
The shoot action
Section titled “The shoot action”The project already has the direction actions. One is missing for the shot:
-
Open Projeto › Configurações do projeto… (Project › Project settings) and go to Ações de entrada (input actions).
-
Click + Ação (add action) and name it
atirar(shoot). -
On its row, click the +, choose Apertar a tecla… (press the key) and press the space bar.
-
If you also want to shoot with the gamepad, click the + again and choose Botão do controle › Pad:A (gamepad button).
-
Click Salvar (save).
The interval between shots
Section titled “The interval between shots”Without a pause, the ship would fire 60 shots per second. Create one more variable to control that: in the Inspetor of Player, + Variável › inteiro (integer), with the name intervaloTiro (shot interval) and the value 10. That is 10 steps between one shot and the next.
In Player.hpp, add the private part, with a variable that says whether the ship can shoot. The file ends up like this:
#pragma once#include <brasa/brasa.hpp>
// The ship. velocidade and intervaloTiro are variables of the object: change them in the Inspector, without touching the code.class Player : public brasa::Entity {public: BRASA_VARS(Player)
void onSpawn() override; void onUpdate() override;
private: bool canShoot_ = true; // turns false for intervaloTiro steps after each shot};canShoot_ belongs to the ship. It starts true (can shoot) and will turn false for a moment after each shot. private: says that only Player itself touches it.
In Player.cpp, add the shooting block at the end of onUpdate. The whole file ends up like this:
#include "Player.hpp"
#include <algorithm>
using namespace brasa;
void Player::onSpawn() { animSpeed = 0.2f; // the engine flame alternates between the 2 frames}
void Player::onUpdate() { // The actions are in the project settings (keyboard and gamepad). // axis returns from -1 to 1: -1 = the first action, 1 = the second. float dx = axis("esquerda", "direita"); float dy = axis("cima", "baixo"); if (dx != 0.0f && dy != 0.0f) { // on the diagonal, it moves at the same speed dx *= 0.7071f; dy *= 0.7071f; }
// clamp keeps the ship inside the screen (the ship is 16 px, so the margin is 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(action) is true while the key or the button is pressed. if (held("atirar") && canShoot_) { spawn<Bullet>(x, y - 8.0f, "Tiros"); // the shot is born in the Tiros layer, behind the ship canShoot_ = false; after(intervaloTiro, [this] { canShoot_ = true; }); // only shoots again a few steps from now }}
BRASA_ENTITY(Player);The new block, line by line:
held("atirar") && canShoot_: the actionatiraris held and it can shoot.spawn<Bullet>(x, y - 8.0f, "Tiros")creates a shot at the position of the ship, a little above it (8 pixels), in theTiroslayer.canShoot_ = falseblocks new shots.after(intervaloTiro, ...)schedules something forintervaloTirosteps from now (10 steps, one sixth of a second). When the time comes,canShoot_becomestrueagain.
Notice that Player.cpp uses Bullet with no extra #include: in Brasa, every object sees all the others.
Test it now
Section titled “Test it now”Press Play. Hold the space bar: the ship fires a burst of shots that go up the screen.
Try changing intervaloTiro in the Inspector to 3 or to 30 and see the difference.
What you learned
Section titled “What you learned”- Objects can be created by the code with
spawn<Object>(x, y, "Camada"). See Shoot. hspeedandvspeedmove the object by themselves on every step.destroy()removes the instance from the game.- You create your own actions in the project settings and ask about them with
held. after(steps, ...)runs something later. See Wait and repeat.
Next part: 4. Enemies →
