Advertisement
  1. Web Design
  2. HTML/CSS
  3. HTML

Juegos HTML: Crea un Rompecabezas con Intercambio de Piezas en HTML5 Canvas

Scroll to top
Read Time: 22 min

() translation by (you can also view the original English article)

Crea un juego de intercambio de piezas en JavaScript. El resultado funcionará con cualquier imagen y tendrá niveles de dificultad ajustables. 

El Rompecabezas HTML5 Canvas Completado

Aquí está una demostración del rompecabezas que estaremos creando en nuestro tutorial de HTML:

Un par de comentarios:

  • Compatibilidad entre navegadores: Este rompecabezas se probó y funciona en todas las versiones de Safari, Firefox y Chrome que admiten el elemento canvas.
  • Dificultad Ajustable: Construiremos un control deslizante que te permite cambiar la dificultad antes de cada juego.

Comencemos

Para comenzar, crea un directorio para el proyecto. Coloca la imagen en el directorio que quieres utilizar para crear un rompecabezas. Cualquier imagen compatible con la web funciona y pude tener cualquier tamaño que desees. Solo asegúrate que se ajuste dentro del doblez de la ventana de tu navegador

1. Creando la Plantilla para Juegos HTML

Abre un nuevo archivo usando tu editor de texto favorito y guárdalo dentro del directorio del proyecto, junto a tu imagen. A continuación, completa esta plantilla HTML básica. 

1
<!DOCTYPE html>
2
<html>
3
    <head>
4
        <title>HTML5 Puzzle</title>
5
    </head>
6
7
    <body>
8
        <canvas id="canvas"></canvas>
9
        <br />
10
    	<label for="difficulty">Difficulty</label>
11
        <input type="range" min="2" max="16" value="4" id="difficulty" />
12
        <script>
13
        </script>
14
    </body>
15
</html>

Todo lo que necesitamos hacer aquí es crear una plantilla HTML5 estándar que contenga una etiqueta canvas  con el id de “canvas”, así como la dificultad del control deslizante. Trabajaremos en crear el control deslizante de dificultad más adelante. 

Ahora comienza colocando tu cursor dentro de la etiqueta de script. De ahora en adelante, todo JavaScript está dentro de esa etiqueta con la excepción de las variables iniciales. Estaré organizando las secciones por función, primero te mostraré el código y luego explicaré la lógica. 

¿Listo? Veamos cómo hacer rompecabezas.

2. Configurando Nuestras Variables

Configuremos nuestras variables en el tutorial de HTML y echemos un vistazo a cada una. 

1
const PUZZLE_HOVER_TINT = '#009900';
2
3
4
const canvas = document.querySelector("#canvas");
5
const stage = canvas.getContext("2d");
6
const img = new Image();
7
8
let difficulty = 4;
9
let pieces;
10
let puzzleWidth;
11
let puzzleHeight;
12
let pieceWidth;
13
let pieceHeight;
14
let currentPiece;
15
let currentDropPiece;
16
17
let mouse;

Para comenzar, tenemos la constante PUZZLE_HOVER_TINT. La constante PUZZLE_HOVER_TINT define cuál debe ser el tinte del color al desplazarte por las piezas de la imagen. 

A continuación tenemos una serie de variables:

  • CANVASSTAGE mantendrán una referencia al canvas y a su contexto de dibujo respectivamente. Hacemos esto para no tener que escribir la consulta completa cada vez que los utilicemos. ¡Y los estaremos usando mucho para crear un rompecabezas!
  • difficulty contiene el nivel de dificultad actual. Más adelante configuraremos este conjunto utilizando un menú deslizante, por ahora manténlo en 4.
  • img será una referencia de la imagen descargada, de la cual copiaremos pixeles durante toda la aplicación.
  • puzzleWidth, puzzleHeight, pieceWidthpieceHeight se usarán para almacenar las dimensiones de todo el rompecabezas JS y cada pieza individual. Los configuramos una vez para evitar calcularlos de nuevo cada vez que los necesitemos. 
  • currentPiece contiene una referencia a cada pieza que actualmente se arrastre. 
  • currentDropPiece contiene una referencia a la pieza actualmente en posición para ser soltada. (En la demostración del tutorial de HTML, esta pieza está resaltada en verde.)
  • mouse es una referencia que contendrá las posiciones actuales xy del cursor. Estas se actualizan cuando se da clic en el rompecabezas, así se determina determinar cuál pieza está seleccionada y cuando una pieza se arrastra para determinar cuál pieza está flotando. 

