Juego de rompecabezas Flip con jQuery

JavaScriptJavaScriptBeginner
Practicar Ahora

💡 Este tutorial está traducido por IA desde la versión en inglés. Para ver la versión original, puedes hacer clic aquí

Introducción

Este proyecto, el "jQuery Flip Puzzle Game", ofrece una experiencia práctica en el desarrollo web, centrándose en JavaScript, jQuery y Bootstrap. Involucra la programación orientada a objetos en JavaScript y aborda el problema común del enlace de "this" en el contexto del desarrollo web. El juego se implementa utilizando jQuery y Bootstrap 3 para la interfaz de usuario. Aunque es útil tener conocimientos de Bootstrap, la lógica central del proyecto se construye con jQuery y JavaScript.

En el juego, los jugadores empiezan con una cuadrícula de bloques naranjas. Cada bloque tiene un lado naranja y un lado azul. Cuando un jugador hace clic en un bloque, su color se invierte y los colores de los bloques adyacentes también cambian. El objetivo es convertir todos los bloques a azul para completar el juego.

👀 Vista previa

jQuery Flip Puzzle Game

🎯 Tareas

En este proyecto, aprenderás:

  • Cómo implementar la programación orientada a objetos en JavaScript y abordar el problema de enlace de "this".
  • Cómo construir la lógica central del juego utilizando jQuery y JavaScript.
  • Cómo crear un juego de rompecabezas interactivo donde los jugadores invierten los colores de los bloques para ganar.

🏆 Logros

Después de completar este proyecto, serás capaz de:

  • Aplicar los principios de programación orientada a objetos en JavaScript.
  • Manejar el enlace de "this" en JavaScript para el control de eventos y métodos de objetos.
  • Desarrollar juegos web interactivos utilizando jQuery.
  • Utilizar Bootstrap 3 para crear interfaces visualmente atractivas y fáciles de usar.

Estructura básica de HTML

De acuerdo con la imagen de vista previa, se crea un diseño preliminar de página web. En Bootstrap, existe un diálogo Modal similar a la ventana emergente alert, y el estilo de Modal es más atractivo en comparación.

Necesitamos incluir los estilos CSS de Bootstrap, nuestros estilos CSS personalizados, el archivo jQuery, el archivo JavaScript de Bootstrap y el archivo JavaScript principal del juego en el proyecto. Escribe el siguiente código en la etiqueta head de la página HTML:

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

  <!-- Incluir jQuery y 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>
  <!-- Incluir JavaScript principal del juego -->
  <script src="game.js"></script>
</head>
✨ Revisar Solución y Practicar

Configuración del área de juego

Para configurar el área de juego, incluyendo los botones para reiniciar el juego y otras funciones, inserta el siguiente código en la etiqueta body:

<div class="container">
  <div class="heading">
    <h1 class="title">jQuery Flip Puzzle Game</h1>
    <div class="scoresContainer">
      <!-- Muestra el nivel actual del juego -->
      <div class="currLevel">Nivel actual: <b>1</b></div>
    </div>
  </div>
  <div class="aboveGame">
    <!-- Botones del juego -->
    <a
      class="instruct btn btn-primary"
      data-toggle="modal"
      data-target="#instructions"
      >Instrucciones del juego</a
    >
    <a
      class="newgame btn btn-primary"
      data-toggle="modal"
      data-target="#newGame"
      >Reiniciar</a
    >
    <a
      class="reset btn btn-primary"
      data-toggle="modal"
      data-target="#restartLevel"
      >Reiniciar nivel</a
    >
  </div>
  <div class="board">
    <!-- Área de juego -->
    <div class="gamerow">
      <div class="gamesquare coord0q0"></div>
    </div>
  </div>
</div>
✨ Revisar Solución y Practicar

Edita el contenido del popup correspondiente al botón "Editar instrucciones del juego". Escribe el siguiente código debajo del código del área de juego:

<!-- Modal de juego -->
<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">Juego</h4>
      </div>
      <div class="modal-body">
        <p>
          Cómo ganar: Hacer que todas las piezas del rompecabezas se pongan
          azules.
        </p>
        <p>
          Juego: Cada cuadrado tiene un lado naranja y un lado azul. Cuando
          haces clic en un cuadrado, su color se invertirá y los colores de los
          cuadrados adyacentes también se invertirán.
        </p>
      </div>
      <div class="modal-footer">
        <button type="button" class="btn btn-primary" data-dismiss="modal">
          Comenzar el juego
        </button>
      </div>
    </div>
  </div>
</div>
✨ Revisar Solución y Practicar

Edita el contenido del modal correspondiente al botón "Reiniciar". Escribe el siguiente código debajo del código de la lógica del juego:

