12 KiB
Construir um Jogo Espacial Parte 4: Adicionar um Laser e Detetar Colisões
Questionário Pré-Aula
Nesta lição vais aprender a disparar lasers com JavaScript! Vamos adicionar duas coisas ao nosso jogo:
- Um laser: este laser é disparado da nave do herói e segue verticalmente para cima.
- Deteção de colisões, como parte da implementação da capacidade de disparar, também vamos adicionar algumas regras interessantes ao jogo:
- Laser atinge inimigo: O inimigo morre se for atingido por um laser.
- Laser atinge o topo do ecrã: Um laser é destruído se atingir a parte superior do ecrã.
- Colisão entre inimigo e herói: Um inimigo e o herói são destruídos se colidirem um com o outro.
- Inimigo atinge o fundo do ecrã: Um inimigo e o herói são destruídos se o inimigo atingir o fundo do ecrã.
Resumindo, tu -- o herói -- precisas de atingir todos os inimigos com um laser antes que eles consigam chegar ao fundo do ecrã.
✅ Faz uma pequena pesquisa sobre o primeiro jogo de computador alguma vez criado. Qual era a sua funcionalidade?
Vamos ser heróicos juntos!
Deteção de colisões
Como fazemos a deteção de colisões? Precisamos de pensar nos objetos do jogo como retângulos em movimento. Porquê, perguntas tu? Bem, a imagem usada para desenhar um objeto do jogo é um retângulo: tem x
, y
, largura
e altura
.
Se dois retângulos, ou seja, um herói e um inimigo intersetarem, tens uma colisão. O que deve acontecer a seguir depende das regras do jogo. Para implementar a deteção de colisões, precisas do seguinte:
-
Uma forma de obter uma representação em retângulo de um objeto do jogo, algo como isto:
rectFromGameObject() { return { top: this.y, left: this.x, bottom: this.y + this.height, right: this.x + this.width } }
-
Uma função de comparação, que pode ser assim:
function intersectRect(r1, r2) { return !(r2.left > r1.right || r2.right < r1.left || r2.top > r1.bottom || r2.bottom < r1.top); }
Como destruímos coisas
Para destruir coisas num jogo, precisas de informar o jogo que não deve mais pintar esse item no ciclo de jogo que é acionado em intervalos específicos. Uma forma de fazer isso é marcar um objeto do jogo como morto quando algo acontece, assim:
// collision happened
enemy.dead = true
Depois podes proceder para filtrar os objetos mortos antes de repintar o ecrã, assim:
gameObjects = gameObject.filter(go => !go.dead);
Como disparamos um laser
Disparar um laser traduz-se em responder a um evento de tecla e criar um objeto que se move numa direção específica. Por isso, precisamos de realizar os seguintes passos:
- Criar um objeto laser: a partir do topo da nave do herói, que ao ser criado começa a mover-se para cima em direção ao topo do ecrã.
- Associar código a um evento de tecla: precisamos de escolher uma tecla no teclado que represente o jogador a disparar o laser.
- Criar um objeto do jogo que se pareça com um laser quando a tecla é pressionada.
Intervalo de disparo do laser
O laser precisa de ser disparado sempre que pressionas uma tecla, como espaço, por exemplo. Para evitar que o jogo produza demasiados lasers num curto espaço de tempo, precisamos de corrigir isso. A solução é implementar um chamado intervalo de disparo, um temporizador, que garante que um laser só pode ser disparado de tempos em tempos. Podes implementar isso da seguinte forma:
class Cooldown {
constructor(time) {
this.cool = false;
setTimeout(() => {
this.cool = true;
}, time)
}
}
class Weapon {
constructor {
}
fire() {
if (!this.cooldown || this.cooldown.cool) {
// produce a laser
this.cooldown = new Cooldown(500);
} else {
// do nothing - it hasn't cooled down yet.
}
}
}
✅ Consulta a lição 1 da série de jogos espaciais para te lembrares sobre intervalos de disparo.
O que construir
Vais pegar no código existente (que deves ter limpo e refatorado) da lição anterior e expandi-lo. Podes começar com o código da parte II ou usar o código em Parte III - inicial.
dica: o laser com que vais trabalhar já está na tua pasta de recursos e referenciado pelo teu código.
- Adicionar deteção de colisões, quando um laser colide com algo, as seguintes regras devem ser aplicadas:
- Laser atinge inimigo: o inimigo morre se for atingido por um laser.
- Laser atinge o topo do ecrã: Um laser é destruído se atingir a parte superior do ecrã.
- Colisão entre inimigo e herói: um inimigo e o herói são destruídos se colidirem um com o outro.
- Inimigo atinge o fundo do ecrã: Um inimigo e o herói são destruídos se o inimigo atingir o fundo do ecrã.
Passos recomendados
Localiza os ficheiros que foram criados para ti na subpasta your-work
. Deve conter o seguinte:
-| assets
-| enemyShip.png
-| player.png
-| laserRed.png
-| index.html
-| app.js
-| package.json
Inicia o teu projeto na pasta your_work
digitando:
cd your-work
npm start
O comando acima iniciará um servidor HTTP no endereço http://localhost:5000
. Abre um navegador e insere esse endereço, neste momento deve renderizar o herói e todos os inimigos, nada está a mover-se - ainda :).
Adicionar código
-
Configurar uma representação em retângulo do teu objeto do jogo, para lidar com colisões O código abaixo permite-te obter uma representação em retângulo de um
GameObject
. Edita a tua classe GameObject para expandi-la:rectFromGameObject() { return { top: this.y, left: this.x, bottom: this.y + this.height, right: this.x + this.width, }; }
-
Adicionar código que verifica colisões Isto será uma nova função que testa se dois retângulos se intersetam:
function intersectRect(r1, r2) { return !( r2.left > r1.right || r2.right < r1.left || r2.top > r1.bottom || r2.bottom < r1.top ); }
-
Adicionar capacidade de disparar laser
-
Adicionar mensagem de evento de tecla. A tecla espaço deve criar um laser logo acima da nave do herói. Adiciona três constantes no objeto Messages:
KEY_EVENT_SPACE: "KEY_EVENT_SPACE", COLLISION_ENEMY_LASER: "COLLISION_ENEMY_LASER", COLLISION_ENEMY_HERO: "COLLISION_ENEMY_HERO",
-
Lidar com a tecla espaço. Edita a função
window.addEventListener
keyup para lidar com espaços:} else if(evt.keyCode === 32) { eventEmitter.emit(Messages.KEY_EVENT_SPACE); }
-
Adicionar ouvintes. Edita a função
initGame()
para garantir que o herói pode disparar quando a barra de espaço é pressionada:eventEmitter.on(Messages.KEY_EVENT_SPACE, () => { if (hero.canFire()) { hero.fire(); }
e adiciona uma nova função
eventEmitter.on()
para garantir o comportamento quando um inimigo colide com um laser:eventEmitter.on(Messages.COLLISION_ENEMY_LASER, (_, { first, second }) => { first.dead = true; second.dead = true; })
-
Mover objeto, Garante que o laser se move gradualmente para o topo do ecrã. Vais criar uma nova classe Laser que expande
GameObject
, como já fizeste antes:class Laser extends GameObject { constructor(x, y) { super(x,y); (this.width = 9), (this.height = 33); this.type = 'Laser'; this.img = laserImg; let id = setInterval(() => { if (this.y > 0) { this.y -= 15; } else { this.dead = true; clearInterval(id); } }, 100) } }
-
Lidar com colisões, Implementa regras de colisão para o laser. Adiciona uma função
updateGameObjects()
que testa objetos em colisão:function updateGameObjects() { const enemies = gameObjects.filter(go => go.type === 'Enemy'); const lasers = gameObjects.filter((go) => go.type === "Laser"); // laser hit something lasers.forEach((l) => { enemies.forEach((m) => { if (intersectRect(l.rectFromGameObject(), m.rectFromGameObject())) { eventEmitter.emit(Messages.COLLISION_ENEMY_LASER, { first: l, second: m, }); } }); }); gameObjects = gameObjects.filter(go => !go.dead); }
Certifica-te de adicionar
updateGameObjects()
no teu ciclo de jogo emwindow.onload
. -
Implementar intervalo de disparo no laser, para que só possa ser disparado de tempos em tempos.
Finalmente, edita a classe Hero para que possa ter intervalo de disparo:
class Hero extends GameObject { constructor(x, y) { super(x, y); (this.width = 99), (this.height = 75); this.type = "Hero"; this.speed = { x: 0, y: 0 }; this.cooldown = 0; } fire() { gameObjects.push(new Laser(this.x + 45, this.y - 10)); this.cooldown = 500; let id = setInterval(() => { if (this.cooldown > 0) { this.cooldown -= 100; } else { clearInterval(id); } }, 200); } canFire() { return this.cooldown === 0; } }
-
Neste ponto, o teu jogo já tem alguma funcionalidade! Podes navegar com as teclas de seta, disparar um laser com a barra de espaço, e os inimigos desaparecem quando os atinges. Muito bem!
🚀 Desafio
Adiciona uma explosão! Dá uma olhada nos recursos do jogo no repositório Space Art e tenta adicionar uma explosão quando o laser atinge um alienígena.
Questionário Pós-Aula
Revisão e Estudo Individual
Experimenta com os intervalos no teu jogo até agora. O que acontece quando os alteras? Lê mais sobre eventos de temporização em JavaScript.
Tarefa
Aviso Legal:
Este documento foi traduzido utilizando o serviço de tradução por IA Co-op Translator. Embora nos esforcemos para garantir a precisão, é importante ter em conta que traduções automáticas podem conter erros ou imprecisões. O documento original na sua língua nativa deve ser considerado a fonte autoritária. Para informações críticas, recomenda-se a tradução profissional realizada por humanos. Não nos responsabilizamos por quaisquer mal-entendidos ou interpretações incorretas decorrentes da utilização desta tradução.