Ahora continuamos con las funciones en nuestro tutorial de HTML.

3. Inicializar la Imagen

1
img.addEventListener('load',onImage,false);
2
img.src = "mke.jpg";

La primer cosa que queremos hacer en nuestra aplicación es descargar la imagen para el rompecabezas JS. Primero se crea una instancia del objeto y se establece en nuestra variable img. A continuación escuchamos el evento load, que luego detonará nuestra función onImage() cuando la imagen termine de cargar. Finalmente, establecemos la fuente de la imagen que detona la carga. 

4. La FunciónonImage()

1
function onImage(e) {
2
    pieceWidth = Math.floor(img.width / difficulty);
3
	pieceHeight = Math.floor(img.height / difficulty);
4
	puzzleWidth = pieceWidth * difficulty;
5
	puzzleHeight = pieceHeight * difficulty;
6
	setCanvas();
7
	initPuzzle();
8
}

Ahora que la imagen para nuestros juegos HTML se cargó con éxito, podemos establecer la mayoría de las variables que declaramos anteriormente. Hacemos esto aquí porque ahora tenemos la información sobre la imagen y podemos establecer nuestros valores apropiadamente. 

Lo primero que haremos para crear un rompecabezas es calcular el tamaño de cada pieza. Lo haremos dividiendo el valor difficulty por el ancho y altura de la imagen cargada. También recortamos algunos bordes para que nos den números pares para trabajar y asegurarnos que cada pieza se puede cambiar de lugar con otra.

A continuación para cómo hacer rompecabezas, usamos los nuevos valores para determinar el tamaño total del rompecabezas y configurar dichos valores a puzzleWidthpuzzleHeight.

Finalmente, cancelamos algunas funciones: setCanvas() y initPuzzle().

5. La Función setCanvas()

1
function setCanvas() {
2
    canvas.width = puzzleWidth;
3
    canvas.height = puzzleHeight;
4
    canvas.style.border = "1px solid black";
5
}

Ahora que los valores para crear un rompecabezas están completos, queremos establecer nuestro elemento canvas. Primero, configuramos nuestra variable canvas  para hacer referencia a nuestro elemento canvasstage para referir a su context.

Ahora configuramos el width y el height de nuestro canvas para ajustar el tamaño de nuestra imagen recortada, luego aplicamos algunos estilos simples para crear un borde negro alrededor de nuestro canvas para mostrar los límites de nuestro rompecabezas.

6. La Función initPuzzle()

1
function initPuzzle() {
2
    pieces = [];
3
	mouse = { x: 0, y: 0 };
4
	currentPiece = null;
5
	currentDropPiece = null;
6
	stage.drawImage(
7
		img,
8
		0,
9
		0,
10
		puzzleWidth,
11
		puzzleHeight,
12
		0,
13
		0,
14
		puzzleWidth,
15
		puzzleHeight
16
	);
17
	createTitle("Click to Start Puzzle");
18
	buildPieces();
19
}

Aquí, inicializamos el rompecabezas. Configuramos esta función de manera que podamos llamarla más tarde cuando queramos volver a jugar el rompecabezas JS. Cualquier otra cosa que se necesite configurar antes de jugar no se necesitará ajustar nuevamente para nuestros juegos HTML.

Primero, configuramos pieces como una formación vacía y creamos el objeto mouse , que contendrá la posición de nuestro cursor a lo largo de la aplicación. A continuación, configuramos currentPiececurrentPieceDropnull. (En la primer partida estos valores ya estarán como null, pero queremos asegurarnos que se reinicien al volver a jugar el rompecabezas.)

Finalmente, ¡es momento de dibujar! Primero, para crear un rompecabezas dibujaremos toda la imagen que mostrará al jugador lo que creará. Después, haremos algunas instrucciones simples llamando a nuestra función createTitle() .

7. La Función createTitle() 