<!-- Modal de nuevo juego -->
<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">Reiniciar</h4>
      </div>
      <div class="modal-body">
        <p>¿Estás seguro de que quieres reiniciar?</p>
      </div>
      <div class="modal-footer">
        <button
          type="button"
          class="btn btn-primary"
          id="newGameConfirm"
          data-dismiss="modal"
        >
          Comenzar el juego
        </button>
      </div>
    </div>
  </div>
</div>
✨ Revisar Solución y Practicar

Modifica el contenido del popup correspondiente al botón "Reiniciar nivel". Agrega el siguiente código debajo del código de "Reiniciar nivel":

<!-- Modal de confirmación de reinicio de nivel -->
<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">Confirmación de reinicio de nivel</h4>
      </div>
      <div class="modal-body">
        <p>¿Estás seguro de que quieres reiniciar el nivel?</p>
      </div>
      <div class="modal-footer">
        <button
          type="button"
          class="btn btn-primary"
          id="resetLevelConfirm"
          data-dismiss="modal"
        >
          Reiniciar
        </button>
      </div>
    </div>
  </div>
</div>
✨ Revisar Solución y Practicar

Estilos CSS

Los estilos CSS del juego son relativamente simples. El código en style.css se muestra a continuación:

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

/* Nivel del juego */
.scoresContainer {
  float: right;
  text-align: right;
  font-size: 18px;
}

/* Botones del juego */
.aboveGame:after {
  display: block;
  margin: 20px 0;
  content: "";
  clear: both;
}

/* Área de juego */
.board {
  position: absolute;
  background-color: #5f5f5f;
  border-radius: 4px;
}

.gamesquare {
  float: left;
  margin-right: 15px;
  border-radius: 3px;
}
✨ Revisar Solución y Practicar

Configuración de la escena del juego

Las fichas en el panel de juego se almacenan como una matriz bidimensional, donde cada elemento tiene un valor de 0 (establecido en naranja) o 1 (establecido en azul). Inicialmente, todos los valores se establecen en 0. Cuando se hace clic en una ficha, su color se invierte, y se determinan las coordenadas de las fichas adyacentes para invertir también sus colores, mientras que también se cambian los valores en la matriz bidimensional. El juego se completa cuando todos los valores en la matriz se convierten en 1 (es decir, todas las fichas son azules).

Lógica principal:

  1. La relación entre el tamaño de la escena del juego, el nivel de dificultad y el tamaño y número de las fichas.

  2. Hacer clic en una ficha y cambiar el estado de las fichas adyacentes.

  3. Verificar si todos los estados de las fichas son 1.

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

  // Establece el tamaño y el espaciado de las fichas
  $(".gamesquare").css("margin-right", margin);
  $(".gamesquare").css("width", res);
  $(".gamesquare").css("height", res);

  // Establece la altura, el margen derecho y el margen inferior de cada fila
  $(".gamerow").css("height", res);
  $(".gamerow").css("margin-right", margin * -1);
  $(".gamerow").css("margin-bottom", margin);

  // Establece el relleno del área de juego
  $(".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;
};
✨ Revisar Solución y Practicar

Crear constructor de juego

// Constructor de juego
function Game() {
  // Nivel del juego
  this.level = 1;
  // Crear objetos para controlar el juego
  this.gb;
  this.sh = new SetStyle();
}

El constructor de Game está destinado a crear instancias de un juego con propiedades para rastrear el estado del juego y métodos para manejar funcionalidades del juego como iniciar el juego, actualizar su estado y renderizar visuales. Sin embargo, la lógica real para estas funcionalidades (dentro de los métodos start, update y render) actualmente no se proporciona, y se han dejado espacios en blanco para una implementación futura.

✨ Revisar Solución y Practicar

Establecimiento de los detalles del juego

Establece los detalles del juego en los métodos prototipales de la clase Game:

