Construir un juego de rompecabezas deslizante con JavaScript

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

Bienvenido a este proyecto de construcción de un sencillo juego de rompecabezas deslizante utilizando JavaScript. Al final de esta guía, tendrás un juego de rompecabezas deslizante 3x3 funcional que puedes jugar en tu navegador. El juego tendrá fichas numeradas, un temporizador y controles para iniciar, pausar y reiniciar el juego.

No es necesario tener experiencia previa, pero una comprensión básica de JavaScript y HTML resultará útil. ¡Vamos a sumergirnos!

👀 Vista previa

Vista previa del juego de rompecabezas deslizante

🎯 Tareas

En este proyecto, aprenderás:

  • Cómo diseñar el diseño del juego en HTML
  • Cómo escribir los estilos CSS para el juego
  • Cómo inicializar variables de juego en JavaScript
  • Cómo implementar la función de movimiento para manejar los movimientos de las fichas
  • Cómo determinar los posibles movimientos de las fichas
  • Cómo implementar el temporizador del juego
  • Cómo controlar el flujo del juego con las funciones de inicio, pausa y reinicio
  • Cómo barajar las fichas aleatoriamente al principio o al reiniciar
  • Cómo inicializar el juego al cargar la página

🏆 Logros

Después de completar este proyecto, podrás:

  • Diseñar el diseño de un juego utilizando HTML
  • Estilizar elementos utilizando CSS
  • Implementar la lógica del juego utilizando JavaScript
  • Manejar la entrada del usuario y realizar acciones en base a ella
  • Manipular el DOM para actualizar el estado del juego y mostrar información

Skills Graph

%%%%{init: {'theme':'neutral'}}%%%% flowchart RL javascript(("JavaScript")) -.-> javascript/BasicConceptsGroup(["Basic Concepts"]) javascript(("JavaScript")) -.-> javascript/DOMManipulationGroup(["DOM Manipulation"]) javascript/BasicConceptsGroup -.-> javascript/variables("Variables") javascript/BasicConceptsGroup -.-> javascript/data_types("Data Types") javascript/BasicConceptsGroup -.-> javascript/cond_stmts("Conditional Statements") javascript/BasicConceptsGroup -.-> javascript/loops("Loops") javascript/BasicConceptsGroup -.-> javascript/functions("Functions") javascript/BasicConceptsGroup -.-> javascript/array_methods("Array Methods") javascript/DOMManipulationGroup -.-> javascript/dom_select("DOM Selection") javascript/DOMManipulationGroup -.-> javascript/dom_manip("DOM Manipulation") javascript/DOMManipulationGroup -.-> javascript/event_handle("Event Handling") subgraph Lab Skills javascript/variables -.-> lab-445694{{"Construir un juego de rompecabezas deslizante con JavaScript"}} javascript/data_types -.-> lab-445694{{"Construir un juego de rompecabezas deslizante con JavaScript"}} javascript/cond_stmts -.-> lab-445694{{"Construir un juego de rompecabezas deslizante con JavaScript"}} javascript/loops -.-> lab-445694{{"Construir un juego de rompecabezas deslizante con JavaScript"}} javascript/functions -.-> lab-445694{{"Construir un juego de rompecabezas deslizante con JavaScript"}} javascript/array_methods -.-> lab-445694{{"Construir un juego de rompecabezas deslizante con JavaScript"}} javascript/dom_select -.-> lab-445694{{"Construir un juego de rompecabezas deslizante con JavaScript"}} javascript/dom_manip -.-> lab-445694{{"Construir un juego de rompecabezas deslizante con JavaScript"}} javascript/event_handle -.-> lab-445694{{"Construir un juego de rompecabezas deslizante con JavaScript"}} end

Diseña el diseño del juego en HTML

Diseña el diseño del juego en index.html tal como se mencionó en la descomposición 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 más externo, utilizado para contener la estructura interna -->
      <div id="game">
        <!-- Área del juego, que es el gran bloque DIV -->
        <div id="d1" onclick="move(1)">1</div>
        <!-- DIVs pequeños, que son los 8 bloques pequeños. Cuando se hace clic, se ejecuta la función move(). El parámetro es el número mostrado, así que sabemos qué bloque se hizo clic -->
        <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 control del juego -->
        <p>
          <rowspan id="timeText">Tiempo total</rowspan>
          <!--  "Tiempo total" -->
          <rowspan id="timer"></rowspan>
        </p>
        <!-- Área para mostrar el tiempo del juego -->
        <p>
          <rowspan id="start" onclick="start()">Iniciar</rowspan>
          <!--  "Iniciar" -->
          <rowspan id="reset" onclick="reset()">Reintentar</rowspan>
          <!--  "Reintentar" -->
        </p>
        <!-- Área para mostrar los botones de control -->
      </div>
    </div>
  </body>
</html>
✨ Revisar Solución y Practicar

Escribe los estilos CSS