1
function createTitle(msg) {
2
    stage.fillStyle = "#000000";
3
    stage.globalAlpha = 0.4;
4
    stage.fillRect(100, puzzleHeight - 40, puzzleWidth - 200, 40);
5
    stage.fillStyle = "#FFFFFF";
6
    stage.globalAlpha = 1;
7
    stage.textAlign = "center";
8
    stage.textBaseline = "middle";
9
    stage.font = "20px Arial";
10
    stage.fillText(msg, puzzleWidth / 2, puzzleHeight - 20);
11
}

Aquí, crearemos un mensaje bastante simple que le indique al usuario hacer clic en el rompecabezas para comenzar. Nuestro mensaje estará en un rectángulo semi transparente que servirá como el fondo de nuestro texto. Esto permite al usuario ver la imagen detrás de él y también se asegura que el texto blanco será legible en cualquier imagen que elijas al crear un rompecabezas.

Solamente configuramos fillStyle a negro y globalAlpha0.4, antes de rellenar un pequeño rectángulo negro en la parte inferior de la imagen. 

Debido a que globalAlpha afecta a todo el canvas, tenemos que volver a configurarlo a 1 (opaco) antes de dibujar el texto. Para configurar nuestro título, establecemos el textAlign"center" y el textBaseline"middle". También podemos cambiar la fuente por medio de la propiedad font.

Para dibujar el texto, usamos el método fillText() . Mostramos la variable msg y la colocamos en el centro horizontal del canvas y el centro vertical del rectángulo.

8. La Función buildPieces() 

1
function buildPieces() {
2
    let i;
3
	let piece;
4
	let xPos = 0;
5
	let yPos = 0;
6
	for (i = 0; i < difficulty * difficulty; i++) {
7
		piece = {};
8
		piece.sx = xPos;
9
		piece.sy = yPos;
10
		pieces.push(piece);
11
		xPos += pieceWidth;
12
		if (xPos >= puzzleWidth) {
13
			xPos = 0;
14
			yPos += pieceHeight;
15
		}
16
	}
17
	document.onpointerdown = shufflePuzzle;
18
}

Finalmente, ¡es momento de crear un rompecabezas!

Lo haremos construyendo un objeto para cada pieza. Estos objetos no serán responsables de renderizar al canvas, más bien contendrán referencias sobre lo que hay que dibujar y dónde. Dicho esto, veamos como hacer rompecabezas. 

Antes que nada, declaremos algunas variables que estaremos reutilizando durante el ciclo. Queremos configurar el ciclo para iterar a través de la cantidad de piezas que necesitamos. Obtenemos este valor multiplicando difficulty por sí mismo. En este caso obtenemos 16.

En el Ciclo

1
for (i = 0; i < difficult * difficulty; i++) {
2
    piece = {};
3
	piece.sx = xPos;
4
	piece.sy = yPos;
5
	pieces.push(piece);
6
	xPos += pieceWidth;
7
	if (xPos >= puzzleWidth) {
8
		xPos = 0;
9
		yPos += pieceHeight;
10
	}
11
}

Comienza creando un objeto piece vacío. A continuación, añade las propiedades sxsy al objeto. En la primera iteración, estos valores son 0 y representan el punto en nuestra imagen desde donde comenzaremos a dibujar. Ahora, empújalo a la agrupación pieces. Este objeto también contendrá las propiedades xPosyPos, que nos dirán la posición actual del rompecabezas donde la pieza se debe dibujar. Estaremos alternando los objetos antes de que sea reproducible, por lo que aún no es necesario establecer estos valores. 

La última cosa haremos en cada ciclo es aumentar la variable xPos junto a pieceWidth. Antes de continuar con el ciclo, determinamos si necesitamos bajar a la siguiente fila de piezas verificando si xPos está más allá del ancho del rompecabezas. Si es así, volvemos a establecer xPos de nuevo a 0 y aumentamos yPos junto a pieceHeight.

Ahora ya tenemos debidamente guardadas todas nuestras piezas de rompecabezas en nuestra agrupación pieces. Para este punto, el código se termina de ejecutar y espera a que el usuario interactúe. Configuramos un listener para los eventos de clic en el document para lanzar la función shufflePuzzle() cuando se detone, lo que comenzará el juego. Un gran consejo para cómo usar HTML.

9. La FunciónshufflePuzzle() 

