Jogo de Quebra-Cabeça Flip com jQuery

JavaScriptBeginner
Pratique Agora

Introdução

Este projeto, o "jQuery Flip Puzzle Game", proporciona uma experiência prática em desenvolvimento web, com foco em JavaScript, jQuery e Bootstrap. Ele envolve programação orientada a objetos em JavaScript e aborda a questão comum da ligação de "this" no contexto do desenvolvimento web. O jogo é implementado usando jQuery e Bootstrap 3 para a interface do usuário. Embora a familiaridade com Bootstrap seja útil, a lógica principal do projeto é construída com jQuery e JavaScript.

No jogo, os jogadores começam com uma grade de blocos laranja. Cada bloco tem um lado laranja e um lado azul. Quando um jogador clica em um bloco, sua cor se inverte, e as cores dos blocos adjacentes também mudam. O objetivo é transformar todos os blocos em azul para completar o jogo.

👀 Pré-visualização

jQuery Flip Puzzle Game

🎯 Tarefas

Neste projeto, você aprenderá:

  • Como implementar programação orientada a objetos em JavaScript e abordar a questão da ligação de "this".
  • Como construir a lógica principal do jogo usando jQuery e JavaScript.
  • Como criar um jogo de quebra-cabeça interativo onde os jogadores invertem as cores dos blocos para vencer.

🏆 Conquistas

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

  • Aplicar princípios de programação orientada a objetos em JavaScript.
  • Lidar com a ligação de "this" em JavaScript para tratamento de eventos e métodos de objetos.
  • Desenvolver jogos web interativos usando jQuery.
  • Utilizar Bootstrap 3 para criar interfaces visualmente atraentes e fáceis de usar.

Estrutura HTML Básica

De acordo com a imagem de pré-visualização, é criado um layout preliminar da página web. No Bootstrap, existe um diálogo Modal semelhante à janela pop-up alert, e o estilo do Modal é mais estético em comparação.

Precisamos incluir os estilos CSS do Bootstrap, nossos estilos CSS personalizados, o arquivo jQuery, o arquivo JavaScript do Bootstrap e o arquivo JavaScript principal do jogo no projeto. Escreva o seguinte código na tag head da página HTML:

<head>
  <meta charset="utf-8" />
  <title>Blue Puzzle</title>
  <!-- Include Bootstrap CSS -->
  <link
    rel="stylesheet"
    href="https://cdn.jsdelivr.net/npm/bootstrap@3.3.7/dist/css/bootstrap.min.css"
  />
  <!-- Include custom CSS -->
  <link rel="stylesheet" href="style.css" />

  <!-- Include jQuery and Bootstrap JavaScript -->
  <script src="https://cdn.bootcss.com/jquery/3.3.1/jquery.min.js"></script>
  <script src="https://cdn.jsdelivr.net/npm/bootstrap@3.3.7/dist/js/bootstrap.min.js"></script>
  <!-- Include game main JavaScript -->
  <script src="game.js"></script>
</head>
✨ Verificar Solução e Praticar

Configuração da Área do Jogo

Para configurar a área do jogo, incluindo os botões para reiniciar o jogo e outras funções, insira o seguinte código na tag body:

<div class="container">
  <div class="heading">
    <h1 class="title">jQuery Flip Puzzle Game</h1>
    <div class="scoresContainer">
      <!-- Display current game level -->
      <div class="currLevel">Current level: <b>1</b></div>
    </div>
  </div>
  <div class="aboveGame">
    <!-- Game buttons -->
    <a
      class="instruct btn btn-primary"
      data-toggle="modal"
      data-target="#instructions"
      >Game Instructions</a
    >
    <a
      class="newgame btn btn-primary"
      data-toggle="modal"
      data-target="#newGame"
      >Restart</a
    >
    <a
      class="reset btn btn-primary"
      data-toggle="modal"
      data-target="#restartLevel"
      >Reset Level</a
    >
  </div>
  <div class="board">
    <!-- Game area -->
    <div class="gamerow">
      <div class="gamesquare coord0q0"></div>
    </div>
  </div>
</div>
✨ Verificar Solução e Praticar

