Construa um Jogo de Quebra-Cabeça Deslizante com JavaScript

JavaScriptBeginner
Pratique Agora

Introdução

Bem-vindo a este projeto sobre a construção de um simples jogo de Quebra-Cabeça Deslizante usando JavaScript. Ao final deste guia, você terá um jogo de quebra-cabeça deslizante 3x3 funcional que poderá jogar em seu navegador. O jogo apresentará peças numeradas, um cronômetro e controles para iniciar, pausar e reiniciar o jogo.

Nenhuma experiência prévia é necessária, mas uma compreensão básica de JavaScript e HTML seria útil. Vamos mergulhar!

👀 Pré-visualização

Sliding Puzzle Game Preview

🎯 Tarefas

Neste projeto, você aprenderá:

  • Como projetar o layout do jogo em HTML
  • Como escrever os estilos CSS para o jogo
  • Como inicializar as variáveis do jogo em JavaScript
  • Como implementar a função de movimento para lidar com os movimentos das peças
  • Como determinar os movimentos possíveis das peças
  • Como implementar o cronômetro do jogo
  • Como controlar o fluxo do jogo com as funções de iniciar, pausar e reiniciar
  • Como embaralhar as peças aleatoriamente no início ou ao reiniciar
  • Como inicializar o jogo no carregamento da página

🏆 Conquistas

Após concluir este projeto, você será capaz de:

  • Projetar o layout de um jogo usando HTML
  • Estilizar elementos usando CSS
  • Implementar a lógica do jogo usando JavaScript
  • Lidar com a entrada do usuário e realizar ações com base nela
  • Manipular o DOM para atualizar o estado do jogo e exibir informações

Design do Layout do Jogo em HTML

Projete o layout do jogo em index.html, conforme mencionado na análise anterior.

<!doctype html>
<html lang="en">
  <head>
    <meta charset="UTF-8" />
    <title>Puzzle</title>
    <link rel="stylesheet" type="text/css" href="puzzle.css" />
    <script type="text/javascript" src="puzzle.js"></script>
  </head>
  <body>
    <div id="container">
      <!-- DIV mais externa, usada para conter a estrutura interna -->
      <div id="game">
        <!-- Área do jogo, que é o grande bloco DIV -->
        <div id="d1" onclick="move(1)">1</div>
        <!-- DIVs pequenas, que são os 8 blocos pequenos. Quando clicados, a função move() é executada. O parâmetro é o número exibido, para que saibamos qual bloco foi clicado -->
        <div id="d2" onclick="move(2)">2</div>
        <div id="d3" onclick="move(3)">3</div>
        <div id="d4" onclick="move(4)">4</div>
        <div id="d5" onclick="move(5)">5</div>
        <div id="d6" onclick="move(6)">6</div>
        <div id="d7" onclick="move(7)">7</div>
        <div id="d8" onclick="move(8)">8</div>
      </div>
      <div id="control">
        <!-- Área de controle do jogo -->
        <p>
          <rowspan id="timeText">Tempo Total</rowspan>
          <!--  "Total Time" -->
          <rowspan id="timer"></rowspan>
        </p>
        <!-- Área de exibição do tempo do jogo -->
        <p>
          <rowspan id="start" onclick="start()">Iniciar</rowspan>
          <!--  "Start" -->
          <rowspan id="reset" onclick="reset()">Reiniciar</rowspan>
          <!--  "Retry" -->
        </p>
        <!-- Área de exibição dos botões de controle -->
      </div>
    </div>
  </body>
</html>
✨ Verificar Solução e Praticar

Escrevendo os Estilos CSS

Após concluir a estrutura do layout, é hora de embelezar nosso jogo com estilos CSS. Durante esta fase, você tem a liberdade de personalizar e projetar o jogo de acordo com sua preferência, adicionando toques e detalhes exclusivos. Você também pode introduzir mais elementos decorativos para aprimorar a estética do jogo. No entanto, uma nota importante: se você alterar o tamanho do DIV principal do jogo, certifique-se de ajustar o código JavaScript correspondente. Discutiremos isso com mais profundidade em breve.