1
function shufflePuzzle() {
2
    pieces = shuffleArray(pieces);
3
	stage.clearRect(0, 0, puzzleWidth, puzzleHeight);
4
	let xPos = 0;
5
	let yPos = 0;
6
	for (const piece of pieces) {
7
		piece.xPos = xPos;
8
		piece.yPos = yPos;
9
		stage.drawImage(
10
			img,
11
			piece.sx,
12
			piece.sy,
13
			pieceWidth,
14
			pieceHeight,
15
			xPos,
16
			yPos,
17
			pieceWidth,
18
			pieceHeight
19
		);
20
		stage.strokeRect(xPos, yPos, pieceWidth, pieceHeight);
21
		xPos += pieceWidth;
22
		if (xPos >= puzzleWidth) {
23
			xPos = 0;
24
			yPos += pieceHeight;
25
		}
26
	}
27
	document.onpointerdown = onPuzzleClick;
28
}
1
function shuffleArray(o){
2
    for(var j, x, i = o.length; i; j = parseInt(Math.random() * i), x = o[--i], o[i] = o[j], o[j] = x);
3
    return o;
4
}

Primero lo primero: revuelve la agrupación pieces[]. Aquí estaré usando una buena función de utilidad que revolverá los índices de la agrupación a la que se transfirió. La explicación de esta función va más allá de nuestro tutorial de HTML, así que seguiremos adelante, sabiendo que revolvimos exitosamente nuestras piezas. (Para obtener una introducción básica al shuffling, echa un vistazo a este tutorial.)

Primero hay que despejar los gráficos dibujados en el canvas para hacer espacio al dibujo de nuestras piezas. A continuación, configuramos la agrupación de forma similar a como lo hicimos cuando creamos los objetos de pieza por primera vez. 

En el Ciclo

1
for (i = 0; i < pieces.length; i++) {
2
    piece = pieces[i];
3
	piece.xPos = xPos;
4
	piece.yPos = yPos;
5
	stage.drawImage(
6
		img,
7
		piece.sx,
8
		piece.sy,
9
		pieceWidth,
10
		pieceHeight,
11
		xPos,
12
		yPos,
13
		pieceWidth,
14
		pieceHeight
15
	);
16
	stage.strokeRect(xPos, yPos, pieceWidth, pieceHeight);
17
	xPos += _pieceWidth;
18
	if (xPos >= puzzleWidth) {
19
		xPos = 0;
20
		yPos += pieceHeight;
21
	}
22
}

Primero, utiliza la variable i para configurar nuestra referencia a la pieza del objeto en el ciclo. Ahora completamos las propiedades xPosyPos que mencioné anteriormente, lo que será nuestro 0 en nuestra primera itinerancia.

Ahora, por fin dibujamos nuestras piezas en el rompecabezas JS. 

El primer parámetro de drawImage() asigna la fuente de la imagen de la que queremos dibujar. A continuación usamos las propiedades sxsy de los objetos piece, junto con pieceWidth y pieceHeight, para rellenar los parámetros que declaran el área de la imagen de la que hay que dibujar. Los cuatro últimos parámetros ajustan el área del canvas donde queremos dibujar. Usamos los valores xPosyPos que estamos construyendo en el ciclo y asignando al objeto. 

Inmediatamente después de esto, dibujamos un trazo rápido alrededor de la pieza para darle un borde, lo que la separará gentilmente de las otras piezas. 

Ahora esperamos a que el usuario tome una pieza para configurar otro click listener. Esta vez, este detonará una función onPuzzleClick().

Final HTML5 Puzzle

10. La Función onPuzzleClick()

1
function onPuzzleClick(e) {
2
    if (e.layerX || e.layerX === 0) {
3
		mouse.x = e.layerX - canvas.offsetLeft;
4
		mouse.y = e.layerY - canvas.offsetTop;
5
	} else if (e.offsetX || e.offsetX === 0) {
6
		mouse.x = e.offsetX - canvas.offsetLeft;
7
		mouse.y = e.offsetY - canvas.offsetTop;
8
	}
9
	currentPiece = checkPieceClicked();
10
	if (currentPiece !== null) {
11
		stage.clearRect(
12
			currentPiece.xPos,
13
			currentPiece.yPos,
14
			pieceWidth,
15
			pieceHeight
16
		);
17
		stage.save();
18
		stage.globalAlpha = 0.9;
19
		stage.drawImage(
20
			img,
21
			currentPiece.sx,
22
			currentPiece.sy,
23
			pieceWidth,
24
			pieceHeight,
25
			mouse.x - pieceWidth / 2,
26
			mouse.y - pieceHeight / 2,
27
			pieceWidth,
28
			pieceHeight
29
		);
30
		stage.restore();
31
        document.onpointermove = updatePuzzle;
32
        document.onpointerup = pieceDropped;
33
	}
34
}