Edite o conteúdo do popup correspondente ao botão "Edit Game Instructions". Escreva o seguinte código abaixo do código da área do jogo:

<!-- Gameplay Modal -->
<div
  class="modal fade"
  id="instructions"
  tabindex="-1"
  role="dialog"
  aria-hidden="true"
>
  <div class="modal-dialog">
    <div class="modal-content">
      <div class="modal-header">
        <h4 class="modal-title">Gameplay</h4>
      </div>
      <div class="modal-body">
        <p>How to win: Make all puzzle pieces turn blue.</p>
        <p>
          Gameplay: Each square has one orange side and one blue side. When you
          click on a square, its color will flip, and the colors of the squares
          adjacent to it will also flip.
        </p>
      </div>
      <div class="modal-footer">
        <button type="button" class="btn btn-primary" data-dismiss="modal">
          Start Game
        </button>
      </div>
    </div>
  </div>
</div>
✨ Verificar Solução e Praticar

Edite o conteúdo do modal correspondente ao botão "Restart". Escreva o seguinte código abaixo do código da lógica do jogo:

<!-- New Game modal -->
<div
  class="modal fade"
  id="newGame"
  tabindex="-1"
  role="dialog"
  aria-hidden="true"
>
  <div class="modal-dialog">
    <div class="modal-content">
      <div class="modal-header">
        <h4 class="modal-title">Restart</h4>
      </div>
      <div class="modal-body">
        <p>Are you sure you want to restart?</p>
      </div>
      <div class="modal-footer">
        <button
          type="button"
          class="btn btn-primary"
          id="newGameConfirm"
          data-dismiss="modal"
        >
          Start Game
        </button>
      </div>
    </div>
  </div>
</div>
✨ Verificar Solução e Praticar

Modifique o conteúdo do popup correspondente ao botão "Reset Level". Adicione o seguinte código abaixo do código "Restart Level":

<!-- Reset Level Confirmation Modal -->
<div
  class="modal fade"
  id="restartLevel"
  tabindex="-1"
  role="dialog"
  aria-hidden="true"
>
  <div class="modal-dialog">
    <div class="modal-content">
      <div class="modal-header">
        <h4 class="modal-title">Reset Level Confirmation</h4>
      </div>
      <div class="modal-body">
        <p>Are you sure you want to reset the level?</p>
      </div>
      <div class="modal-footer">
        <button
          type="button"
          class="btn btn-primary"
          id="resetLevelConfirm"
          data-dismiss="modal"
        >
          Reset
        </button>
      </div>
    </div>
  </div>
</div>
✨ Verificar Solução e Praticar

Estilos CSS

Os estilos CSS do jogo são relativamente simples. O código em style.css é mostrado abaixo:

.container {
  width: 600px;
  margin: 0 auto;
}

/* Game level */
.scoresContainer {
  float: right;
  text-align: right;
  font-size: 18px;
}

/* Game buttons */
.aboveGame:after {
  display: block;
  margin: 20px 0;
  content: "";
  clear: both;
}

/* Game area */
.board {
  position: absolute;
  background-color: #5f5f5f;
  border-radius: 4px;
}

.gamesquare {
  float: left;
  margin-right: 15px;
  border-radius: 3px;
}
✨ Verificar Solução e Praticar

Configurando a Cena do Jogo

As peças no painel do jogo são armazenadas como um array bidimensional, onde cada elemento tem um valor de 0 (definido para laranja) ou 1 (definido para azul). Inicialmente, todos os valores são definidos como 0. Quando uma peça é clicada, sua cor é invertida, e as coordenadas das peças adjacentes são determinadas para inverter suas cores também, enquanto também se alteram os valores no array bidimensional. O jogo é concluído quando todos os valores no array se tornam 1 (ou seja, todas as peças são azuis).

Lógica principal:

  1. A relação entre o tamanho da cena do jogo, o nível de dificuldade e o tamanho e número de peças.

  2. Clicar em uma peça e mudar o estado das peças adjacentes.

  3. Verificar se todos os estados das peças são 1.

