Tutorial juego memoria: Crea un Memory Match con HTML, CSS y JavaScript paso a paso
¿Quieres aprender a crear un juego interactivo clásico y divertido? En este tutorial juego memoria para desarrolladores principiantes, te guiaré paso a paso para desarrollar un Memory Match usando HTML, CSS y JavaScript. Aprenderás a estructurar correctamente tu proyecto, diseñar una interfaz atractiva y responsiva, y programar la lógica para que tu juego funcione perfectamente.
Este tutorial es ideal si buscas adentrarte en el desarrollo frontend, especialmente en el desarrollo web JavaScript para crear juegos simples y educativos. ¡Comencemos!
Índice
- Estructura básica con HTML
- Diseño visual con CSS responsive
- Lógica del juego con JavaScript
- Conclusión y buenas prácticas
Estructura básica con HTML
Primero, crearemos la estructura básica de nuestro juego de memoria. Esto consistirá en el contenedor principal, un área para mostrar las cartas, un contador de movimientos 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 21 22 23 24 25 26 27 28 |
<!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 - Memory Match</title> <!-- Enlazamos hoja de estilos --> <link rel="stylesheet" href="styles.css" /> </head> <body> <h1>Juego de Memoria - Memory Match</h1> <!-- Contador de movimientos --> <div id="movements">Movimientos: 0</div> <!-- Contenedor de cartas --> <div class="game-container" id="gameContainer"> <!-- Aquí se insertarán las cartas con JavaScript --> </div> <!-- Botón para reiniciar juego --> <button id="restartBtn">Reiniciar Juego</button> <!-- Vinculamos el archivo JavaScript --> <script src="script.js"></script> </body> </html> |
Explicación
- El contenedor principal con la clase
.game-container
actuará como el tablero donde aparecerán las cartas. - El elemento
#movements
mostrará en pantalla cuántos movimientos se han hecho. - El botón
#restartBtn
permitirá reiniciar el juego. - El archivo
styles.css
contendrá el diseño, yscript.js
la lógica del juego.
Diseño visual con CSS responsive
Ahora que tenemos nuestra estructura, vamos a darle estilo para que el juego sea atractivo y adaptable a diferentes dispositivos.
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 |
/* styles.css */ /* Reset de márgenes y fuente base */ * { box-sizing: border-box; margin: 0; padding: 0; font-family: 'Segoe UI', Tahoma, Geneva, Verdana, sans-serif; } body { background-color: #282c34; color: #61dafb; display: flex; flex-direction: column; align-items: center; min-height: 100vh; padding: 2rem; } h1 { margin-bottom: 1.5rem; } #movements { font-size: 1.2rem; margin-bottom: 1rem; } .game-container { display: grid; grid-template-columns: repeat(4, 100px); grid-template-rows: repeat(3, 100px); gap: 15px; justify-content: center; margin-bottom: 1.5rem; } /* Estilo para cada carta */ .card { perspective: 600px; /* Para efecto 3D al voltear */ } .card-inner { position: relative; width: 100px; height: 100px; cursor: pointer; transform-style: preserve-3d; transition: transform 0.5s; border-radius: 10px; box-shadow: 0 4px 8px rgba(0,0,0,0.3); } /* Cuando está volteada */ .card.flipped .card-inner { transform: rotateY(180deg); } .card-front, .card-back { position: absolute; width: 100%; height: 100%; border-radius: 10px; backface-visibility: hidden; display: flex; justify-content: center; align-items: center; font-size: 3rem; } .card-front { background-color: #61dafb; color: #282c34; } .card-back { background-color: #20232a; transform: rotateY(180deg); color: #61dafb; } #restartBtn { padding: 10px 20px; font-size: 1rem; background-color: #61dafb; border: none; border-radius: 5px; color: #282c34; cursor: pointer; transition: background-color 0.3s; } #restartBtn:hover { background-color: #21a1f1; } /* Responsive para pantallas pequeñas */ @media (max-width: 480px) { .game-container { grid-template-columns: repeat(3, 80px); grid-template-rows: repeat(4, 80px); gap: 10px; } .card-inner { width: 80px; height: 80px; } .card-front, .card-back { font-size: 2rem; } } |
Explicación
- Se emplea un diseño con Grid CSS para colocar nuestras cartas en filas y columnas.
- Cada carta tiene un efecto de voltear 3D usando la propiedad
transform
yperspective
. - Se define un estilo para las caras frontales y traseras de las cartas.
- El botón
#restartBtn
tiene estilos para hacerlo visible y atractivo. - Se añade una media query para que el juego sea responsive y funcione bien en móviles.
Lógica del juego con JavaScript
Llegó la hora de programar la lógica, controlar la selección de cartas, comprobar coincidencias, contar movimientos y reiniciar el juego.
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 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 |
// script.js // Listado de iconos o símbolos para las cartas. Usamos emojis para simplicidad. const symbols = ["🐶", "🐱", "🦊", "🐸", "🐵", "🐰"]; // Variables globales para el estado del juego let cards = []; let flippedCards = []; let matchedCount = 0; let movements = 0; // Referencias a elementos DOM const gameContainer = document.getElementById('gameContainer'); const movementsDisplay = document.getElementById('movements'); const restartBtn = document.getElementById('restartBtn'); /** * Inicializa el juego: crea y asigna las cartas al tablero. */ function initGame() { // Reiniciamos estado cards = []; flippedCards = []; matchedCount = 0; movements = 0; movementsDisplay.textContent = `Movimientos: ${movements}`; gameContainer.innerHTML = ''; // Duplicamos y mezclamos los símbolos para tener pares const doubleSymbols = [...symbols, ...symbols]; shuffleArray(doubleSymbols); // Creamos carta para cada símbolo mezclado doubleSymbols.forEach((symbol, index) => { const card = createCard(symbol, index); gameContainer.appendChild(card); cards.push(card); }); } /** * Crea un elemento carta con el símbolo y estructura HTML necesaria. * @param {string} symbol - Símbolo de la carta (emoji). * @param {number} id - Identificador único. * @returns {HTMLElement} - Elemento carta configurado. */ function createCard(symbol, id) { // Contenedor principal const card = document.createElement('div'); card.classList.add('card'); card.dataset.symbol = symbol; // Guardamos símbolo en atributo card.dataset.id = id; // Elemento interno para el flip const cardInner = document.createElement('div'); cardInner.classList.add('card-inner'); // Frente de la carta (contenido oculto hasta voltear) const cardFront = document.createElement('div'); cardFront.classList.add('card-front'); cardFront.textContent = symbol; // Parte trasera const cardBack = document.createElement('div'); cardBack.classList.add('card-back'); cardBack.textContent = '❓'; // Estructura cardInner.appendChild(cardFront); cardInner.appendChild(cardBack); card.appendChild(cardInner); // Evento click para voltear card.addEventListener('click', onCardClick); return card; } /** * Evento que maneja el clic en una carta. * @param {Event} event */ function onCardClick(event) { const clickedCard = event.currentTarget; // Si la carta ya está volteada o ya emparejada, ignoramos clic if ( clickedCard.classList.contains('flipped') || flippedCards.length === 2 ) { return; } // Volteamos la carta clickedCard.classList.add('flipped'); flippedCards.push(clickedCard); if (flippedCards.length === 2) { // Contamos movimiento movements++; movementsDisplay.textContent = `Movimientos: ${movements}`; // Comprobamos si coinciden checkForMatch(); } } /** * Compara las dos cartas volteadas y determina si son iguales. * Si coinciden, las dejamos volteadas; si no, se voltean de nuevo. */ function checkForMatch() { const [cardOne, cardTwo] = flippedCards; if (cardOne.dataset.symbol === cardTwo.dataset.symbol) { // Coincidencia correcta matchedCount += 2; // Removemos listeners para evitar volver a seleccionar cardOne.removeEventListener('click', onCardClick); cardTwo.removeEventListener('click', onCardClick); flippedCards = []; // Verificamos si finalizó el juego if (matchedCount === cards.length) { setTimeout(() => { alert(`¡Felicidades! Completaste el juego en ${movements} movimientos.`); }, 500); } } else { // No coinciden - volteamos nuevamente tras breve retraso setTimeout(() => { cardOne.classList.remove('flipped'); cardTwo.classList.remove('flipped'); flippedCards = []; }, 1000); } } /** * Función para mezclar un array (algoritmo Fisher-Yates) * @param {Array} array */ function shuffleArray(array) { for (let i = array.length - 1; i > 0; i--) { const j = Math.floor(Math.random() * (i + 1)); [array[i], array[j]] = [array[j], array[i]]; } } // Reiniciar el juego al hacer click en botón restartBtn.addEventListener('click', initGame); // Inicializamos el juego la primera vez initGame(); |
Explicación detallada
- Declaraciones y estado:
- Definimos los símbolos a usar (emojis) y variables para controlar cartas, movimientos y emparejamientos.
- Creación del tablero:
- La función
initGame()
restablece el juego, mezcla las cartas y las agrega al DOM.
- Estructura de cada carta:
- Cada carta tiene estructura HTML con dos lados (
card-front
ycard-back
) para dar el efecto de volteo mediante CSS.
- Manejador de clics:
- Cuando se hace clic en una carta, sola puede voltearse si no hay más de 2 cartas volteadas y si no está emparejada.
- Se almacenan las cartas volteadas y se chequea si coinciden después de dos selecciones.
- Comprobación de coincidencias:
- Si los símbolos coinciden, las cartas permanecen volteadas y sin escuchar clics.
- Si no coinciden, se voltean nuevamente tras 1 segundo.
- Conteo de movimientos:
- Cada par de selección cuenta como un movimiento, reflejado en pantalla.
- Final del juego:
- Al encontrar todas las coincidencias, se muestra un mensaje felicitando al jugador.
- Reinicio:
- El botón de reinicio vuelve a llamar a
initGame()
para comenzar desde cero.
Conclusión y buenas prácticas
¡Felicidades! Acabas de crear un juego de memoria completo y funcional utilizando solo HTML, CSS y JavaScript.
Resumen
- Estructuraste correctamente el HTML para mostrar elementos esenciales como el tablero y contador.
- Diseñaste un estilo visual con CSS moderno, utilizando Grid y efectos de rotación 3D.
- Programaste la lógica frontend para gestionar cartas, comparar selecciones, contar movimientos y reiniciar.
Buenas prácticas que aplicamos
- Separación clara entre estructura (HTML), estilos (CSS) y lógica (JS).
- Comentarios detallados para entender cada bloque de código.
- Uso de eventos y manipulación DOM de manera eficiente.
- Código modular y legible para facilitar futuras mejoras.
- Implementación responsive para móviles.
Ideas para seguir mejorando
- Añadir temporizador para medir tiempo jugado.
- Incluir niveles de dificultad con más cartas.
- Agregar animaciones y sonidos para mejor experiencia.
- Guardar puntajes usando localStorage para juego persistente.
Si este tutorial te fue útil y quieres aprender más sobre desarrollo web JavaScript o crear juegos interactivos con HTML CSS, te invito a seguir explorando y practicar.
¡Empieza a programar y diviértete creando!
¿Quieres más tutoriales como este? Suscríbete a nuestro blog y recibe contenido actualizado para perfeccionar tus habilidades en programación frontend y desarrollo web.