Sabemos que se le dio clic al rompecabezas JS, ahora necesitamos determinar la pieza a la que se le dio clic. Este sencillo condicional nos dará la posición de nuestro cursor en todos los navegadores de escritorio modernos que admiten canvas, utilizando tanto e.layerXe.layerY o e.offsetX y e.offsetY. Utiliza estos valores para actualizar nuestro objeto mouse al asignarle una propiedad  xy para mantener la posición actual del cursor. En este caso, la posición a la que se le dio clic.

En la línea 112, establecemos inmediatamente currentPiece en el valor devuelto por nuestra función checkPieceClicked(). Separamos este código porque queremos utilizarlo más adelante cuando arrastremos una pieza de nuestro rompecabezas JS. Te explicaré esta función en el siguiente paso. 

Si el valor devuelto fue null, simplemente no hacemos nada, ya que esto implica que el usuario no hizo clic en alguna pieza del rompecabezas. Sin embargo, si recuperamos una pieza del rompecabezas, queremos adjuntarla al mouse y desvanecerla un poco para mostrar las piezas de abajo. ¿Cómo lo hacemos?

Primero, borramos el área del canvas donde se encontraba la pieza antes de dar clic en ella. Usamos clearRect() de nuevo, solo que en este caso solo pasamos al área obtenida del objeto currentPiece. Antes de volver a dibujarla, queremos save() el contexto del canvas antes de seguir. Esto asegurará que todo lo que dibujemos después de guardar, no se dibuje simplemente sobre cualquier cosa en su camino. Hacemos esto porque desvaneceremos ligeramente la pieza arrastrada y queremos ver las piezas debajo de ella. Si no llamamos save(), solamente dibujamos sobre cualquier gráfico que esté en el camino, aún si está desvanecido o no. 

Ahora, dibujamos la imagen de manera que su centro se posicione en el puntero del mouse. Los primeros cinco parámetros de drawImage siempre serán los mismos durante toda la aplicación. Al hacer clic, los dos parámetros se actualizarán para centrarse en el puntero del mouse. Los dos últimos parámetros, el width y el height para dibujar, tampoco cambiarán nunca.

Finalmente, llamamos el método restore(). Esto esencialmente significa que terminamos de usar el nuevo valor alfa y queremos reestablecer las propiedades como estaban. Para finalizar esta función, agregamos dos listeners más: uno para cuando movemos el mouse (arrastrando la pieza del rompecabezas) y otro para cuando lo soltamos (soltar la pieza del rompecabezas). 

11. La Función checkPieceClicked()

1
function checkPieceClicked() {
2
    for (const piece of pieces) {
3
		if (
4
			mouse.x < piece.xPos ||
5
			mouse.x > piece.xPos + pieceWidth ||
6
			mouse.y < piece.yPos ||
7
			mouse.y > piece.yPos + pieceHeight
8
		) {
9
			//PIECE NOT HIT

10
		} else {
11
			return piece;
12
		}
13
	}
14
	return null;
15
}

Ahora necesitamos retroceder un poco en nuestro tutorial de HTML. Ya determinamos a qué pieza se le dio clic, ¿pero cómo lo hicimos? Esto es muy sencillo de hecho. Lo que necesitamos hacer es recorrer todas las piezas del rompecabezas y determinar si el clic estaba dentro de los límites de nuestros objetos. Si encontramos uno, devolvemos el objeto que coincide y terminamos la función. Si no encontramos nada, devolvemos null.

12. La Función updatePuzzle() 