function SetStyle() {}
SetStyle.prototype.setGridSize = function (level) {
  var margin = this.getMargin(level);
  var res = ($(".container").width() - margin * level) / level;

  // Set the size and spacing of the tiles
  $(".gamesquare").css("margin-right", margin);
  $(".gamesquare").css("width", res);
  $(".gamesquare").css("height", res);

  // Set the height, right margin, and bottom margin of each row
  $(".gamerow").css("height", res);
  $(".gamerow").css("margin-right", margin * -1);
  $(".gamerow").css("margin-bottom", margin);

  // Set the padding of the game area
  $(".board").css("padding", margin);
  $(".board").css("padding-bottom", 0);
};
SetStyle.prototype.getMargin = function (level) {
  if (level <= 6) return 15;
  if (level > 15) return 5;
  return 20 - level;
};
✨ Verificar Solução e Praticar

Criar Construtor do Jogo

// Game constructor
function Game() {
  // Game level
  this.level = 1;
  // Create objects for controlling the game
  this.gb;
  this.sh = new SetStyle();
}

O construtor Game tem como objetivo criar instâncias de um jogo com propriedades para rastrear o estado do jogo e métodos para lidar com funcionalidades do jogo, como iniciar o jogo, atualizar seu estado e renderizar visuais. No entanto, a lógica real para essas funcionalidades (dentro dos métodos start, update e render) não é fornecida atualmente, e espaços reservados estão no lugar para implementação futura.

✨ Verificar Solução e Praticar

Configurando Detalhes do Jogo

Defina os detalhes do jogo nos métodos prototype da classe Game:

// Prototype method of the Game class, controlling the specific game logic, make sure to reset the 'this' reference
Game.prototype = {
  processClick: function (w, h) {
    this.gb.processClick(w, h);
    this.updateCounts();
    if (this.gb.isGameWin()) {
      this.gameEnd();
    }
  },
  // Start the game
  beginGame: function () {
    this.setupLevel();
  },
  // Game end
  gameEnd: function () {
    this.level++;
    this.resetGame();
  },
  // Reset the game, redirect 'this' using bind
  resetGame: function () {
    $("#levelDescriptor").html("Enter Level " + this.level);
    setTimeout(
      function () {
        this.setupLevel(); // When 'this' is not reset, it refers to the window object
      }.bind(this),
      500
    ); // Use bind to redirect 'this' from window to the instance
  },
  // Set the difficulty level
  setupLevel: function () {
    this.gb = new GameBoard(this.level, this.level);
    $(".board").html(""); // Clear game board
    this.gb.populate(); // Reset all tiles to orange color
    this.gb.renderBoard(); // Render game board and create tiles
    this.sh.setGridSize(this.level); // Control tile size in game area
    this.updateCounts(); // Update current level display
    this.applyBindings(); // Flip the colors of tiles around the clicked tile
  },
  // Update current level display
  updateCounts: function () {
    $(".currLevel").html("Current Level: <b>" + this.level + "</b>");
  },
  applyBindings: function () {
    var that = this; // Save 'this' as a variable before the DOM event callback for easy reference
    $(".gamesquare").click(function () {
      // Get the position of the clicked tile
      var cname = $(this).attr("class").split(" ")[1];
      var coord = cname.substring(5).split("q");
      var height = parseInt(coord[1]);
      var width = parseInt(coord[0]);
      that.processClick(width, height);
    });
  },
  onNewGameClick: function () {
    this.level = 1;
    this.setupLevel();
  }
};

Este código estende a funcionalidade do construtor Game adicionando métodos prototype. Esses métodos definem a lógica principal do jogo e as interações.

✨ Verificar Solução e Praticar

Definindo as Coordenadas dos Blocos

Este código define uma função construtora chamada GameBoard que é usada para criar objetos de tabuleiro de jogo.

// xPos, yPos are the coordinates of the block
function GameBoard(xPos, yPos) {
  // Game board
  // Tile coordinates
  this.high = yPos - 1; // Index starts from 0
  this.wide = xPos - 1; // Index starts from 0
  this.count = 0;
  // The horizontal coordinate is wide, and the vertical coordinate is high
  //    [0][0] |  [0][1]
  //  - - - - - - - - - - - -
  //    [1][0] |  |[1][1]

  // Create a two-dimensional array of tiles
  this.board = new Array(xPos);
  for (var i = 0; i <= this.wide; i++) {
    this.board[i] = new Array(yPos);
  }
}
✨ Verificar Solução e Praticar

