Cómo Crear un Juego de Memoria con JavaScript y CSS: Tutorial Paso a Paso para Principiantes
Crear un juego de memoria es una excelente manera de practicar tus habilidades en JavaScript, CSS y HTML. En este tutorial completo y sencillo, aprenderás a programar un juego interactivo desde cero. Este proyecto es ideal para desarrolladores principiantes que buscan fortalecer su comprensión de la manipulación del DOM, eventos y animaciones.
Objetivo: Crear un juego de memoria web, donde el usuario debe encontrar pares de tarjetas iguales volteándolas.
Contenido
- 1. Estructura HTML básica del juego
- 2. Diseño visual con CSS para tarjetas y animaciones
- 3. Lógica en JavaScript para el juego de memoria
- 4. Mejoras y consejos finales
1. Estructura HTML básica del juego
El HTML será simple y semántico. Crearemos una sección principal que contendrá el tablero de tarjetas, un mensaje para informar el estado y un botón para reiniciar.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 |
<!DOCTYPE html> <html lang="es"> <head> <meta charset="UTF-8"> <meta name="viewport" content="width=device-width, initial-scale=1.0"> <title>Juego de Memoria JavaScript</title> <link rel="stylesheet" href="styles.css"> </head> <body> <h1>Juego de Memoria</h1> <p id="mensaje">Encuentra todos los pares</p> <section class="tablero" id="tablero"> <!-- Las tarjetas se generarán con JavaScript --> </section> <button id="reiniciar">Reiniciar Juego</button> <script src="script.js"></script> </body> </html> |
Explicación
- El elemento
<section>
con idtablero
será donde se colocarán todas las tarjetas. - El párrafo
mensaje
mostrará información dinámica (ejemplo: “¡Has ganado!”). - El botón
reiniciar
nos permitirá empezar un nuevo juego.
2. Diseño visual con CSS para tarjetas y animaciones
Un buen diseño mantiene la motivación para seguir programando. Aquí trabajaremos el aspecto de las tarjetas y la animación del volteo para que sea fluida.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 |
/* styles.css */ body { font-family: 'Segoe UI', Tahoma, Geneva, Verdana, sans-serif; background-color: #f0f4f8; display: flex; flex-direction: column; align-items: center; padding: 20px; margin: 0; } h1 { color: #333; } #mensaje { margin-bottom: 20px; font-size: 1.1rem; color: #555; } .tablero { width: 340px; /* Ajusta para 16 tarjetas en 4x4 */ display: grid; grid-template-columns: repeat(4, 1fr); gap: 15px; margin-bottom: 20px; } .tarjeta { perspective: 1000px; /* Necesario para efecto 3D */ cursor: pointer; } .tarjeta__contenido { position: relative; width: 80px; height: 80px; transition: transform 0.6s; transform-style: preserve-3d; border-radius: 10px; box-shadow: 0 4px 6px rgba(0,0,0,0.1); } .tarjeta.volteada .tarjeta__contenido { transform: rotateY(180deg); } .tarjeta__cara { position: absolute; width: 100%; height: 100%; backface-visibility: hidden; border-radius: 10px; display: flex; justify-content: center; align-items: center; font-size: 2.5rem; user-select: none; } .tarjeta__cara--frontal { background-color: #3b82f6; color: white; } .tarjeta__cara--trasera { background-color: #ffffff; color: #333; transform: rotateY(180deg); border: 2px solid #3b82f6; } #reiniciar { padding: 10px 20px; font-size: 1rem; border: none; border-radius: 7px; background-color: #3b82f6; color: #fff; cursor: pointer; box-shadow: 0 4px 8px rgba(59, 130, 246, 0.3); transition: background-color 0.3s ease; } #reiniciar:hover { background-color: #2563eb; } |
Explicación CSS
- La clase
.tarjeta
envuelve cada carta y provee perspectiva para animación 3D. .tarjeta__contenido
es la parte que gira: si tiene la clase.volteada
, rota 180 grados para revelar la otra cara.- Las caras frontal y trasera tienen estilos diferenciados para que se note el volteo.
- La grid del tablero acomoda las tarjetas en filas y columnas.
3. Lógica en JavaScript para el juego de memoria
Ahora viene la parte más divertida: la programación. El script manejará la creación de tarjetas, detección de pares, manejo de estados y mensajes.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 |
// script.js document.addEventListener('DOMContentLoaded', () => { const tablero = document.getElementById('tablero'); const mensaje = document.getElementById('mensaje'); const reiniciarBtn = document.getElementById('reiniciar'); // Estos serán los símbolos (pueden ser emojis o letras) const simbolos = ['🍎', '🍌', '🍇', '🍓', '🍍', '🥝', '🍉', '🍑']; let tarjetasArray = []; let tarjetaVolteada = false; let primerTarjeta, segundaTarjeta; let bloqueoTablero = false; // Evita que se volteen más tarjetas mientras se comparan let paresEncontrados = 0; // Inicializa el juego function iniciarJuego() { paresEncontrados = 0; mensaje.textContent = 'Encuentra todos los pares'; tarjetasArray = [...simbolos, ...simbolos]; // duplicamos los símbolos tarjetasArray = mezclar(tarjetasArray); tablero.innerHTML = ''; // Crear tarjetas en el DOM tarjetasArray.forEach((simbolo, index) => { const tarjeta = document.createElement('div'); tarjeta.classList.add('tarjeta'); tarjeta.dataset.simbolo = simbolo; tarjeta.innerHTML = ` <div class="tarjeta__contenido"> <div class="tarjeta__cara tarjeta__cara--frontal">?</div> <div class="tarjeta__cara tarjeta__cara--trasera">${simbolo}</div> </div> `; tarjeta.addEventListener('click', voltearTarjeta); tablero.appendChild(tarjeta); }); } // Función para mezclar el arreglo con Fisher-Yates function mezclar(array) { let currentIndex = array.length, temporaryValue, randomIndex; while (currentIndex !== 0) { randomIndex = Math.floor(Math.random() * currentIndex); currentIndex -= 1; // Intercambia temporaryValue = array[currentIndex]; array[currentIndex] = array[randomIndex]; array[randomIndex] = temporaryValue; } return array; } // Función que maneja el clic en una tarjeta function voltearTarjeta() { if (bloqueoTablero) return; // Si estamos comparando, no hacer nada if (this === primerTarjeta) return; // No permitir hacer clic en la misma tarjeta this.classList.add('volteada'); if (!tarjetaVolteada) { // Primera tarjeta volteada tarjetaVolteada = true; primerTarjeta = this; return; } // Segunda tarjeta volteada segundaTarjeta = this; bloqueoTablero = true; comprobarPareja(); } // Comprueba si las tarjetas forman pareja function comprobarPareja() { const esPareja = primerTarjeta.dataset.simbolo === segundaTarjeta.dataset.simbolo; if (esPareja) { // Pareja correcta paresEncontrados++; deshabilitarTarjetas(); mensaje.textContent = `¡Bien hecho! Pares encontrados: ${paresEncontrados}`; if (paresEncontrados === simbolos.length) { mensaje.textContent = '🎉 ¡Felicidades! Has encontrado todos los pares. 🎉'; } resetearEstado(); } else { // Pares incorrectos - volteamos después de un tiempo setTimeout(() => { primerTarjeta.classList.remove('volteada'); segundaTarjeta.classList.remove('volteada'); resetearEstado(); }, 1000); } } // Quitar evento de las tarjetas que ya forman pareja function deshabilitarTarjetas() { primerTarjeta.removeEventListener('click', voltearTarjeta); segundaTarjeta.removeEventListener('click', voltearTarjeta); } // Reinicia variables para continuar function resetearEstado() { [tarjetaVolteada, bloqueoTablero] = [false, false]; [primerTarjeta, segundaTarjeta] = [null, null]; } reiniciarBtn.addEventListener('click', iniciarJuego); // Comenzar juego la primera vez iniciarJuego(); }); |
Explicación paso a paso
- Definimos un array con símbolos, duplicamos para tener pares.
- Mezclamos los símbolos al azar con el algoritmo Fisher-Yates para que la distribución sea variable.
- Creamos las tarjetas dinámicamente y las colocamos en el tablero.
- Al hacer clic en una tarjeta:
- Se voltea la tarjeta mostrando el símbolo.
- Cuando se voltean dos tarjetas, se comparan sus símbolos.
- Si son iguales, se deshabilitan (no pueden voltearse otra vez).
- Si no, se vuelven a ocultar después de 1 segundo.
- Se controla el estado con variables para evitar clics durante la comprobación.
- Se muestra un mensaje de progreso y victoria.
- El botón de reiniciar comienza un nuevo juego con las tarjetas mezcladas nuevamente.
4. Mejoras y consejos finales
Buenas prácticas para tu proyecto JavaScript para principiantes:
- Separar responsabilidades: Mantén cada función clara y corta, como vimos con
voltearTarjeta
,comprobarPareja
,mezclar
. - Comentarios y legibilidad: Usa comentarios para ayudar a entender el código, especialmente para quien está aprendiendo.
- Uso de clases y atributos: Aquí usamos clases CSS y
data-
para asociar información útil. - Control de estados: Las variables booleanas evitan errores lógicos y clics extra.
- Accesibilidad: Puedes mejorar agregando roles ARIA o etiquetas para lectores de pantalla.
¿Qué puedes agregar después?
- Contador de movimientos o tiempo para hacerlo más desafiante.
- Diferentes niveles de dificultad cambiando el número de pares.
- Guardar el mejor puntaje en localStorage.
Conclusión
¡Felicidades! Has aprendido a crear un tutorial juego memoria JavaScript completo y funcional desde cero. Esta base te ayudará a seguir explorando proyectos interactivos y a consolidar tus habilidades.
No dudes en personalizar el diseño, los símbolos, o añadir funcionalidades nuevas para hacer el juego tuyo.
¿Quieres seguir practicando? Intenta aumentar la dificultad o incorporar efectos sonoros y verás cómo avanzas en tu camino como desarrollador.
¡Empieza ahora a crear juego memoria web y disfruta programando!
Recuerda: La clave para dominar es practicar paso a paso y aprender de cada proyecto.
Si te gustó este tutorial, compártelo y suscríbete para más contenido para desarrolladores.