1
function updatePuzzle(e) {
2
    currentDropPiece = null;
3
    if (e.layerX || e.layerX === 0) {
4
		mouse.x = e.layerX - canvas.offsetLeft;
5
		mouse.y = e.layerY - canvas.offsetTop;
6
	} else if (e.offsetX || e.offsetX === 0) {
7
		mouse.x = e.offsetX - canvas.offsetLeft;
8
		mouse.y = e.offsetY - canvas.offsetTop;
9
	}
10
	stage.clearRect(0, 0, puzzleWidth, puzzleHeight);
11
	for (const piece of pieces) {
12
		if (piece === currentPiece) {
13
			continue;
14
		}
15
		stage.drawImage(
16
			img,
17
			piece.sx,
18
			piece.sy,
19
			pieceWidth,
20
			pieceHeight,
21
			piece.xPos,
22
			piece.yPos,
23
			pieceWidth,
24
			pieceHeight
25
		);
26
		stage.strokeRect(piece.xPos, piece.yPos, pieceWidth, pieceHeight);
27
		if (currentDropPiece === null) {
28
			if (
29
				mouse.x < piece.xPos ||
30
				mouse.x > piece.xPos + pieceWidth ||
31
				mouse.y < piece.yPos ||
32
				mouse.y > piece.yPos + pieceHeight
33
			) {
34
				//NOT OVER

35
			} else {
36
				currentDropPiece = piece;
37
				stage.save();
38
				stage.globalAlpha = 0.4;
39
				stage.fillStyle = PUZZLE_HOVER_TINT;
40
				stage.fillRect(
41
					currentDropPiece.xPos,
42
					currentDropPiece.yPos,
43
					pieceWidth,
44
					pieceHeight
45
				);
46
				stage.restore();
47
			}
48
		}
49
	}
50
	stage.save();
51
	stage.globalAlpha = 0.6;
52
	stage.drawImage(
53
		img,
54
		currentPiece.sx,
55
		currentPiece.sy,
56
		pieceWidth,
57
		pieceHeight,
58
		mouse.x - pieceWidth / 2,
59
		mouse.y - pieceHeight / 2,
60
		pieceWidth,
61
		pieceHeight
62
	);
63
	stage.restore();
64
	stage.strokeRect(
65
		mouse.x - pieceWidth / 2,
66
		mouse.y - pieceHeight / 2,
67
		pieceWidth,
68
		pieceHeight
69
	);
70
}

De vuelta al arrastre y continuando con nuestro tutorial para cómo usar HTML. Llamaremos esta función cuando el usuario mueva el mouse. Esta el la función más grande de la aplicación, ya que hace muchas cosas y te ayuda a saber cómo usar HTML. Comencemos. Iré explicando a medida que avancemos.

1
currentDropPiece = null;
2
if (e.layerX || e.layerX === 0) {
3
	mouse.x = e.layerX - canvas.offsetLeft;
4
	mouse.y = e.layerY - canvas.offsetTop;
5
} else if (e.offsetX || e.offsetX === 0) {
6
	mouse.x = e.offsetX - canvas.offsetLeft;
7
	mouse.y = e.offsetY - canvas.offsetTop;
8
}
9
	

Comienza configurando currentDropPiecenull. Necesitamos reestablecer esto de vuelta a null en la actualización debido a la posibilidad de que nuestra pieza se arrastre de vuelta a su sitio original. No queremos que el valor anterior currentDropPiece se quede colgando. A continuación, ajustamos el objeto mouse de la misma forma que hicimos al hacer clic.

1
stage.clearRect(0, 0, puzzleWidth, puzzleHeight);

Aquí, necesitamos borrar todos los gráficos en el canvas. Básicamente necesitamos volver a dibujar las piezas del rompecabezas, ya que el objeto que se arrastre a la parte superior afectará su apariencia. De lo contrario, veríamos resultados muy extraños al seguir el camino de la pieza arrastrada.

