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/pt/6-space-game/3-moving-elements-around/README.md

402 lines
14 KiB

<!--
CO_OP_TRANSLATOR_METADATA:
{
"original_hash": "a9a161871de7706cb0e23b1bd0c74559",
"translation_date": "2025-08-29T16:12:26+00:00",
"source_file": "6-space-game/3-moving-elements-around/README.md",
"language_code": "pt"
}
-->
# Construir um Jogo Espacial Parte 3: Adicionando Movimento
## Questionário Pré-Aula
[Questionário pré-aula](https://ff-quizzes.netlify.app/web/quiz/33)
Os jogos não são muito divertidos até que você tenha alienígenas se movendo na tela! Neste jogo, vamos usar dois tipos de movimentos:
- **Movimento por teclado/rato**: quando o utilizador interage com o teclado ou rato para mover um objeto na tela.
- **Movimento induzido pelo jogo**: quando o jogo move um objeto em intervalos de tempo definidos.
Então, como movemos coisas na tela? Tudo se resume a coordenadas cartesianas: alteramos a localização (x, y) do objeto e depois redesenhamos a tela.
Normalmente, são necessários os 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. Ao fazer isso, finalmente conseguimos mover o objeto de um local para outro.
Aqui está como isso pode parecer em 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);
```
✅ Consegue pensar em uma razão pela qual redesenhar o 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).
## Lidar com eventos de teclado
Lidamos com eventos ao associar eventos específicos ao código. Eventos de teclado são acionados em toda a janela, enquanto eventos de rato, como um `click`, podem ser conectados ao clique em um elemento específico. Vamos usar eventos de teclado ao longo deste projeto.
Para lidar com um evento, é necessário 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 podem ser usadas para identificar qual tecla foi pressionada:
- `key`, esta é uma representação em string da tecla pressionada, por exemplo, `ArrowUp`.
- `keyCode`, esta é uma representação numérica, por exemplo, `37`, que corresponde a `ArrowLeft`.
✅ Manipulação de eventos de teclado é útil fora do desenvolvimento de jogos. Que outros usos consegue imaginar para esta técnica?
### Teclas especiais: um alerta
Existem algumas teclas *especiais* que afetam a janela. Isso significa que, se estiver a ouvir um evento `keyup` e usar essas teclas especiais para mover o herói, também ocorrerá rolagem horizontal. Por essa razão, pode ser necessário *desativar* este comportamento padrão do navegador ao construir o seu jogo. É necessário 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 tecla de espaço tenham o seu comportamento *padrão* desativado. O mecanismo de *desativação* ocorre quando chamamos `e.preventDefault()`.
## Movimento induzido pelo jogo
Podemos fazer com que os objetos se movam sozinhos usando temporizadores, como as funções `setTimeout()` ou `setInterval()`, que atualizam a localização do objeto a cada intervalo de tempo. Aqui está 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 utilizador é 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 que foi atingido por um laser e explodiu, ele não faz mais parte do loop atual do jogo (vai aprender mais sobre isso em lições subsequentes).
Aqui está 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. Pode escolher o intervalo que faz mais sentido para o seu jogo.
## Continuando o Jogo Espacial
Vai pegar o código existente e expandi-lo. Pode começar com o código que completou na Parte I ou usar o código em [Parte II - inicial](../../../../6-space-game/3-moving-elements-around/your-work).
- **Mover o herói**: vai adicionar código para garantir que pode mover o herói usando as teclas de seta.
- **Mover inimigos**: também precisará adicionar código para garantir que os inimigos se movam de cima para baixo a uma determinada velocidade.
## Passos recomendados
Localize os ficheiros que foram criados para si na subpasta `your-work`. Deve conter o seguinte:
```bash
-| assets
-| enemyShip.png
-| player.png
-| index.html
-| app.js
-| package.json
```
Inicie o 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; neste momento, deve renderizar o herói e todos os inimigos; nada está a mover-se - ainda!
### Adicionar código
1. **Adicionar objetos dedicados** para `hero`, `enemy` e `game object`, que devem ter propriedades `x` e `y`. (Lembre-se da parte sobre [Herança ou composição](../README.md)).
*DICA*: `game object` deve ser o objeto com `x` e `y` e a capacidade de se desenhar no canvas.
> dica: comece por adicionar uma nova classe GameObject com o seu construtor delineado como abaixo e, em seguida, 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. **Adicionar 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 parar o *comportamento padrão*.
> dica: crie a sua função onKeyDown e associe-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 veja as teclas sendo registadas.
3. **Implementar** o [Padrão Pub/Sub](../README.md), isso manterá o seu código limpo enquanto segue as partes restantes.
Para fazer esta última parte, 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 subscrever 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 com um bom intervalo. 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. **Adicionar código** para mover inimigos em um determinado intervalo
Refatore a função `createEnemies()` para criar os inimigos e adicioná-los à 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 realizar 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 iniciar o desenho:
```javascript
function drawGameObjects(ctx) {
gameObjects.forEach(go => go.draw(ctx));
}
```
Os seus inimigos devem começar a avançar na direção da sua nave espacial!
---
## 🚀 Desafio
Como pode ver, o seu código pode transformar-se em 'código espaguete' quando começa a adicionar funções, variáveis e classes. Como pode organizar melhor o seu código para que seja mais legível? Esboce um sistema para organizar o seu código, mesmo que ainda esteja num único ficheiro.
## Questionário Pós-Aula
[Questionário pós-aula](https://ff-quizzes.netlify.app/web/quiz/34)
## Revisão & Autoestudo
Embora estejamos a escrever o nosso jogo sem usar frameworks, existem muitos frameworks baseados em JavaScript para desenvolvimento de jogos com canvas. Dedique algum tempo para [ler sobre eles](https://github.com/collections/javascript-game-engines).
## Tarefa
[Comente o 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, é 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.