Después de completar la estructura del diseño, es hora de embelezar nuestro juego con estilos CSS. Durante esta fase, tienes la libertad de personalizar y diseñar el juego según tus preferencias, agregando toques y detalles únicos. También podrías introducir más elementos decorativos para mejorar la estética del juego. Sin embargo, una nota importante: si cambias el tamaño del DIV principal del juego, asegúrate de ajustar el código JavaScript correspondiente. Lo discutiremos en mayor profundidad en breve.

En puzzle.css, agrega:

* {
  padding: 0;
  margin: 0;
  border: 0;
}
/* El '*' es un comodín que quita los estilos predeterminados de todos los elementos porque algunos navegadores pueden agregar estilos predeterminados, lo que podría causar problemas de diseño. */

body {
  width: 100%;
  height: 100%;
}
/* Establece la altura y el ancho del cuerpo en 100%, para que se adapte automáticamente según el tamaño de la pantalla del navegador. */

#container {
  position: relative;
  width: 620px;
  height: 450px;
  margin: 0 auto;
  margin-top: 100px;
  border-radius: 1px;
}
/* Este es el DIV que envuelve todos los elementos. Se establece con un ancho de 620px y una altura de 450px. Este tamaño se puede aumentar, pero no disminuir. Debe ser lo suficientemente grande para contener todos los elementos internos. */

#game {
  position: absolute;
  width: 450px;
  height: 450px;
  border-radius: 5px;
  display: inline-block;
  background-color: #ffe171;
  box-shadow: 0 0 10px #ffe171;
}
/* Este es el DIV del área del juego. El tamaño se calcula en base al tamaño de los bloques más pequeños. Aquí, los bloques se establecen en 150px por 150px, por lo que el área del juego es 150px * 3, lo que da 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; /* Prefijo del navegador para la compatibilidad con Chrome */
  -moz-transition: 0.3s; /* Para Firefox */
  -ms-transition: 0.3s; /* Para IE */
  -o-transition: 0.3s; /* Para Opera */
  transition: 0.3s;
}
/* Esto establece el tamaño de los bloques pequeños. Establecido como absoluto, los cambios en la posición no afectarán las posiciones de otros elementos. El ancho y la altura son ambos 149px. Notablemente, hay una sombra de caja, lo que agrega a su dimensión general. La propiedad 'transition:0.3s' hace que los cambios aparezcan como animaciones, por lo que cuando la posición del bloque cambia, se reproduce automáticamente una animación. */

#game div:hover {
  color: #ffe171;
}
/* Establece la animación al pasar el mouse sobre los bloques. Cuando el mouse pasa sobre un elemento, estas propiedades reemplazan a las anteriores y vuelven al original cuando el mouse se aleja. Aquí, estamos cambiando el color de la fuente. */

#control {
  width: 150px;
  height: 450px;
  display: inline-block;
  float: right;
}
/* Sección de control. 'display:inline-block' permite que el elemento mantenga las propiedades de bloque sin ocupar una línea completa, y 'float:right' lo coloca en el lado derecho. */

#control rowspan {
  height: 25px;
  font-size: 20px;
  color: #222;
  margin-top: 10px;
}
/* Estilos comunes para los botones en el área de control. */

#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 el botón 'Iniciar'. 'cursor:pointer' hace que el cursor cambie a un símbolo de mano cuando se pasa el mouse sobre él. */

#reset {
  display: inline-block;
  font-size: 28px;
  width: 100px;
  height: 28px;
  background-color: #20a6fa;
  color: #ffe171;
  text-shadow: 1px 1px 2px #ffe171; /* Sombra de la fuente */
  border-radius: 5px; /* Bordes redondeados */
  box-shadow: 2px 2px 5px #4c98f5; /* Sombra de la caja */
  text-align: center; /* Texto centrado */
  cursor: pointer;
}
/* Estilos para el botón 'Reintentar'. */

#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;
}
/* Posiciones predefinidas para cada uno de los bloques, en orden. */

Establecer los estilos es un paso importante adelante. A continuación, pasaríamos a la lógica de JavaScript para dar vida a nuestro juego de rompecabezas. Se recomienda hacer el estilo inicial según las instrucciones anteriores. Una vez que se entienda bien la funcionalidad del juego, estás libre de exhibir tu creatividad y personalizar aún más los estilos. Sin embargo, hacer cambios tempranos e informados puede llevar a errores imprevistos.
✨ Revisar Solución y Practicar

Inicializa las variables del juego en JavaScript

Antes de agregar la funcionalidad del juego, inicializa las variables del estado esencial del juego.

En puzzle.js, agrega:

// puzzle.js