1
for (const piece of pieces) {

Comienza por configurar nuestras piezas de siempre en el ciclo. 

En el Ciclo

1
if(piece === currentPiece){
2
    continue;
3
}

Crea nuestra piece de referencia de manera usual. A continuación, revisa si la pieza a la que hacemos referencia es la que actualmente estamos arrastrando. Si este es el caso, sigue con el ciclo. Esto mantendrá vacío el espacio de inicio de la pieza arrastrada. 

1
stage.drawImage(
2
    img,
3
	piece.sx,
4
	piece.sy,
5
	pieceWidth,
6
	pieceHeight,
7
	piece.xPos,
8
	piece.yPos,
9
	pieceWidth,
10
	pieceHeight
11
);
12
stage.strokeRect(piece.xPos, piece.yPos, pieceWidth, pieceHeight);

Sigamos adelante, vuelve a dibujar la pieza del rompecabezas utilizando exactamente las propiedades que empleamos la primera vez que las dibujamos. También necesitarás dibujar los bordes.

1
if (currentDropPiece === null) {
2
    if (
3
		mouse.x < piece.xPos ||
4
		mouse.x > piece.xPos + pieceWidth ||
5
		mouse.y < piece.yPos ||
6
		mouse.y > piece.yPos + pieceHeight
7
	) {
8
		//NOT OVER

9
	} else {
10
		currentDropPiece = piece;
11
		stage.save();
12
		stage.globalAlpha = 0.4;
13
		stage.fillStyle = PUZZLE_HOVER_TINT;
14
		stage.fillRect(
15
			currentDropPiece.xPos,
16
			currentDropPiece.yPos,
17
			pieceWidth,
18
			pieceHeight
19
		);
20
		stage.restore();
21
	}
22
}

Debido a que tenemos una referencia para cada objeto en el ciclo, también podemos usar esta oportunidad para revisar si la pieza arrastrada está en la parte superior. Hacemos esto debido a que queremos darle al usuario retroalimentación respecto a qué pieza se puede colocar. Ahora adentrémonos en el código. 

Primero, queremos ver si este ciclo produjo un destino de colocación. Si es así, no necesitamos molestarnos, ya que se puede colocar un objetivo para cada movimiento del cursor. De no ser así, currentDropPiece será null, y podemos proceder con la lógica. Debido a que nuestro cursor está en medio de la pieza arrastrada, todo lo que realmente necesitamos hacer es determinar sobre qué otra pieza está nuestro mouse.

A continuación, usaremos nuestra útil función checkPieceClicked() para determinar si el cursor se está desplazando sobre el objeto de la pieza actual en el ciclo. De ser así, configuramos la variable currentDropPiece y dibujaremos un cuadro coloreado sobre la pieza del rompecabezas, así indicarás que ahora es el destino de colocación.

Recuerda hacer save()restore(). De otro modo, obtendrás el cuadro coloreado y no la imagen de abajo.  

Fuera del Ciclo

1
stage.save();
2
stage.globalAlpha = 0.6;
3
stage.drawImage(
4
	img,
5
	currentPiece.sx,
6
	currentPiece.sy,
7
	pieceWidth,
8
	pieceHeight,
9
	mouse.x - pieceWidth / 2,
10
	mouse.y - pieceHeight / 2,
11
	pieceWidth,
12
	pieceHeight
13
);
14
stage.restore();
15
stage.strokeRect(
16
	mouse.x - pieceWidth / 2,
17
	mouse.y - pieceHeight / 2,
18
	pieceWidth,
19
	pieceHeight
20
);

Por último pero no menos importante, necesitamos volver a dibujar la pieza arrastrada. El código es el mismo que cuando le dimos clic por primera vez, pero el cursor se movió, por lo que su posición se actualizará.

13. La Función pieceDropped() 

1
function pieceDropped(e){
2
    document.onpointermove = null;
3
    document.onpointerup = null;
4
    if(currentDropPiece !== null){
5
        let tmp = {xPos:currentPiece.xPos,yPos:currentPiece.yPos};
6
        currentPiece.xPos = currentDropPiece.xPos;
7
        currentPiece.yPos = currentDropPiece.yPos;
8
        currentDropPiece.xPos = tmp.xPos;
9
        currentDropPiece.yPos = tmp.yPos;
10
    }
11
    resetPuzzleAndCheckWin();
12
}

Muy bien, ya pasamos la parte más difícil. Ya arrastramos con éxito la pieza del rompecabezas e incluso obtuvimos retroalimentación visual sobre dónde se colocará. Ahora lo que falta es soltar la pieza. Primero, quitemos los listeners inmediatamente debido a que no se está arrastrando nada. 

Ahora, revisa que _currentDropPiece no está en null. Si lo está, significa que la estamos arrastrando de vuelta al área de inicio de la pieza y no sobre otro espacio. Si no está en null, podemos continuar con la función.

Lo que ahora tenemos que hacer es solamente cambiar el xPosyPos de cada pieza. Para este punto de nuestros juegos HTML, ambas piezas tienen los nuevos valores xPosyPos, y se instalarán en sus nuevas posiciones en la siguiente partida.  Eso es lo que haremos a continuación, revisar simultáneamente si se ganó el juego.

14. La Función resetPuzzleAndCheckWin() 

1
function resetPuzzleAndCheckWin() {
2
    stage.clearRect(0, 0, puzzleWidth, puzzleHeight);
3
	let gameWin = true;
4
	for (piece in pieces) {
5
		stage.drawImage(
6
			img,
7
			piece.sx,
8
			piece.sy,
9
			pieceWidth,
10
			pieceHeight,
11
			piece.xPos,
12
			piece.yPos,
13
			pieceWidth,
14
			pieceHeight
15
		);
16
		stage.strokeRect(piece.xPos, piece.yPos, pieceWidth, pieceHeight);
17
		if (piece.xPos != piece.sx || piece.yPos != piece.sy) {
18
			gameWin = false;
19
		}
20
	}
21
	if (gameWin) {
22
		setTimeout(gameOver, 500);
23
	}
24
}

El siguiente paso para cómo hacer rompecabezas, limpiaremos el canvas y configuraremos la variable gameWin, ajustándola a true por defecto. Ahora procede con nuestro conocido ciclo de piezas. 

El código aquí debería verse familiar, así que no lo repasaremos. Simplemente dibuja las piezas de vuelta a su posición original o a nuevos espacios. Dentro de este ciclo, queremos ver si cada pieza se está dibujando en su posición ganadora. Esto es sencillo: revisamos para ver si nuestras propiedades sxsy se ajustan con xPosyPos. De no ser así, no podríamos ganar el juego y configurar gameWinfalse. Si superamos el ciclo con todos en sus posiciones de ganar, configuramos un timeout rápido para llamar a nuestro método gameOver(). (Configuramos un tiempo de espera para que la pantalla no cambie tan drásticamente al dejar caer la pieza del rompecabezas)

15. La Función gameOver() 

1
function gameOver() {
2
    document.onpointerdown = null;
3
    document.onpointermove = null;
4
    document.onpointerup = null;
5
    initPuzzle();
6
}

¡Esta es nuestra última función para las mecánicas del juego principal y para aprender cómo hacer rompecabezas! Aquí, simplemente eliminaremos todos los listeners y llamaremos initPuzzle(), el cual reinicia todos los valores necesarios y espera a que el usuario vuelva a jugar

16. Agrega un Control Deslizante de Dificultad

Ahora, agregaremos el comportamiento al control deslizante de dificultad para cómo usar HTML. Primero, crea una función updateDifficulty().

1
function updateDifficulty(e) {
2
    difficulty = e.target.value;
3
    pieceWidth = Math.floor(img.width / difficulty);
4
    pieceHeight = Math.floor(img.height / difficulty);
5
    puzzleWidth = pieceWidth * difficulty;
6
    puzzleHeight = pieceHeight * difficulty;
7
    gameOver();
8
}

Veamos esto paso a paso para saber cómo usar HTML.

1
difficulty = e.target.value;

Esta parte actualiza la dificultad para que se establezca en el valor del control deslizante. 

1
pieceWidth = Math.floor(img.width / difficulty);
2
pieceHeight = Math.floor(img.height / difficulty);
3
puzzleWidth = pieceWidth * difficulty;
4
puzzleHeight = pieceHeight * difficulty;

Este fragmento actualiza las piezas para que tengan el tamaño correcto según la dificultad. 

1
gameOver();

Finalmente, esto reinicia el juego con la nueva dificultad.

Conclusión 

Como ves, puedes hacer muchas cosas nuevas y creativas en HTML5 mediante el uso de áreas de bit seleccionadas de imágenes y dibujos. Puedes ampliar esta aplicación fácilmente con tan solo agregar puntuación e incluso un temporizador para darle más aspectos a tus juegos HTML.

Este artículo se actualizó con las contribuciones de Jacob Jackson. Jacob es un desarrollador web, escritor técnico y colaborador de código abierto. 

Advertisement
Did you find this post useful?
Want a weekly email summary?
Subscribe below and we’ll send you a weekly email summary of all new Web Design tutorials. Never miss out on learning about the next big thing.
Advertisement
Looking for something to help kick start your next project?
Envato Market has a range of items for sale to help get you started.