Criando um Joguinho Runner em JavaScript no navegador [estilo dinossauro do Chrome (sem o dinossauro por direitos autorais haha)]

Se você está procurando aprender como criar um jogo básico de plataforma com HTML5 e JavaScript, este post é para você! Vamos explorar um jogo estilo runner, onde o objetivo é controlar um bloco que precisa pular para evitar obstáculos.

O que vamos construir:

  • Um bloco (representado por um quadrado) que pode pular (pode ser um dinossauro aqui).
  • Obstáculos que aparecem do lado direito e vão em direção ao bloco (que podem ser as árvores).
  • Detecção de colisões.
  • Contagem de pontos.

Estrutura do código

Vamos dividir o código em duas partes principais:

  1. HTML: Para estruturar a tela e criar a área de jogo.
  2. JavaScript: Para adicionar a lógica do jogo, movimento do bloco, obstáculos, colisões e o loop de jogo.

Passo 1: Estrutura HTML

Primeiro, criamos um arquivo simples de HTML com uma tag <canvas>, onde o jogo será renderizado. O <canvas> é a área onde desenharemos o bloco e os obstáculos. O arquivo HTML ficará assim:

<!DOCTYPE html>
<html lang="pt-br">
<head>
  <meta charset="UTF-8">
  <meta name="viewport" content="width=device-width, initial-scale=1.0">
  <title>Bloco Runner DEVDATA</title>
</head>
<body>
  <canvas id="game" width="800" height="400"></canvas>
  <script>
    // Aqui vai o código JavaScript
  </script>
</body>
</html>

Passo 2: Configuração do Canvas e Contexto

Agora, no script JavaScript, começamos pegando o elemento <canvas> e criando o contexto de desenho (ctx). Este contexto nos permite desenhar o bloco e os obstáculos na tela.

const canvas = document.getElementById("game");
const ctx = canvas.getContext("2d");

Passo 3: Definição do Bloco (o carinha que pula)

Vamos criar um objeto dino (representando nosso bloco) que pode pular e é controlado pela gravidade. O bloco começará em uma posição fixa no eixo Y, mas vai cair de volta quando não estiver pulando.

const dino = {
  x: 50,
  y: 150,
  width: 40,
  height: 40,
  vy: 0,
  jumping: false,
  draw() {
    ctx.fillStyle = "#000";
    ctx.fillRect(this.x, this.y, this.width, this.height);
  },
  update() {
    if (this.jumping) {
      this.vy += gravity;
      this.y += this.vy;

      if (this.y >= 150) {
        this.y = 150;
        this.vy = 0;
        this.jumping = false;
      }
    }
  },
  jump() {
    if (!this.jumping) {
      this.jumping = true;
      this.vy = -12;
    }
  }
};

Passo 4: Criando os Obstáculos

Agora, precisamos criar obstáculos que aparecem aleatoriamente na tela e vão da direita para a esquerda. Esses obstáculos têm uma largura e altura aleatória e, quando saem da tela, são removidos.

class Obstacle {
  constructor() {
    this.width = 20 + Math.random() * 20;
    this.height = 30 + Math.random() * 40;
    this.x = canvas.width;
    this.y = 200 - this.height;
  }

  draw() {
    ctx.fillStyle = "#666";
    ctx.fillRect(this.x, this.y, this.width, this.height);
  }

  update() {
    this.x -= gameSpeed;
  }
}

Passo 5: Lógica do Jogo

O loop do jogo é o coração do processo. Aqui, o bloco será desenhado e atualizado a cada frame, assim como os obstáculos. Além disso, a pontuação será atualizada à medida que o jogo avança.

let obstacles = [];
let spawnTimer = 0;
let score = 0;
let gameOver = false;

function resetGame() {
  obstacles = [];
  spawnTimer = 0;
  score = 0;
  gameSpeed = 6;
  dino.y = 150;
  dino.vy = 0;
  dino.jumping = false;
  gameOver = false;
  loop();
}

function loop() {
  if (gameOver) return;

  ctx.clearRect(0, 0, canvas.width, canvas.height);

  dino.update();
  dino.draw();

  if (spawnTimer <= 0) {
    obstacles.push(new Obstacle());
    spawnTimer = 60 + Math.random() * 40;
  } else {
    spawnTimer--;
  }

  for (let i = 0; i < obstacles.length; i++) {
    const o = obstacles[i];
    o.update();
    o.draw();

    // Colisão
    if (
      dino.x < o.x + o.width &&
      dino.x + dino.width > o.x &&
      dino.y < o.y + o.height &&
      dino.y + dino.height > o.y
    ) {
      gameOver = true;
      alert("Game Over! Score: " + Math.floor(score));
      resetGame();
      return;
    }

    // Remove obstáculos fora da tela
    if (o.x + o.width < 0) {
      obstacles.splice(i, 1);
      i--;
    }
  }

  score += 0.1;
  gameSpeed += 0.001;

  // Desenha o chão
  ctx.fillStyle = "#888";
  ctx.fillRect(0, 190, canvas.width, 2);

  // Exibe a pontuação
  ctx.fillStyle = "#000";
  ctx.font = "16px monospace";
  ctx.fillText("Score: " + Math.floor(score), 10, 20);

  requestAnimationFrame(loop);
}