var time = 0;
// Guarda el tiempo transcurrido
var pause = true;
// Bandera para indicar si el juego está pausado, true significa que está pausado
var set_timer;
// Función del temporizador
var d = new Array(10);
// Almacena el número del DIV pequeño actualmente en el DIV grande
var d_direct = new Array(
  [0],
  [2, 4], // El DIV grande con el número 1 puede ir a las posiciones 2 y 4
  [1, 3, 5],
  [2, 6],
  [1, 5, 7],
  [2, 4, 6, 8],
  [3, 5, 9],
  [4, 8],
  [5, 7, 9],
  [6, 8]
);
// Guarda las posiciones de movimiento posibles para los DIVs grandes
var d_posXY = new Array(
  [0],
  [0, 0], // El primer número representa la izquierda, el segundo representa la arriba. Por ejemplo, la posición del primer bloque es izquierda:0px, arriba:0px
  [150, 0],
  [300, 0],
  [0, 150],
  [150, 150],
  [300, 150],
  [0, 300],
  [150, 300],
  [300, 300]
);
// Posición de los DIVs grandes
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;
// Arreglo predeterminado, el noveno DIV grande está vacío, por lo que es 0. Usamos 0 para representar la ficha en blanco.
✨ Revisar Solución y Practicar

Implementa la función de movimiento

Crea una función que permita que las fichas se muevan en base a la interacción del usuario.

Agrega a puzzle.js:

// puzzle.js

function move(id) {
  // Función de movimiento
  var i = 1;
  for (i = 1; i < 10; ++i) {
    if (d[i] == id) break;
  }
  // Este bucle encuentra la posición del DIV pequeño dentro del DIV grande
  var target_d = 0;
  // Guarda las posiciones posibles para el DIV pequeño, 0 significa que no puede moverse
  target_d = whereCanTo(i);
  // Descubre a dónde puede moverse el DIV pequeño. Si devuelve 0, significa que no puede moverse, de lo contrario, devuelve el número de la posición objetivo
  if (target_d != 0) {
    d[i] = 0;
    // Establece el número del DIV grande actual a 0 porque el DIV pequeño se ha movido, por lo que el DIV grande actual ahora está vacío
    d[target_d] = id;
    // Establece el DIV grande objetivo al número del DIV pequeño 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, establece la posición del DIV pequeño clicado a la posición del DIV grande objetivo
  }

  var finish_flag = true;
  // Bandera para indicar si el juego ha sido completado, true significa que ha sido completado
  for (var k = 1; k < 9; ++k) {
    if (d[k] != k) {
      finish_flag = false;
      break;
    }
  }
  // Itera desde 1, comprueba el número de cada DIV grande. Si no están en orden, el juego no ha sido completado.
  if (finish_flag == true) {
    if (!pause) start();
    alert("¡Felicidades!");
  }
  // Si es true, el juego ha sido completado. Si no está pausado, llama a la función de pausa y muestra un mensaje de éxito.
}
✨ Revisar Solución y Practicar

Determina los movimientos posibles de las fichas

Crea una función para determinar a dónde puede moverse una ficha en base a su posición actual.

Agrega a puzzle.js:

// puzzle.js

function whereCanTo(cur_div) {
  // Función para determinar si un DIV dado puede moverse y a qué posición
  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;
  }
  // Si puede moverse, devuelve el número de la posición objetivo, de lo contrario devuelve 0
}
✨ Revisar Solución y Practicar

Implementa el temporizador del juego

Agrega una función de temporizador para llevar un registro del tiempo transcurrido durante el juego.

Agrega a puzzle.js:

// puzzle.js

function timer() {
  // Función del temporizador, se ejecuta cada segundo
  time += 1;
  var min = parseInt(time / 60); // Convierte segundos a minutos
  var sec = time % 60; // Obtiene los segundos restantes
  document.getElementById("timer").innerHTML =
    min + " minutos " + sec + " segundos";
}
✨ Revisar Solución y Practicar

Controla el flujo del juego

Implementa funciones para iniciar, pausar o reiniciar el juego en base a la entrada del usuario.

Agrega a puzzle.js:

// puzzle.js

function start() {
  // Inicia o pausa el juego
  if (pause) {
    document.getElementById("start").innerHTML = "Pausa";
    pause = false;
    set_timer = setInterval(timer, 1000);
  } else {
    document.getElementById("start").innerHTML = "Iniciar";
    pause = true;
    clearInterval(set_timer);
  }
}
function reset() {
  // Reinicia el juego
  time = 0;
  random_d();
  if (pause) start();
}
✨ Revisar Solución y Practicar

Baraja aleatoriamente las fichas

Crea una función para barajar aleatoriamente las fichas al principio o cuando se reinicia el juego.

Agrega a puzzle.js:

// puzzle.js

function random_d() {
  // Baraja aleatoriamente las fichas
  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;
  }
}
✨ Revisar Solución y Practicar

Inicializa el juego al cargar la página

Por último, asegúrate de que cuando se carga la página web, las fichas del juego se muestren y estén listas para jugar.

Agrega a puzzle.js:

// puzzle.js

// Inicializa el juego al cargar la página
window.onload = function () {
  reset();
};
✨ 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:
    puzzle game page preview

✨ Revisar Solución y Practicar

Resumen

¡Ahora has construido con éxito un juego de rompecabezas deslizante usando JavaScript de manera estructurada y organizada! La descomposición paso a paso debería facilitar la comprensión de cada componente de la lógica del juego para los aprendices. Recuerda, esta es solo una versión básica y hay mucho margen para mejoras y características adicionales. Sumérgete y hazlo tuyo. ¡Que la codificación te divierta!