Em puzzle.css, adicione:

* {
  padding: 0;
  margin: 0;
  border: 0;
}
/* O '*' é um curinga, removendo os estilos padrão para todos os elementos porque alguns navegadores podem adicionar estilos padrão, o que pode criar problemas de layout. */

body {
  width: 100%;
  height: 100%;
}
/* Definindo a altura e largura do corpo para 100%, para que ele se adapte automaticamente de acordo com o tamanho da tela do navegador. */

#container {
  position: relative;
  width: 620px;
  height: 450px;
  margin: 0 auto;
  margin-top: 100px;
  border-radius: 1px;
}
/* Este é o DIV que envolve todos os elementos. Definido para 620px de largura e 450px de altura. Este tamanho pode ser aumentado, mas não diminuído. Deve ser grande o suficiente para conter todos os elementos internos. */

#game {
  position: absolute;
  width: 450px;
  height: 450px;
  border-radius: 5px;
  display: inline-block;
  background-color: #ffe171;
  box-shadow: 0 0 10px #ffe171;
}
/* Esta é a área do jogo DIV. O tamanho é calculado com base no tamanho dos blocos menores. Aqui, os blocos são definidos para 150px por 150px, então a área do jogo é 150px*3, totalizando 450px. */

#game div {
  position: absolute;
  width: 149px;
  height: 149px;
  box-shadow: 1px 1px 2px #777;
  background-color: #20a6fa;
  color: white;
  text-align: center;
  font-size: 150px;
  line-height: 150px;
  cursor: pointer;
  -webkit-transition: 0.3s; /* Prefixo do navegador para compatibilidade com Chrome */
  -moz-transition: 0.3s; /* Para Firefox */
  -ms-transition: 0.3s; /* Para IE */
  -o-transition: 0.3s; /* Para Opera */
  transition: 0.3s;
}
/* Isso define o tamanho dos blocos pequenos. Posicionados como absolute, as mudanças na posição não afetarão as posições de outros elementos. A largura e a altura são ambas 149px. Notavelmente, há uma box-shadow, adicionando à sua dimensão geral. A propriedade 'transition:0.3s' faz com que as mudanças apareçam como animações, então, quando a posição do bloco muda, uma animação é reproduzida automaticamente. */

#game div:hover {
  color: #ffe171;
}
/* Definindo a animação de mouse hover para os blocos. Quando o mouse passa sobre um elemento, essas propriedades substituem as anteriores, e ele retorna ao original quando o mouse se afasta. Aqui, estamos mudando a cor da fonte. */

#control {
  width: 150px;
  height: 450px;
  display: inline-block;
  float: right;
}
/* Seção de controle. 'display:inline-block' permite que o elemento mantenha as propriedades de bloco sem ocupar uma linha inteira, e 'float:right' o posiciona no lado direito. */

#control rowspan {
  height: 25px;
  font-size: 20px;
  color: #222;
  margin-top: 10px;
}
/* Estilos comuns para botões na área de controle. */

#start {
  display: inline-block;
  font-size: 28px;
  width: 100px;
  height: 28px;
  background-color: #20a6fa;
  color: #ffe171;
  text-shadow: 1px 1px 2px #ffe171;
  border-radius: 5px;
  box-shadow: 2px 2px 5px #4c98f5;
  text-align: center;
  cursor: pointer;
}
/* Estilos para o botão 'Iniciar'. 'cursor:pointer' faz com que o cursor mude para um símbolo de mão ao passar o mouse sobre ele. */

#reset {
  display: inline-block;
  font-size: 28px;
  width: 100px;
  height: 28px;
  background-color: #20a6fa;
  color: #ffe171;
  text-shadow: 1px 1px 2px #ffe171; /* Sombra da fonte */
  border-radius: 5px; /* Cantos arredondados */
  box-shadow: 2px 2px 5px #4c98f5; /* Sombra da caixa */
  text-align: center; /* Centralizar texto */
  cursor: pointer;
}
/* Estilos para o botão 'Reiniciar'. */

