Introducción
2048 es un juego extremadamente popular y fácil de aprender que ha tomado el mundo por la tormenta. Si aún no lo has jugado, puedes descargarlo en tu teléfono móvil para probarlo. Este proyecto te guiará en el uso de HTML, CSS, JavaScript y jQuery para crear una versión web del juego 2048.
- Aprender el proceso de desarrollo de una aplicación web
- Explorar cómo hacer que la aplicación sea responsive en dispositivos móviles para adaptarse a pantallas de varios tamaños y manejar el diseño y la inicialización
- Utilizar JavaScript y jQuery para escribir la lógica del juego, implementar el movimiento de bloques y determinar los resultados del juego.
👀 Vista previa

🎯 Tareas
En este proyecto, aprenderás:
- Cómo crear el diseño de página de un juego web 2048 utilizando HTML y CSS
- Cómo implementar la lógica del juego en JavaScript y jQuery
- Cómo manejar el movimiento de bloques y la fusión de bloques
- Cómo probar y ejecutar el juego web en un navegador web
🏆 Logros
Después de completar este proyecto, podrás:
- Desarrollar una aplicación web responsive para el juego 2048
- Utilizar JavaScript y jQuery para escribir la lógica y la funcionalidad del juego
- Implementar el movimiento y la fusión de bloques en el juego
- Probar y ejecutar un juego web en un navegador web
Preparación del desarrollo
Dejando a un lado el estilo de página, el juego 2048 se puede abstraer como una matriz bidimensional. En el estado inicial, se deben generar dos números al azar. Los números generados al azar solo pueden ser 2 o 4. En la implementación siguiente, haremos que la probabilidad de aparición de 2 y 4 sea igual. Por supuesto, también puedes hacer que la probabilidad de aparición de 4 sea menor.
0 0 0 0
2 0 0 0
0 0 2 0
Luego, cuando presiono una tecla direccional, los números se moverán en esa dirección y las celdas adyacentes con el mismo número se fusionarán. Luego, se generará otro número al azar. Por ejemplo, si presionamos Arriba → Izquierda → Arriba, la interfaz puede quedar así:
2 0 2 0
0 0 0 0
0 0 0 0
4 0 0 0
4 0 0 0
0 0 0 0
0 0 2 0
4 0 0 0
8 0 2 0
0 0 0 0
0 4 0 0
De esta manera, seguimos moviendo y fusionando números hasta que ya no se puedan fusionar o hasta que aparezca 2048, terminando el juego. Como puedes ver, el núcleo del juego es manipular esta matriz bidimensional.
Estructura de archivos del proyecto
Primero, debemos crear la siguiente estructura de archivos en el camino ~/proyecto:
~/proyecto
|__ index.html
|__ main.js
|__ support.js
|__ showanimation.js
|__ style.css
jquery.min.js ya se encuentra en el camino ~/proyecto, por lo que se puede utilizar directamente.
Ahora, ¡comencemos oficialmente con nuestras tareas de codificación! Empezaremos con el diseño de página y gradualmente completaremos módulos como la inicialización del tablero de ajedrez y el movimiento de los bloques de números.
Diseño de página
Como es probable que hayas observado, el juego 2048 se construye sobre 16 cuadrados. A continuación, usaremos div + css para dibujar estas celdas de 4X4. Agregue el siguiente código al archivo index.html:
<!doctype html>
<html>
<head>
<meta charset="UTF-8" />
<meta
name="viewport"
content="width=device-width, height=device-height, initial-scale=1.0, minimum-scale=1.0, maximum-scale=1.0, user-scalable=no"
/>
<title>Juego 2048</title>
<link rel="stylesheet" href="style.css" />
<script type="text/javascript" src="jquery.min.js"></script>
<script type="text/javascript" src="main.js"></script>
<script type="text/javascript" src="showanimation.js"></script>
<script type="text/javascript" src="support.js"></script>
</head>
<body>
<header>
<h1>Juego 2048</h1>
<a href="javascript:new_game();" id="new_game_button">Nuevo Juego</a>
<p>puntuación: <span id="score">0</span></p>
</header>
<div id="grid_container">
<div class="grid_cell" id="grid_cell_0_0"></div>
<div class="grid_cell" id="grid_cell_0_1"></div>
<div class="grid_cell" id="grid_cell_0_2"></div>
<div class="grid_cell" id="grid_cell_0_3"></div>
<div class="grid_cell" id="grid_cell_1_0"></div>
<div class="grid_cell" id="grid_cell_1_1"></div>
<div class="grid_cell" id="grid_cell_1_2"></div>
<div class="grid_cell" id="grid_cell_1_3"></div>
<div class="grid_cell" id="grid_cell_2_0"></div>
<div class="grid_cell" id="grid_cell_2_1"></div>
<div class="grid_cell" id="grid_cell_2_2"></div>
<div class="grid_cell" id="grid_cell_2_3"></div>
<div class="grid_cell" id="grid_cell_3_0"></div>
<div class="grid_cell" id="grid_cell_3_1"></div>
<div class="grid_cell" id="grid_cell_3_2"></div>
<div class="grid_cell" id="grid_cell_3_3"></div>
</div>
</body>
</html>
A continuación, necesitamos agregar estilos a la página y a cada celda. Agregue el siguiente código al archivo style.css:
body {
padding: 50px 0px;
}
header {
display: block;
margin: 0 auto;
width: 100%;
text-align: center;
}
header h1 {
font-family: Arial;
font-size: 40px;
font-weight: bold;
margin: 0 auto;
color: #776e65;
padding: 20px 0px;
}
header #new_game_button {
display: block;
margin: 0px auto;
width: 100px;
padding: 10px 10px;
background-color: #8f7a66;
font-family: Arial;
color: white;
border-radius: 10px;
text-decoration: none;
}
header #new_game_button:hover {
background-color: #9f8b77;
}
header p {
font-family: Arial;
font-size: 25px;
margin: 5px auto;
}
#grid_container {
width: 460px;
height: 460px;
padding: 20px;
margin: 0px auto;
background-color: #bbada0;
border-radius: 10px;
position: relative;
}
.grid_cell {
width: 100px;
height: 100px;
border-radius: 6px;
background-color: #ccc0b3;
position: absolute;
}
.number_cell {
border-radius: 6px;
font-family: Arial;
font-weight: bold;
font-size: 60px;
line-height: 100px;
text-align: center;
position: absolute;
}
Después de completar este paso, abra el archivo index.html usando Vista previa,

