Clique no retângulo azul para soltar a caixa superior sobre a caixa inferior!

Jogo de torre


Modo tela cheia

O objetivo deste jogo é empilhar o maior número possível de caixas umas sobre as outras.
Quando você clica na tela, a caixa saltitante começa a cair. A menos que você clique no momento exato em que as duas caixas estão idealmente alinhadas, a parte da caixa que estiver para fora cai da tela ("restos") e a próxima caixa tem menos espaço para pousar.
Para tornar as coisas ainda mais difíceis, cada caixa rebate mais rápido que a anterior.

O código fonte completo está abaixo e aqui está uma breve explicação:

[Linhas 1-7] Inicia o Canvas HTML5.
[8-20] Declara variáveis e objetos.
[22-28] A função que cria uma nova caixa após a queda da anterior.
[30-33] A função 'Fim de jogo' (Game over).
[35-89] O loop principal do jogo. Vamos ver os detalhes, pois é aqui que toda a ação acontece.
[36] Se o jogo estiver ativo:
[37-38] Limpa o quadro anterior e exibe a pontuação.
[39-43] Desenha todas as caixas.
[41] Seleciona a cor da caixa.
[44-45] Desenha os restos (entulho).
[46] Se o jogo estiver no modo 'bounce' (a caixa atual está rebatendo horizontalmente nas paredes):
[47-52] Move a caixa atual no eixo x. Inverte a direção se ela bater em uma parede.
[53] Se o jogo estiver no modo 'fall' (a caixa atual foi solta):
[54] Move a caixa atual no eixo y.
[55] Se ela pousar em cima da caixa anterior:
[56] Muda o modo de volta para 'bounce'.
[57] Calcula o comprimento da parte não sobreposta.
[58-60] Se a diferença for maior que a caixa, significa que o jogador errou completamente: fim de jogo!
[61-64] Atualiza o objeto de restos.
[65-72] Determina se os restos devem ficar do lado esquerdo ou direito da caixa.
[73-76] Aumenta a velocidade de rebote.
[77-79] Cria a nova caixa.
[82] Anima os restos - eles continuam caindo até serem atualizados novamente em [61-64].
[83-85] O scrollCounter determina por quantos quadros a câmera deve subir. Se for positivo, a posição da câmera é atualizada.
[88] Aguarda o próximo quadro de animação
[91] Função que reatribui valores iniciais às variáveis no início de um novo jogo.
[102-109] O manipulador de clique (mouse ou toque):
[103-4] Se estivermos no modo 'gameOver', apenas reinicie o jogo.
[106-7] Se estivermos no modo 'bounce', mude para 'fall' (solte a caixa).
[111] Inicializa o primeiro jogo.
[112] Inicia a animação.

<html>
<body>
<canvas id="myCanvaswidth="800height="600"></canvas>
<script>
let canvas = document.getElementById("myCanvas");
let context = canvas.getContext("2d");
context.font = 'bold 30px sans-serif';
let scrollCountercameraYcurrentmodexSpeed;
let ySpeed = 5;
let height = 50;
let boxes = [];
boxes[0] = {
  x300,
  y300,
  width200
};
let debris = {
  x0,
  width0
};
 
function newBox() {
  boxes[current] = {
    x0,
    y: (current + 10) * height,
    widthboxes[current - 1].width
  };
}
 
function gameOver() {
  mode = 'gameOver';
  context.fillText('Fim de jogoClique para jogar de novo!', 5050);
}
 
function animate() {
  if (mode != 'gameOver') {
    context.clearRect(00canvas.widthcanvas.height);
    context.fillText('Pontuação: ' + (current - 1).toString(), 100200);
    for (let n = 0n < boxes.lengthn++) {
      let box = boxes[n];
      context.fillStyle = 'rgb(' + n * 16 + ',' + n * 16 + ',' + n * 16 + ')';
      context.fillRect(box.x600 - box.y + cameraYbox.widthheight);
    }
    context.fillStyle = 'red';
    context.fillRect(debris.x600 - debris.y + cameraYdebris.widthheight);
    if (mode == 'bounce') {
      boxes[current].x = boxes[current].x + xSpeed;
      if (xSpeed > 0 && boxes[current].x + boxes[current].width > canvas.width)
        xSpeed = -xSpeed;
      if (xSpeed < 0 && boxes[current].x < 0)
        xSpeed = -xSpeed;
    }
    if (mode == 'fall') {
      boxes[current].y = boxes[current].y - ySpeed;
      if (boxes[current].y == boxes[current - 1].y + height) {
        mode = 'bounce';
        let difference = boxes[current].x - boxes[current - 1].x;
        if (Math.abs(difference) >= boxes[current].width) {
          gameOver();
        }
        debris = {
          yboxes[current].y,
          widthdifference
        };
        if (boxes[current].x > boxes[current - 1].x) {
          boxes[current].width = boxes[current].width - difference;
          debris.x = boxes[current].x + boxes[current].width;
        } else {
          debris.x = boxes[current].x - difference;
          boxes[current].width = boxes[current].width + difference;
          boxes[current].x = boxes[current - 1].x;
        }
        if (xSpeed > 0)
          xSpeed++;
        else
          xSpeed--;
        current++;
        scrollCounter = height;
        newBox();
      }
    }
    debris.y = debris.y - ySpeed;
    if (scrollCounter) {
      cameraY++;
      scrollCounter--;
    }
  }
  window.requestAnimationFrame(animate);
}
 
function restart() {
  boxes.splice(1boxes.length - 1);
  mode = 'bounce';
  cameraY = 0;
  scrollCounter = 0;
  xSpeed = 2;
  current = 1;
  newBox();
  debris.y = 0;
}
 
canvas.onpointerdown = function() {
  if (mode == 'gameOver')
    restart();
  else {
    if (mode == 'bounce')
      mode = 'fall';
  }
};
 
restart();
animate();
</script>
</body>
</html>