#d1 {
  left: 0px;
}
#d2 {
  left: 150px;
}
#d3 {
  left: 300px;
}
#d4 {
  top: 150px;
}
#d5 {
  top: 150px;
  left: 150px;
}
#d6 {
  top: 150px;
  left: 300px;
}
#d7 {
  top: 300px;
}
#d8 {
  left: 150px;
  top: 300px;
}
/* Posições predefinidas para cada um dos blocos em ordem. */

Ter os estilos definidos é um passo significativo. Em seguida, passaremos para a lógica JavaScript para dar vida ao nosso jogo de quebra-cabeça. É aconselhável que o estilo inicial seja feito de acordo com as instruções acima. Depois que a funcionalidade do jogo for bem compreendida, você estará livre para exibir sua criatividade e personalizar ainda mais os estilos. No entanto, fazer alterações precoces e desinformadas pode levar a falhas imprevistas.

✨ Verificar Solução e Praticar

Inicializar Variáveis do Jogo em JavaScript

Antes de adicionar a funcionalidade do jogo, inicialize as variáveis essenciais do estado do jogo.

Em puzzle.js, adicione:

// puzzle.js

var time = 0;
// Salva o tempo decorrido
var pause = true;
// Flag para indicar se o jogo está pausado, true significa que está pausado
var set_timer;
// Função do temporizador
var d = new Array(10);
// Armazena o número do pequeno DIV atualmente no grande DIV
var d_direct = new Array(
  [0],
  [2, 4], // O grande DIV com o número 1 pode ir para as posições 2 e 4
  [1, 3, 5],
  [2, 6],
  [1, 5, 7],
  [2, 4, 6, 8],
  [3, 5, 9],
  [4, 8],
  [5, 7, 9],
  [6, 8]
);
// Salva as possíveis posições de movimento para os grandes DIVs
var d_posXY = new Array(
  [0],
  [0, 0], // O primeiro número representa left, o segundo representa top. Ex: a posição do primeiro bloco é left:0px, top:0px
  [150, 0],
  [300, 0],
  [0, 150],
  [150, 150],
  [300, 150],
  [0, 300],
  [150, 300],
  [300, 300]
);
// Posição dos grandes DIVs
d[1] = 1;
d[2] = 2;
d[3] = 3;
d[4] = 4;
d[5] = 5;
d[6] = 6;
d[7] = 7;
d[8] = 8;
d[9] = 0;
// Arranjo padrão, o nono grande DIV está vazio, então é 0. Usamos 0 para representar a peça em branco.
✨ Verificar Solução e Praticar

Implementar a Função de Movimento

Crie uma função que permita que as peças se movam com base na interação do usuário.

Adicione a puzzle.js:

// puzzle.js

function move(id) {
  // Função de movimento
  var i = 1;
  for (i = 1; i < 10; ++i) {
    if (d[i] == id) break;
  }
  // Este loop encontra a posição do pequeno DIV dentro do grande DIV
  var target_d = 0;
  // Salva as possíveis posições para o pequeno DIV, 0 significa que ele não pode se mover
  target_d = whereCanTo(i);
  // Descobre para onde o pequeno DIV pode se mover. Se retornar 0, significa que não pode se mover, caso contrário, retorna o número da posição de destino
  if (target_d != 0) {
    d[i] = 0;
    // Define o número do grande DIV atual como 0 porque o pequeno DIV se moveu, então o grande DIV atual está agora vazio
    d[target_d] = id;
    // Define o grande DIV de destino para o número do pequeno DIV clicado
    document.getElementById("d" + id).style.left = d_posXY[target_d][0] + "px";
    document.getElementById("d" + id).style.top = d_posXY[target_d][1] + "px";
    // Finalmente, define a posição do pequeno DIV clicado para a posição do grande DIV de destino
  }

  var finish_flag = true;
  // Flag para indicar se o jogo foi concluído, true significa que foi concluído
  for (var k = 1; k < 9; ++k) {
    if (d[k] != k) {
      finish_flag = false;
      break;
    }
  }
  // Itera de 1, verifica cada número do grande DIV. Se eles não estiverem em ordem, o jogo não foi concluído.
  if (finish_flag == true) {
    if (!pause) start();
    alert("Congratulations!");
  }
  // Se true, o jogo foi concluído. Se não estiver pausado, chama a função de pausa e exibe uma mensagem de sucesso.
}
✨ Verificar Solução e Praticar