y deberíamos ver el siguiente resultado:

Inicializando el tablero de ajedrez
Con la creciente popularidad de los dispositivos móviles, necesitamos manejar la adaptabilidad a varios tamaños de pantalla.
Al comienzo del juego, necesitamos generar dos números aleatorios en el tablero de ajedrez. Esto se logra a través de código JavaScript.
En main.js, agregue lo siguiente:
var board = new Array(); // Los números en cada celda
var score = 0; // La puntuación
var has_conflicted = new Array(); // Bandera para resolver eliminaciones consecutivas
var startx = 0; // Coordenada x del punto de inicio al tocar la pantalla móvil
var starty = 0; // Coordenada y del punto de inicio al tocar la pantalla móvil
var endx = 0; // Coordenada x del punto final al tocar la pantalla móvil
var endy = 0; // Coordenada y del punto final al tocar la pantalla móvil
var success_string = "Success";
var gameover_string = "GameOver";
// Inicializar el tablero de ajedrez después de que se haya cargado el documento HTML
$(document).ready(function () {
// Manejar la adaptabilidad
prepare_for_mobile();
new_game();
});
// Iniciar un nuevo juego
function new_game() {
// Inicializar el tablero de ajedrez
init();
// Generar números en dos celdas aleatorias
generate_one_number();
}
// Inicializar
function init() {
for (var i = 0; i < 4; i++) {
for (var j = 0; j < 4; j++) {
var grid_cell = $("#grid_cell_" + i + "_" + j);
grid_cell.css("top", get_pos_top(i, j));
grid_cell.css("left", get_pos_left(i, j));
}
}
for (var i = 0; i < 4; i++) {
board[i] = new Array();
has_conflicted[i] = new Array();
for (var j = 0; j < 4; j++) {
board[i][j] = 0;
has_conflicted[i][j] = false;
}
}
update_board_view();
score = 0;
update_score(score);
}
// Actualizar la vista del tablero de ajedrez
function update_board_view() {
$(".number_cell").remove();
for (var i = 0; i < 4; i++) {
for (var j = 0; j < 4; j++) {
$("#grid_container").append(
'<div class="number_cell" id="number_cell_' + i + "_" + j + '"></div>'
);
var number_cell = $("#number_cell_" + i + "_" + j);
if (board[i][j] == 0) {
number_cell.css("width", "0px");
number_cell.css("height", "0px");
number_cell.css("top", get_pos_top(i, j) + cell_side_length / 2);
number_cell.css("left", get_pos_left(i, j) + cell_side_length / 2);
} else {
number_cell.css("width", cell_side_length);
number_cell.css("height", cell_side_length);
number_cell.css("top", get_pos_top(i, j));
number_cell.css("left", get_pos_left(i, j));
number_cell.css(
"background-color",
get_number_background_color(board[i][j])
);
number_cell.css("color", get_number_color(board[i][j]));
number_cell.text(board[i][j]);
}
has_conflicted[i][j] = false;
}
}
$(".number_cell").css("line-height", cell_side_length + "px");
$(".number_cell").css("font-size", 0.6 * cell_side_length + "px");
}
// Generar un número en una celda aleatoria
function generate_one_number() {
if (nospace(board)) {
return false;
}
// Posición aleatoria
var randx = parseInt(Math.floor(Math.random() * 4));
var randy = parseInt(Math.floor(Math.random() * 4));
var time = 0;
while (time < 50) {
if (board[randx][randy] == 0) {
break;
}
randx = parseInt(Math.floor(Math.random() * 4));
randy = parseInt(Math.floor(Math.random() * 4));
time++;
}
if (time == 50) {
for (var i = 0; i < 4; i++) {
for (var j = 0; j < 4; j++) {
if (board[i][j] == 0) {
randx = i;
randy = j;
}
}
}
}
// Número aleatorio
var rand_number = Math.random() < 0.5 ? 2 : 4;
// Mostrar el número aleatorio en la posición aleatoria
board[randx][randy] = rand_number;
show_number_with_animation(randx, randy, rand_number);
return true;
}
// Manejar la adaptabilidad
function prepare_for_mobile() {
if (document_width > 500) {
grid_container_width = 500;
cell_side_length = 100;
cell_space = 20;
}
$("#grid_container").css("width", grid_container_width - 2 * cell_space);
$("#grid_container").css("height", grid_container_width - 2 * cell_space);
$("#grid_container").css("padding", cell_space);
$("#grid_container").css("border-radius", 0.02 * grid_container_width);
$(".grid_cell").css("width", cell_side_length);
$(".grid_cell").css("height", cell_side_length);
$(".grid_cell").css("border-radius", 0.02 * grid_container_width);
}
Mejora la lógica del juego
A continuación, necesitamos mejorar la lógica del juego, incluyendo el movimiento de las fichas numéricas y la comprobación de si el juego ha terminado. Complete el siguiente código en support.js:
document_width = window.screen.availWidth; // El ancho de la pantalla
grid_container_width = 0.92 * document_width; // El ancho del tablero de juego
cell_side_length = 0.18 * document_width; // El tamaño de cada celda del grid
cell_space = 0.04 * document_width; // El espacio entre cada celda del grid
// Obtener la distancia de la celda del grid correspondiente desde la parte superior del tablero de juego
function get_pos_top(i, j) {
return cell_space + i * (cell_space + cell_side_length);
}
// Obtener la distancia de la celda del grid correspondiente desde el lado izquierdo del tablero de juego
function get_pos_left(i, j) {
return cell_space + j * (cell_space + cell_side_length);
}
// Obtener el color de fondo del número correspondiente
function get_number_background_color(number) {
switch (number) {
case 2:
return "#eee4da";
break;
case 4:
return "#ede0c8";
break;
case 8:
return "#f2b179";
break;
case 16:
return "#f59563";
break;
case 32:
return "#f67c5f";
break;
case 64:
return "#f65e3b";
break;
case 128:
return "#edcf72";
break;
case 256:
return "#edcc61";
break;
case 512:
return "#9c0";
break;
case 1024:
return "#33b5e5";
break;
case 2048:
return "#09c";
break;
case 4096:
return "#a6c";
break;
case 8192:
return "#93c";
break;
}
return "black";
}
// Obtener el color del número correspondiente
function get_number_color(number) {
if (number <= 4) return "#776e65";
return "white";
}
// Comprobar si hay alguna celda del grid vacía en el tablero de juego
function nospace(board) {
for (var i = 0; i < 4; i++) {
for (var j = 0; j < 4; j++) {
if (board[i][j] == 0) {
return false;
}
}
}
return true;
}
Mejorando los efectos de animación
A continuación, necesitamos mejorar los efectos de animación, incluyendo la visualización de las fichas numéricas y la actualización de la puntuación. Todo este código se encuentra en showanimation.js:
// Animación para mostrar las fichas numéricas
function show_number_with_animation(i, j, rand_number) {
var number_cell = $("#number_cell_" + i + "_" + j);
number_cell.css("background-color", get_number_background_color(rand_number));
number_cell.css("color", get_number_color(rand_number));
number_cell.text(rand_number);
number_cell.animate(
{
width: cell_side_length,
height: cell_side_length,
top: get_pos_top(i, j),
left: get_pos_left(i, j)
},
50
);
}
// Actualización de la puntuación
function update_score(score) {
$("#score").text(score);
}
Mover los azulejos numéricos
Después de completar el diseño y la inicialización, ahora implementaremos la funcionalidad para mover y eliminar las fichas numéricas hasta que el juego tenga éxito o fracase.
Agregue el siguiente código a main.js:
// Escuchar los movimientos de las teclas de flecha del teclado
$(document).keydown(function (event) {
if ($("#score").text() == success_string) {
new_game();
return;
}
switch (event.keyCode) {
case 37: // Izquierda
event.preventDefault();
if (move_left()) {
setTimeout("generate_one_number()", 210);
setTimeout("is_gameover()", 300);
}
break;
case 38: // Arriba
event.preventDefault();
if (move_up()) {
setTimeout("generate_one_number()", 210);
setTimeout("is_gameover()", 300);
}
break;
case 39: // Derecha
event.preventDefault();
if (move_right()) {
setTimeout("generate_one_number()", 210);
setTimeout("is_gameover()", 300);
}
break;
case 40: // Abajo
event.preventDefault();
if (move_down()) {
setTimeout("generate_one_number()", 210);
setTimeout("is_gameover()", 300);
}
break;
default:
break;
}
});
// Escuchar el evento touchstart en dispositivos móviles
document.addEventListener("touchstart", function (event) {
startx = event.touches[0].pageX;
starty = event.touches[0].pageY;
});
// Escuchar el evento touchmove en dispositivos móviles
document.addEventListener("touchmove", function (event) {
event.preventDefault();
});
// Escuchar el evento touchend en dispositivos móviles
document.addEventListener("touchend", function (event) {
endx = event.changedTouches[0].pageX;
endy = event.changedTouches[0].pageY;
var deltax = endx - startx;
var deltay = endy - starty;
if (
Math.abs(deltax) < 0.3 * document_width &&
Math.abs(deltay) < 0.3 * document_width
) {
return;
}
if ($("#score").text() == success_string) {
new_game();
return;
}
// Movimiento en el eje x
if (Math.abs(deltax) >= Math.abs(deltay)) {
if (deltax > 0) {
// Mover hacia la derecha
if (move_right()) {
setTimeout("generate_one_number()", 210);
setTimeout("is_gameover()", 300);
}
} else {
// Mover hacia la izquierda
if (move_left()) {
setTimeout("generate_one_number()", 210);
setTimeout("is_gameover()", 300);
}
}
} else {
// Movimiento en el eje y
if (deltay > 0) {
// Mover hacia abajo
if (move_down()) {
setTimeout("generate_one_number()", 210);
setTimeout("is_gameover()", 300);
}
} else {
// Mover hacia arriba
if (move_up()) {
setTimeout("generate_one_number()", 210);
setTimeout("is_gameover()", 300);
}
}
}
});
// Mover hacia la izquierda
function move_left() {
if (!can_move_left(board)) {
return false;
}
// Mover hacia la izquierda
for (var i = 0; i < 4; i++) {
for (var j = 1; j < 4; j++) {
if (board[i][j] != 0) {
for (var k = 0; k < j; k++) {
if (board[i][k] == 0 && no_block_horizontal(i, k, j, board)) {
show_move_animation(i, j, i, k);
board[i][k] = board[i][j];
board[i][j] = 0;
break;
} else if (
board[i][k] == board[i][j] &&
no_block_horizontal(i, k, j, board) &&
!has_conflicted[i][k]
) {
show_move_animation(i, j, i, k);
board[i][k] += board[i][j];
board[i][j] = 0;
// Sumar puntuación
score += board[i][k];
update_score(score);
has_conflicted[i][k] = true;
break;
}
}
}
}
}
setTimeout("update_board_view()", 200);
return true;
}
// Mover hacia la derecha
function move_right() {
if (!can_move_right(board)) {
return false;
}
// Mover hacia la derecha
for (var i = 0; i < 4; i++) {
for (var j = 2; j >= 0; j--) {
if (board[i][j] != 0) {
for (var k = 3; k > j; k--) {
if (board[i][k] == 0 && no_block_horizontal(i, j, k, board)) {
show_move_animation(i, j, i, k);
board[i][k] = board[i][j];
board[i][j] = 0;
break;
} else if (
board[i][k] == board[i][j] &&
no_block_horizontal(i, j, k, board) &&
!has_conflicted[i][k]
) {
show_move_animation(i, j, i, k);
board[i][k] += board[i][j];
board[i][j] = 0;
// Sumar puntuación
score += board[i][k];
update_score(score);
has_conflicted[i][k] = true;
break;
}
}
}
}
}
setTimeout("update_board_view()", 200);
return true;
}
// Mover hacia arriba
function move_up() {
if (!can_move_up(board)) {
return false;
}
// Mover hacia arriba
for (var j = 0; j < 4; j++) {
for (var i = 1; i < 4; i++) {
if (board[i][j] != 0) {
for (var k = 0; k < i; k++) {
if (board[k][j] == 0 && no_block_vertical(j, k, i, board)) {
show_move_animation(i, j, k, j);
board[k][j] = board[i][j];
board[i][j] = 0;
break;
} else if (
board[k][j] == board[i][j] &&
no_block_vertical(j, k, i, board) &&
!has_conflicted[k][j]
) {
show_move_animation(i, j, k, j);
board[k][j] += board[i][j];
board[i][j] = 0;
// Sumar puntuación
score += board[k][j];
update_score(score);
has_conflicted[k][j] = true;
break;
}
}
}
}
}
setTimeout("update_board_view()", 200);
return true;
}
// Mover hacia abajo
function move_down() {
if (!can_move_down(board)) {
return false;
}
// Mover hacia abajo
for (var j = 0; j < 4; j++) {
for (var i = 2; i >= 0; i--) {
if (board[i][j] != 0) {
for (var k = 3; k > i; k--) {
if (board[k][j] == 0 && no_block_vertical(j, i, k, board)) {
show_move_animation(i, j, k, j);
board[k][j] = board[i][j];
board[i][j] = 0;
break;
} else if (
board[k][j] == board[i][j] &&
no_block_vertical(j, i, k, board) &&
!has_conflicted[k][j]
) {
show_move_animation(i, j, k, j);
board[k][j] += board[i][j];
board[i][j] = 0;
// Sumar puntuación
score += board[k][j];
update_score(score);
has_conflicted[k][j] = true;
break;
}
}
}
}
}
setTimeout("update_board_view()", 200);
return true;
}
// Comprobar si el juego ha tenido éxito o fracasado
function is_gameover() {
for (var i = 0; i < 4; i++) {
for (var j = 0; j < 4; j++) {
if (board[i][j] == 2048) {
update_score(success_string);
return;
}
}
}
if (nospace(board) && nomove(board)) {
gameover();
}
}
// Actualizar el texto de fin de juego cuando el juego termina
function gameover() {
update_score(gameover_string);
}
Mejora support.js
A continuación, necesitamos mejorar el código en support.js, incluyendo la comprobación de si se puede mover o todavía se puede mover.
// Comprobar si se puede mover hacia la izquierda
function can_move_left(board) {
for (var i = 0; i < 4; i++) {
for (var j = 1; j < 4; j++) {
if (board[i][j] != 0) {
if (board[i][j - 1] == 0 || board[i][j] == board[i][j - 1]) {
return true;
}
}
}
}
return false;
}
// Comprobar si se puede mover hacia la derecha
function can_move_right(board) {
for (var i = 0; i < 4; i++) {
for (var j = 2; j >= 0; j--) {
if (board[i][j] != 0) {
if (board[i][j + 1] == 0 || board[i][j] == board[i][j + 1]) {
return true;
}
}
}
}
return false;
}
// Comprobar si se puede mover hacia arriba
function can_move_up(board) {
for (var j = 0; j < 4; j++) {
for (var i = 1; i < 4; i++) {
if (board[i][j] != 0) {
if (board[i - 1][j] == 0 || board[i - 1][j] == board[i][j]) {
return true;
}
}
}
}
return false;
}
// Comprobar si se puede mover hacia abajo
function can_move_down(board) {
for (var j = 0; j < 4; j++) {
for (var i = 2; i >= 0; i--) {
if (board[i][j] != 0) {
if (board[i + 1][j] == 0 || board[i + 1][j] == board[i][j]) {
return true;
}
}
}
}
return false;
}
// Comprobar si no hay bloques en la dirección horizontal
function no_block_horizontal(row, col1, col2, board) {
for (var i = col1 + 1; i < col2; i++) {
if (board[row][i] != 0) {
return false;
}
}
return true;
}
// Comprobar si no hay bloques en la dirección vertical
function no_block_vertical(col, row1, row2, board) {
for (var i = row1 + 1; i < row2; i++) {
if (board[i][col] != 0) {
return false;
}
}
return true;
}
// Comprobar si todavía se puede mover
function nomove(board) {
if (
can_move_down(board) ||
can_move_up(board) ||
can_move_right(board) ||
can_move_left(board)
) {
return false;
}
return true;
}
Completa el juego
Finalmente, necesitamos completar el código en showanimation.js, incluyendo la visualización de la animación de movimiento y la animación de fusión.
// Efecto de animación cuando una celda del grid se mueve
function show_move_animation(fromx, fromy, tox, toy) {
var number_cell = $("#number_cell_" + fromx + "_" + fromy);
number_cell.animate(
{
top: get_pos_top(tox, toy),
left: get_pos_left(tox, toy)
},
200
);
}
Con esto, nuestra versión web de 2048 está completa.
Ejecución y pruebas
Abra index.html en un navegador web.

Para ver los siguientes efectos, haga clic en el botón Go Live en la esquina inferior derecha de WebIDE y cambie a la pestaña "Web 8080".

Resumen
En este proyecto, hemos implementado una versión web del juego 2048 utilizando HTML, CSS, JavaScript y jQuery. También hemos aprendido cómo hacerlo compatible con dispositivos móviles. Se cree que a través de este proyecto, puede profundizar su comprensión de la tecnología front-end y mejorar sus habilidades de aplicación integral.



