You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
Web-Dev-For-Beginners/translations/br/6-space-game/3-moving-elements-around/README.md

402 lines
14 KiB

<!--
CO_OP_TRANSLATOR_METADATA:
{
"original_hash": "a9a161871de7706cb0e23b1bd0c74559",
"translation_date": "2025-08-28T23:51:59+00:00",
"source_file": "6-space-game/3-moving-elements-around/README.md",
"language_code": "br"
}
-->
# Construindo um Jogo Espacial Parte 3: Adicionando Movimento
## Quiz Pré-Aula
[Quiz pré-aula](https://ff-quizzes.netlify.app/web/quiz/33)
Jogos não são muito divertidos até que você tenha alienígenas se movendo pela tela! Neste jogo, utilizaremos dois tipos de movimentos:
- **Movimento por teclado/mouse**: quando o usuário interage com o teclado ou mouse para mover um objeto na tela.
- **Movimento induzido pelo jogo**: quando o jogo move um objeto em um determinado intervalo de tempo.
Então, como movemos coisas na tela? Tudo se resume a coordenadas cartesianas: mudamos a localização (x, y) do objeto e, em seguida, redesenhamos a tela.
Normalmente, você precisa dos seguintes passos para realizar o *movimento* na tela:
1. **Definir uma nova localização** para um objeto; isso é necessário para que o objeto pareça ter se movido.
2. **Limpar a tela**, a tela precisa ser limpa entre os desenhos. Podemos limpá-la desenhando um retângulo preenchido com uma cor de fundo.
3. **Redesenhar o objeto** na nova localização. Fazendo isso, finalmente conseguimos mover o objeto de um local para outro.
Veja como isso pode parecer no código:
```javascript
//set the hero's location
hero.x += 5;
// clear the rectangle that hosts the hero
ctx.clearRect(0, 0, canvas.width, canvas.height);
// redraw the game background and hero
ctx.fillRect(0, 0, canvas.width, canvas.height)
ctx.fillStyle = "black";
ctx.drawImage(heroImg, hero.x, hero.y);
```
✅ Você consegue pensar em um motivo pelo qual redesenhar seu herói várias vezes por segundo pode gerar custos de desempenho? Leia sobre [alternativas para este padrão](https://developer.mozilla.org/en-US/docs/Web/API/Canvas_API/Tutorial/Optimizing_canvas).
## Manipulando eventos de teclado
Você manipula eventos anexando eventos específicos ao código. Eventos de teclado são acionados em toda a janela, enquanto eventos de mouse, como um `click`, podem ser conectados a um elemento específico. Usaremos eventos de teclado ao longo deste projeto.
Para manipular um evento, você precisa usar o método `addEventListener()` da janela e fornecer dois parâmetros de entrada. O primeiro parâmetro é o nome do evento, por exemplo, `keyup`. O segundo parâmetro é a função que deve ser invocada como resultado do evento.
Aqui está um exemplo:
```javascript
window.addEventListener('keyup', (evt) => {
// `evt.key` = string representation of the key
if (evt.key === 'ArrowUp') {
// do something
}
})
```
Para eventos de teclado, há duas propriedades no evento que você pode usar para ver qual tecla foi pressionada:
- `key`, que é uma representação em string da tecla pressionada, por exemplo, `ArrowUp`.
- `keyCode`, que é uma representação numérica, por exemplo, `37`, que corresponde a `ArrowLeft`.
✅ A manipulação de eventos de teclado é útil fora do desenvolvimento de jogos. Em que outros usos você consegue pensar para essa técnica?
### Teclas especiais: um alerta
Existem algumas teclas *especiais* que afetam a janela. Isso significa que, se você estiver ouvindo um evento `keyup` e usar essas teclas especiais para mover seu herói, isso também realizará o deslocamento horizontal. Por esse motivo, você pode querer *desativar* esse comportamento padrão do navegador ao construir seu jogo. Você precisará de um código como este:
```javascript
let onKeyDown = function (e) {
console.log(e.keyCode);
switch (e.keyCode) {
case 37:
case 39:
case 38:
case 40: // Arrow keys
case 32:
e.preventDefault();
break; // Space
default:
break; // do not block other keys
}
};
window.addEventListener('keydown', onKeyDown);
```
O código acima garantirá que as teclas de seta e a barra de espaço tenham seu comportamento *padrão* desativado. O mecanismo de *desativação* ocorre quando chamamos `e.preventDefault()`.
## Movimento induzido pelo jogo
Podemos fazer as coisas se moverem sozinhas usando temporizadores, como as funções `setTimeout()` ou `setInterval()`, que atualizam a localização do objeto a cada intervalo de tempo. Veja como isso pode parecer:
```javascript
let id = setInterval(() => {
//move the enemy on the y axis
enemy.y += 10;
})
```
## O loop do jogo
O loop do jogo é um conceito que é essencialmente uma função invocada em intervalos regulares. É chamado de loop do jogo porque tudo o que deve ser visível para o usuário é desenhado dentro do loop. O loop do jogo utiliza todos os objetos do jogo que fazem parte dele, desenhando todos eles, a menos que, por algum motivo, não devam mais fazer parte do jogo. Por exemplo, se um objeto for um inimigo atingido por um laser e explodir, ele não faz mais parte do loop atual do jogo (você aprenderá mais sobre isso em lições subsequentes).
Veja como um loop de jogo pode ser tipicamente expresso em código:
```javascript
let gameLoopId = setInterval(() =>
function gameLoop() {
ctx.clearRect(0, 0, canvas.width, canvas.height);
ctx.fillStyle = "black";
ctx.fillRect(0, 0, canvas.width, canvas.height);
drawHero();
drawEnemies();
drawStaticObjects();
}, 200);
```
O loop acima é invocado a cada `200` milissegundos para redesenhar o canvas. Você pode escolher o melhor intervalo que faça sentido para o seu jogo.
## Continuando o Jogo Espacial
Você pegará o código existente e o expandirá. Comece com o código que você completou na parte I ou use o código em [Parte II - inicial](../../../../6-space-game/3-moving-elements-around/your-work).
- **Movendo o herói**: você adicionará código para garantir que pode mover o herói usando as teclas de seta.
- **Mover inimigos**: você também precisará adicionar código para garantir que os inimigos se movam de cima para baixo em uma determinada taxa.
## Passos recomendados
Localize os arquivos que foram criados para você na subpasta `your-work`. Ela deve conter o seguinte:
```bash
-| assets
-| enemyShip.png
-| player.png
-| index.html
-| app.js
-| package.json
```
Você inicia seu projeto na pasta `your_work` digitando:
```bash
cd your-work
npm start
```
O comando acima iniciará um servidor HTTP no endereço `http://localhost:5000`. Abra um navegador e insira esse endereço. No momento, ele deve renderizar o herói e todos os inimigos; nada está se movendo - ainda!
### Adicione código
1. **Adicione objetos dedicados** para `hero`, `enemy` e `game object`, eles devem ter propriedades `x` e `y`. (Lembre-se da seção sobre [Herança ou composição](../README.md)).
*DICA*: `game object` deve ser o que possui `x` e `y` e a capacidade de se desenhar em um canvas.
> dica: comece adicionando uma nova classe GameObject com seu construtor delineado como abaixo, e depois desenhe-a no canvas:
```javascript
class GameObject {
constructor(x, y) {
this.x = x;
this.y = y;
this.dead = false;
this.type = "";
this.width = 0;
this.height = 0;
this.img = undefined;
}
draw(ctx) {
ctx.drawImage(this.img, this.x, this.y, this.width, this.height);
}
}
```
Agora, estenda este GameObject para criar o Hero e o Enemy.
```javascript
class Hero extends GameObject {
constructor(x, y) {
...it needs an x, y, type, and speed
}
}
```
```javascript
class Enemy extends GameObject {
constructor(x, y) {
super(x, y);
(this.width = 98), (this.height = 50);
this.type = "Enemy";
let id = setInterval(() => {
if (this.y < canvas.height - this.height) {
this.y += 5;
} else {
console.log('Stopped at', this.y)
clearInterval(id);
}
}, 300)
}
}
```
2. **Adicione manipuladores de eventos de teclado** para lidar com a navegação por teclas (mover o herói para cima/baixo/esquerda/direita).
*LEMBRE-SE*: é um sistema cartesiano, o canto superior esquerdo é `0,0`. Também lembre-se de adicionar código para interromper o *comportamento padrão*.
> dica: crie sua função onKeyDown e anexe-a à janela:
```javascript
let onKeyDown = function (e) {
console.log(e.keyCode);
...add the code from the lesson above to stop default behavior
}
};
window.addEventListener("keydown", onKeyDown);
```
Verifique o console do navegador neste ponto e observe as teclas sendo registradas.
3. **Implemente** o [Padrão Pub/Sub](../README.md), isso manterá seu código limpo enquanto você segue as próximas partes.
Para fazer esta última parte, você pode:
1. **Adicionar um ouvinte de eventos** na janela:
```javascript
window.addEventListener("keyup", (evt) => {
if (evt.key === "ArrowUp") {
eventEmitter.emit(Messages.KEY_EVENT_UP);
} else if (evt.key === "ArrowDown") {
eventEmitter.emit(Messages.KEY_EVENT_DOWN);
} else if (evt.key === "ArrowLeft") {
eventEmitter.emit(Messages.KEY_EVENT_LEFT);
} else if (evt.key === "ArrowRight") {
eventEmitter.emit(Messages.KEY_EVENT_RIGHT);
}
});
```
1. **Criar uma classe EventEmitter** para publicar e assinar mensagens:
```javascript
class EventEmitter {
constructor() {
this.listeners = {};
}
on(message, listener) {
if (!this.listeners[message]) {
this.listeners[message] = [];
}
this.listeners[message].push(listener);
}
emit(message, payload = null) {
if (this.listeners[message]) {
this.listeners[message].forEach((l) => l(message, payload));
}
}
}
```
1. **Adicionar constantes** e configurar o EventEmitter:
```javascript
const Messages = {
KEY_EVENT_UP: "KEY_EVENT_UP",
KEY_EVENT_DOWN: "KEY_EVENT_DOWN",
KEY_EVENT_LEFT: "KEY_EVENT_LEFT",
KEY_EVENT_RIGHT: "KEY_EVENT_RIGHT",
};
let heroImg,
enemyImg,
laserImg,
canvas, ctx,
gameObjects = [],
hero,
eventEmitter = new EventEmitter();
```
1. **Inicializar o jogo**
```javascript
function initGame() {
gameObjects = [];
createEnemies();
createHero();
eventEmitter.on(Messages.KEY_EVENT_UP, () => {
hero.y -=5 ;
})
eventEmitter.on(Messages.KEY_EVENT_DOWN, () => {
hero.y += 5;
});
eventEmitter.on(Messages.KEY_EVENT_LEFT, () => {
hero.x -= 5;
});
eventEmitter.on(Messages.KEY_EVENT_RIGHT, () => {
hero.x += 5;
});
}
```
1. **Configurar o loop do jogo**
Refatore a função window.onload para inicializar o jogo e configurar um loop de jogo em um bom intervalo. Você também adicionará um feixe de laser:
```javascript
window.onload = async () => {
canvas = document.getElementById("canvas");
ctx = canvas.getContext("2d");
heroImg = await loadTexture("assets/player.png");
enemyImg = await loadTexture("assets/enemyShip.png");
laserImg = await loadTexture("assets/laserRed.png");
initGame();
let gameLoopId = setInterval(() => {
ctx.clearRect(0, 0, canvas.width, canvas.height);
ctx.fillStyle = "black";
ctx.fillRect(0, 0, canvas.width, canvas.height);
drawGameObjects(ctx);
}, 100)
};
```
5. **Adicione código** para mover inimigos em um determinado intervalo.
Refatore a função `createEnemies()` para criar os inimigos e empurrá-los para a nova classe gameObjects:
```javascript
function createEnemies() {
const MONSTER_TOTAL = 5;
const MONSTER_WIDTH = MONSTER_TOTAL * 98;
const START_X = (canvas.width - MONSTER_WIDTH) / 2;
const STOP_X = START_X + MONSTER_WIDTH;
for (let x = START_X; x < STOP_X; x += 98) {
for (let y = 0; y < 50 * 5; y += 50) {
const enemy = new Enemy(x, y);
enemy.img = enemyImg;
gameObjects.push(enemy);
}
}
}
```
e adicione uma função `createHero()` para fazer um processo semelhante para o herói.
```javascript
function createHero() {
hero = new Hero(
canvas.width / 2 - 45,
canvas.height - canvas.height / 4
);
hero.img = heroImg;
gameObjects.push(hero);
}
```
e, finalmente, adicione uma função `drawGameObjects()` para começar a desenhar:
```javascript
function drawGameObjects(ctx) {
gameObjects.forEach(go => go.draw(ctx));
}
```
Seus inimigos devem começar a avançar em direção à sua nave espacial herói!
---
## 🚀 Desafio
Como você pode ver, seu código pode se transformar em um 'código espaguete' quando você começa a adicionar funções, variáveis e classes. Como você pode organizar melhor seu código para que ele seja mais legível? Esboce um sistema para organizar seu código, mesmo que ele ainda resida em um único arquivo.
## Quiz Pós-Aula
[Quiz pós-aula](https://ff-quizzes.netlify.app/web/quiz/34)
## Revisão e Autoestudo
Embora estejamos escrevendo nosso jogo sem usar frameworks, existem muitos frameworks baseados em JavaScript para desenvolvimento de jogos com canvas. Reserve um tempo para fazer algumas [leituras sobre eles](https://github.com/collections/javascript-game-engines).
## Tarefa
[Comente seu código](assignment.md)
---
**Aviso Legal**:
Este documento foi traduzido utilizando o serviço de tradução por IA [Co-op Translator](https://github.com/Azure/co-op-translator). Embora nos esforcemos para garantir a precisão, esteja ciente de que traduções automatizadas podem conter erros ou imprecisões. O documento original em seu idioma nativo deve ser considerado a fonte autoritativa. 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 equivocadas decorrentes do uso desta tradução.