document.addEventListener("keydown", (e) => {
  if (e.code === "Space" || e.code === "ArrowUp") dino.jump();
});

loop();

Passo 6: Detecção de Teclas

Adicionamos um evento keydown para que, quando o usuário pressionar a tecla de espaço ou a seta para cima, o bloco execute o pulo.

Passo 7: Testando o Jogo

Agora, você pode testar o jogo localmente, abrindo o arquivo HTML em seu navegador. O bloco começará correndo e você precisará pular para evitar os obstáculos. A pontuação será mostrada no topo da tela e aumentará à medida que o jogo avança.

O que aprendemos aqui?

  1. Como usar o <canvas> para desenhar objetos dinâmicos.
  2. Como controlar o movimento de um personagem (neste caso, o bloco) com a física básica de gravidade.
  3. Como criar obstáculos que surgem de forma aleatória e interagem com o personagem.
  4. Como configurar o loop do jogo para manter tudo em movimento e atualizando.

Conclusão

Este jogo simples foi construído apenas com HTML5 e JavaScript, e você pode personalizá-lo conforme desejar. Desde modificar o estilo do bloco até adicionar novos elementos no jogo, as possibilidades são infinitas!


Agora é só fazer o download do código HTML e jogar! Se você tiver alguma dúvida ou quiser melhorar o jogo, fique à vontade para compartilhar suas ideias!

EXEMPLO FUNCIONAL COMPLETO:

<!DOCTYPE html>
<html lang="pt-br">
<head>
  <meta charset="UTF-8">
  <meta name="viewport" content="width=device-width, initial-scale=1.0">
  <title>BLock Runner Devdata</title>
</head>
<body>
  <canvas id="game" width="800" height="400"></canvas>
  <script>
    const canvas = document.getElementById("game");
    const ctx = canvas.getContext("2d");

    const gravity = 0.6;
    let gameSpeed = 6;

    const dino = {
      x: 50,
      y: 150,
      width: 40,
      height: 40,
      vy: 0,
      jumping: false,
      draw() {
        ctx.fillStyle = "#000";
        ctx.fillRect(this.x, this.y, this.width, this.height);
      },
      update() {
        if (this.jumping) {
          this.vy += gravity;
          this.y += this.vy;

          if (this.y >= 150) {
            this.y = 150;
            this.vy = 0;
            this.jumping = false;
          }
        }
      },
      jump() {
        if (!this.jumping) {
          this.jumping = true;
          this.vy = -12;
        }
      }
    };

    class Obstacle {
      constructor() {
        this.width = 20 + Math.random() * 20;
        this.height = 30 + Math.random() * 40;
        this.x = canvas.width;
        this.y = 200 - this.height;
      }

      draw() {
        ctx.fillStyle = "#666";
        ctx.fillRect(this.x, this.y, this.width, this.height);
      }

      update() {
        this.x -= gameSpeed;
      }
    }

    let obstacles = [];
    let spawnTimer = 0;
    let score = 0;
    let gameOver = false;

    function resetGame() {
      obstacles = [];
      spawnTimer = 0;
      score = 0;
      gameSpeed = 6;
      dino.y = 150;
      dino.vy = 0;
      dino.jumping = false;
      gameOver = false;
      loop();
    }

    function loop() {
      if (gameOver) return;

      ctx.clearRect(0, 0, canvas.width, canvas.height);

      dino.update();
      dino.draw();

      if (spawnTimer <= 0) {
        obstacles.push(new Obstacle());
        spawnTimer = 60 + Math.random() * 40;
      } else {
        spawnTimer--;
      }

      for (let i = 0; i < obstacles.length; i++) {
        const o = obstacles[i];
        o.update();
        o.draw();

        // colisão
        if (
          dino.x < o.x + o.width &&
          dino.x + dino.width > o.x &&
          dino.y < o.y + o.height &&
          dino.y + dino.height > o.y
        ) {
          gameOver = true;
          alert("Game Over! Score: " + Math.floor(score));
          resetGame();
          return;
        }

        // remove obstáculos fora da tela
        if (o.x + o.width < 0) {
          obstacles.splice(i, 1);
          i--;
        }
      }

      score += 0.1;
      gameSpeed += 0.001;

      // desenha o chão
      ctx.fillStyle = "#888";
      ctx.fillRect(0, 190, canvas.width, 2);

      // score
      ctx.fillStyle = "#000";
      ctx.font = "16px monospace";
      ctx.fillText("Score: " + Math.floor(score), 10, 20);

      requestAnimationFrame(loop);
    }

    document.addEventListener("keydown", (e) => {
      if (e.code === "Space" || e.code === "ArrowUp") dino.jump();
    });

    loop();
  </script>
</body>
</html>
Rolar para cima