// Método prototipal de la clase Game, controla la lógica específica del juego, asegúrate de restablecer la referencia de 'this'
Game.prototype = {
  processClick: function (w, h) {
    this.gb.processClick(w, h);
    this.updateCounts();
    if (this.gb.isGameWin()) {
      this.gameEnd();
    }
  },
  // Inicia el juego
  beginGame: function () {
    this.setupLevel();
  },
  // Fin del juego
  gameEnd: function () {
    this.level++;
    this.resetGame();
  },
  // Reinicia el juego, redirige 'this' usando bind
  resetGame: function () {
    $("#levelDescriptor").html("Ingrese Nivel " + this.level);
    setTimeout(
      function () {
        this.setupLevel(); // Cuando 'this' no se reinicia, se refiere al objeto window
      }.bind(this),
      500
    ); // Utiliza bind para redirigir 'this' desde window hacia la instancia
  },
  // Establece el nivel de dificultad
  setupLevel: function () {
    this.gb = new GameBoard(this.level, this.level);
    $(".board").html(""); // Limpia el tablero de juego
    this.gb.populate(); // Restablece todos los bloques al color naranja
    this.gb.renderBoard(); // Renderiza el tablero de juego y crea los bloques
    this.sh.setGridSize(this.level); // Controla el tamaño de los bloques en el área de juego
    this.updateCounts(); // Actualiza la visualización del nivel actual
    this.applyBindings(); // Invierte los colores de los bloques alrededor del bloque clicado
  },
  // Actualiza la visualización del nivel actual
  updateCounts: function () {
    $(".currLevel").html("Nivel actual: <b>" + this.level + "</b>");
  },
  applyBindings: function () {
    var that = this; // Guarda 'this' como una variable antes del callback del evento DOM para una referencia fácil
    $(".gamesquare").click(function () {
      // Obtiene la posición del bloque clicado
      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 extiende la funcionalidad del constructor de Game al agregar métodos prototipales. Estos métodos definen la lógica principal del juego y las interacciones.

✨ Revisar Solución y Practicar

Establecimiento de las coordenadas de los bloques

Este código define una función constructora llamada GameBoard que se utiliza para crear objetos de tablero de juego.

// xPos, yPos son las coordenadas del bloque
function GameBoard(xPos, yPos) {
  // Tablero de juego
  // Coordenadas de la ficha
  this.high = yPos - 1; // El índice comienza en 0
  this.wide = xPos - 1; // El índice comienza en 0
  this.count = 0;
  // La coordenada horizontal es wide, y la coordenada vertical es high
  //    [0][0] |  [0][1]
  //  - - - - - - - - - - - -
  //    [1][0] |  |[1][1]

  // Crea una matriz bidimensional de fichas
  this.board = new Array(xPos);
  for (var i = 0; i <= this.wide; i++) {
    this.board[i] = new Array(yPos);
  }
}
✨ Revisar Solución y Practicar

Establecimiento de las reglas del juego

El fragmento de código que has proporcionado extiende la funcionalidad del constructor de GameBoard al agregar métodos prototipales que definen las reglas principales del juego y la lógica de renderizado.

// Implementación de las reglas del juego
GameBoard.prototype = {
  renderBoard: function () {
    var htmlString = ""; // Estructura de los cuadrados del juego
    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) {
    //
    // Invierte los colores de los bloques que rodean el bloque clicado
    //

    // Encuentra los bloques que rodean y deben ser invertidos
    var lowx = w - 1;
    var highx = w + 1;
    var lowy = h - 1;
    var highy = h + 1;

    // Verifica si el bloque clicado es un bloque de borde
    if (w == 0) lowx = 0;
    if (w == this.wide) highx = this.wide;
    if (h == 0) lowy = 0;
    if (h == this.high) highy = this.high;

    // Invierte los bloques adyacentes verticalmente al bloque clicado
    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);
    }

    // Invierte los bloques adyacentes horizontalmente al bloque clicado
    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);
    }
  },
  // Invierte el color de un bloque
  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");
    }
  },

  // Restablece todos los bloques al color naranja
  populate: function () {
    for (var i = 0; i <= this.wide; i++) {
      for (var j = 0; j <= this.high; j++) {
        this.board[i][j] = 0;
      }
    }
  },

  // Condición de victoria del juego
  isGameWin: function () {
    return this.count == (this.wide + 1) * (this.high + 1);
  }
};
✨ Revisar Solución y Practicar

Inicialización del juego

Este código configura el juego cuando el documento está listo. Inicializa el juego, comienza el primer nivel y también establece los oyentes de eventos para reiniciar el nivel actual o comenzar un nuevo juego.

// Inicializa el juego
$(document).ready(function () {
  // Crea el juego
  var game = new Game();
  // Comienza el juego
  game.beginGame();

  // Reinicia los bloques del nivel
  $("#resetLevelConfirm").click(function () {
    game.setupLevel();
  });

  // Comienza un nuevo juego
  $("#newGameConfirm").click(function () {
    game.onNewGameClick();
  });
});
✨ Revisar Solución y Practicar

Ejecutando la aplicación

  • Abra index.html en un navegador web.
    open web
  • El efecto de la página es el siguiente:
    Image description
✨ Revisar Solución y Practicar

Resumen

Desde el desarrollo de este juego, hemos encontrado:

  • Métodos orientados a objetos en JavaScript
  • Referencia y redirección interna de this en JavaScript
  • Cómo manipular el DOM con jQuery
  • Problemas de relación de matrices

En realidad no necesitamos implementar esta lógica, pero necesitamos aprender la mentalidad. Para resolver un problema, primero debemos analizar el problema y aclarar las relaciones lógicas implicadas. Ese es el secreto para resolver problemas.