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="myCanvas" width="800" height="600"></canvas> |
| <script> |
| let canvas = document.getElementById("myCanvas"); |
| let context = canvas.getContext("2d"); |
| context.font = 'bold 30px sans-serif'; |
| let scrollCounter, cameraY, current, mode, xSpeed; |
| let ySpeed = 5; |
| let height = 50; |
| let boxes = []; |
| boxes[0] = { |
| x: 300, |
| y: 300, |
| width: 200 |
| }; |
| let debris = { |
| x: 0, |
| width: 0 |
| }; |
| |
| function newBox() { |
| boxes[current] = { |
| x: 0, |
| y: (current + 10) * height, |
| width: boxes[current - 1].width |
| }; |
| } |
| |
| function gameOver() { |
| mode = 'gameOver'; |
| context.fillText('Fim de jogo. Clique para jogar de novo!', 50, 50); |
| } |
| |
| function animate() { |
| if (mode != 'gameOver') { |
| context.clearRect(0, 0, canvas.width, canvas.height); |
| context.fillText('Pontuação: ' + (current - 1).toString(), 100, 200); |
| for (let n = 0; n < boxes.length; n++) { |
| let box = boxes[n]; |
| context.fillStyle = 'rgb(' + n * 16 + ',' + n * 16 + ',' + n * 16 + ')'; |
| context.fillRect(box.x, 600 - box.y + cameraY, box.width, height); |
| } |
| context.fillStyle = 'red'; |
| context.fillRect(debris.x, 600 - debris.y + cameraY, debris.width, height); |
| 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 = { |
| y: boxes[current].y, |
| width: difference |
| }; |
| 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(1, boxes.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> |