Determinar os Movimentos Possíveis das Peças

Crie uma função para determinar para onde uma peça pode se mover com base em sua posição atual.

Adicione a puzzle.js:

// puzzle.js

function whereCanTo(cur_div) {
  // Função para determinar se um determinado DIV pode se mover e para qual posição
  var j = 0;
  var move_flag = false;
  for (j = 0; j < d_direct[cur_div].length; ++j) {
    if (d[d_direct[cur_div][j]] == 0) {
      move_flag = true;
      break;
    }
  }
  if (move_flag == true) {
    return d_direct[cur_div][j];
  } else {
    return 0;
  }
  // Se puder se mover, retorna o número da posição de destino, caso contrário, retorna 0
}
✨ Verificar Solução e Praticar

Implementar o Cronômetro do Jogo

Adicione uma função de cronômetro para acompanhar o tempo decorrido durante o jogo.

Adicione a puzzle.js:

// puzzle.js

function timer() {
  // Função do cronômetro, executa a cada segundo
  time += 1;
  var min = parseInt(time / 60); // Converte segundos em minutos
  var sec = time % 60; // Obtém os segundos restantes
  document.getElementById("timer").innerHTML =
    min + " minutos " + sec + " segundos";
}
✨ Verificar Solução e Praticar

Controlar o Fluxo do Jogo

Implemente funções para iniciar, pausar ou reiniciar o jogo com base na entrada do usuário.

Adicione a puzzle.js:

// puzzle.js

function start() {
  // Inicia ou pausa o jogo
  if (pause) {
    document.getElementById("start").innerHTML = "Pause";
    pause = false;
    set_timer = setInterval(timer, 1000);
  } else {
    document.getElementById("start").innerHTML = "Start";
    pause = true;
    clearInterval(set_timer);
  }
}
function reset() {
  // Reinicia o jogo
  time = 0;
  random_d();
  if (pause) start();
}
✨ Verificar Solução e Praticar

Embaralhar as Peças Aleatoriamente

Crie uma função para embaralhar aleatoriamente as peças no início ou ao reiniciar.

Adicione a puzzle.js:

// puzzle.js

function random_d() {
  // Embaralha aleatoriamente as peças
  for (var i = 9; i > 1; --i) {
    var to = parseInt(Math.random() * (i - 1) + 1);
    if (d[i] != 0) {
      document.getElementById("d" + d[i]).style.left = d_posXY[to][0] + "px";
      document.getElementById("d" + d[i]).style.top = d_posXY[to][1] + "px";
    }
    if (d[to] != 0) {
      document.getElementById("d" + d[to]).style.left = d_posXY[i][0] + "px";
      document.getElementById("d" + d[to]).style.top = d_posXY[i][1] + "px";
    }
    var tem = d[to];
    d[to] = d[i];
    d[i] = tem;
  }
}
✨ Verificar Solução e Praticar

Inicializar o Jogo ao Carregar a Página

Finalmente, certifique-se de que, quando a página da web for carregada, as peças do jogo sejam exibidas e estejam prontas para jogar.

Adicione a puzzle.js:

// puzzle.js

// Inicializa o jogo no carregamento da página
window.onload = function () {
  reset();
};
✨ Verificar Solução e Praticar

Executando o Projeto

  • Abra index.html em um navegador web.
    open web

  • O efeito da página é o seguinte:
    puzzle game page preview

✨ Verificar Solução e Praticar

Resumo

Você construiu com sucesso um jogo de Quebra-Cabeça Deslizante usando JavaScript de maneira estruturada e organizada! A divisão passo a passo deve facilitar para os aprendizes a compreensão de cada componente da lógica do jogo. Lembre-se, esta é apenas uma versão básica, e há muito espaço para aprimoramentos e recursos adicionais. Mergulhe e faça com que seja seu. Feliz codificação!