Definindo as Regras do Jogo

O trecho de código fornecido estende a funcionalidade do construtor GameBoard adicionando métodos prototype que definem as regras principais do jogo e a lógica de renderização.

// Implementation of game rules
GameBoard.prototype = {
  renderBoard: function () {
    var htmlString = ""; // Structure of game squares
    for (var j = 0; j <= this.high; j++) {
      htmlString += "<div class='gamerow'>";
      for (var i = 0; i <= this.wide; i++) {
        htmlString += "<div class='gamesquare coord" + i + "q" + j + "'></div>";
      }
      htmlString += "</div>";
    }
    $(".board").html(htmlString);

    for (var i = 0; i <= this.wide; i++) {
      for (var j = 0; j <= this.high; j++) {
        this.processClickView(i, j);
      }
    }
  },
  processClick: function (w, h) {
    //
    // Flip the colors of the blocks surrounding the clicked block
    //

    // Find the surrounding blocks that need to be flipped
    var lowx = w - 1;
    var highx = w + 1;
    var lowy = h - 1;
    var highy = h + 1;

    // Check if the clicked block is an edge block
    if (w == 0) lowx = 0;
    if (w == this.wide) highx = this.wide;
    if (h == 0) lowy = 0;
    if (h == this.high) highy = this.high;

    // Flip the vertically adjacent blocks of the clicked block
    for (var i = lowy; i <= highy; i++) {
      if (this.board[w][i] == 0) {
        this.board[w][i] = 1;
        this.count++;
      } else {
        this.board[w][i] = 0;
        this.count--;
      }
      this.processClickView(w, i);
    }

    // Flip the horizontally adjacent blocks of the clicked block
    for (var i = lowx; i <= highx; i++) {
      if (i == w) continue;
      if (this.board[i][h] == 0) {
        this.board[i][h] = 1;
        this.count++;
      } else {
        this.board[i][h] = 0;
        this.count--;
      }
      this.processClickView(i, h);
    }
  },
  // Flip the color of a block
  processClickView: function (w, h) {
    var coord = ".coord" + w + "q" + h;
    if (this.board[w][h] == 0) {
      $(coord).css("background-color", "#e8BB39");
    } else {
      $(coord).css("background-color", "#6060e0");
    }
  },

  // Reset all blocks to orange color
  populate: function () {
    for (var i = 0; i <= this.wide; i++) {
      for (var j = 0; j <= this.high; j++) {
        this.board[i][j] = 0;
      }
    }
  },

  // Game win condition
  isGameWin: function () {
    return this.count == (this.wide + 1) * (this.high + 1);
  }
};
✨ Verificar Solução e Praticar

Inicializando o Jogo

Este código configura o jogo quando o documento está pronto. Ele inicializa o jogo, inicia o primeiro nível e também configura os ouvintes de eventos para reiniciar o nível atual ou iniciar um novo jogo.

// Initialize the game
$(document).ready(function () {
  // Create the game
  var game = new Game();
  // Begin the game
  game.beginGame();

  // Reset the level tiles
  $("#resetLevelConfirm").click(function () {
    game.setupLevel();
  });

  // Start a new game
  $("#newGameConfirm").click(function () {
    game.onNewGameClick();
  });
});
✨ Verificar Solução e Praticar

Executando o Aplicativo

  • Abra index.html em um navegador web.
    open web
  • O efeito da página é o seguinte:
    Image description
✨ Verificar Solução e Praticar

Resumo

Do desenvolvimento deste jogo, encontramos:

  • Métodos orientados a objetos em JavaScript
  • Referência interna this e redirecionamento em JavaScript
  • Como manipular o DOM com jQuery
  • Questões de relação matricial

Na verdade, não precisamos implementar essa lógica, mas precisamos aprender a mentalidade. Para resolver um problema, precisamos primeiro analisar o problema e esclarecer as relações lógicas envolvidas. Essa é a chave para a resolução de problemas.