Inicio Apuntes FPApuntes DAW Juego de la serpiente, crea el tuyo con un HTML, CSS y JavaScript

Juego de la serpiente, crea el tuyo con un HTML, CSS y JavaScript

Juego desde el escritorio o desde el teléfono móvil

Publicado por entreunosyceros

Una vez más aquí. Hoy vengo a dejar un pequeño artículo sobre algo que me pidió un usuario la semana pasada. Este usuario debe de estar estudiando JavaScript, por que me pidió un articulo sobre cómo crear el mítico juego de la serpiente con JavaScript.

Este juego creo que es algo típico que se pide a todos los estudiantes de JavaScript, por que en su día a mi también me lo pidieron. Busqué un poco por mis archivos, y me encontré una versión que actualicé un poco, para que se pueda jugar también desde dispositivos móviles.

¿Qué es el juego de la serpiente?

Yo me imagino que en el mundo queda poca gente que no conozca este juego, pero por qué no aclarar que el juego de la serpiente (también conocido como Snake en inglés) es un juego clásico que se originó en la década de 1970. El objetivo del juego es controlar una serpiente que se mueve por una pantalla y hacerla comer manzanas o cualquier cosa que el programador de turno quiera, para hacerla crecer.

el juego de la serpiente creado con JavaScript

La serpiente se mueve continuamente en una dirección y el jugador puede cambiar su dirección para evitar chocar contra las paredes o la propia serpiente. Cada vez que la serpiente come una fruta, su cuerpo se alarga y el jugador debe evitar que la cabeza de la serpiente choque con su propio cuerpo.

Y bueno, teniendo esto claro, podemos pasar a ver el código para crear este juego en nuestro equipo.

El código del juego

Para crear este sencillo juego, solo vamos a necesitar tres archivos, y un cuarto que será una imagen de la comida de la serpiente. El primero de los archivos será el index.html, que nos servirá para mostrar el juego. El segundo será el style.css, donde aplicaremos las reglas CSS necesarias para que todo se vea de forma correcta. Y el tercero lo llamaremos script.js, y será en el que añadiremos la lógica del juego.

El código que vamos a ver, es un juego de serpiente que utiliza HTML, CSS y JavaScript. El código crea una cuadrícula de juego y una serpiente que se mueve por la cuadrícula. El jugador controla la dirección de la serpiente utilizando las teclas de flecha, y el objetivo es comer la comida que aparece aleatoriamente en la cuadrícula, sin chocar con los bordes de la cuadrícula o con el cuerpo de la serpiente.

El archivo index.html

En este archivo vamos a añadir el código básico de una página HTML que tiene como objetivo mostrar una página web para el juego, al que llamaré «La serpientica«.

<!DOCTYPE html>
<html lang="es">
  <head>
    <meta charset="utf-8"/>
    <meta name="viewport" content="width=device-width, initial-scale=1.0"/>
    <title>La serpientica</title>
    <link rel="stylesheet" href="style.css"/>  
    <link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/font-awesome/6.3.0/css/all.min.css"/>  
  </head>
  <body>
    <div class="wrapper">
      <div class="titulo">LA SERPIENTICA</div>
      <div class="game-details">
        <span class="score">Puntuación: 0</span>
        <span class="high-score">Puntuación más alta: 0</span>
      </div>
      <div class="play-board"></div>
      <div class="controls">
        <i data-key="ArrowLeft" class="fa-solid fa-arrow-left-long"></i>
        <i data-key="ArrowUp" class="fa-solid fa-arrow-up-long"></i>
        <i data-key="ArrowRight" class="fa-solid fa-arrow-right-long"></i>
        <i data-key="ArrowDown" class="fa-solid fa-arrow-down-long"></i>
      </div>
    </div>
    <script src="script.js" defer></script>
  </body>
</html>

Este código no tiene mucho que explicar. Lo único sería las etiquetas «<i>«, que son las que vamos a utilizar para añadir los iconos para los controles de movimientos que se mostrarán en la versión móvil. El resto es la típica página html.

Archivo style.css

El código que vamos a añadir a este archivo, son las reglas CSS que se utilizarán para definir los estilos visuales de este juego.

*,
*::before,
*::after {
  margin: 0;
  padding: 0;
  box-sizing: border-box;
}

body {
  min-height: 100vh;
  display: flex;
  align-items: center;
  justify-content: center;
  background-color: #410077;
  font-family: 'Open Sans', sans-serif;
}

.wrapper {
  width: 65vmin;
  height: 70vmin;
  display: flex;
  flex-direction: column;
  justify-content: center;
  border-radius: 5px;
  background-color: #414c5fe8;
  box-shadow: 0 0 0 4px #f00, inset 2px 2px rgba(136, 0, 0, 0.26), 3px 4px rgba(0, 0, 0, 0.144);
  overflow: hidden;
}
.game-details {
  color: #B8C6DC;
  font-weight: 500;
  font-size: 1.2rem;
  padding: 20px 27px;
  display: flex;
  justify-content: space-between;
}
.play-board {
  height: 100%;
  width: 100%;
  display: grid;
  background: #212837;
  grid-template: repeat(30, 1fr) / repeat(30, 1fr);
}
.play-board .food {
  background-repeat: no-repeat;
  background-image: url("./img/Manzana.png");
  background-size: 15px;
}
.play-board .head {
  background: #25df00;
  border-radius: 20%;
}

.controls {
  display: none;
  justify-content: space-between;
}
.controls i {
  padding: 25px 0;
  text-align: center;
  font-size: 1.3rem;
  color: #B8C6DC;
  width: calc(100% / 4);
  cursor: pointer;
  border-right: 1px solid #171B26;
}

/*Título*/
.titulo {
  display: inline-block;
  -webkit-box-sizing: border-box;
  -moz-box-sizing: border-box;
  box-sizing: border-box;
  padding: 10px;
  border: none;
  font: normal 38px / normal "Warnes", Helvetica, sans-serif;
  color: rgba(255, 255, 255, 1);
  text-decoration: normal;
  text-align: center;
  -o-text-overflow: clip;
  text-overflow: clip;
  white-space: pre;
  text-shadow: 0 0 10px rgb(255, 0, 0), 0 0 20px rgba(255, 0, 0),
    0 0 30px rgba(255, 0, 0), 0 0 40px #727272, 0 0 70px #727272,
    0 0 80px #4e4e4e, 0 0 100px #2b2b2b;
  -webkit-transition: all 200ms cubic-bezier(0.42, 0, 0.58, 1);
  -moz-transition: all 200ms cubic-bezier(0.42, 0, 0.58, 1);
  -o-transition: all 200ms cubic-bezier(0.42, 0, 0.58, 1);
  transition: all 200ms cubic-bezier(0.42, 0, 0.58, 1);
  width: 100%;
  margin-top: 10px;
}
.titulo:hover {
  text-shadow: 0 0 10px rgb(184, 0, 0), 0 0 20px rgb(184, 0, 0),
    0 0 30px rgb(236, 0, 0), 0 0 40px #b80707, 0 0 70px #ff0000,
    0 0 80px #bd0000, 0 0 100px #b41212;
}

@media screen and (max-width: 800px) {
  body{
    min-height: 80vh;
  }
  .wrapper {
    width: 85vmin;
    height: 115vmin;
  }
  .game-details {
    font-size: 1rem;
    padding: 15px 27px;
  }
  .controls {
    display: flex;
  }
  .controls i {
    padding: 15px 0;
    font-size: 1rem;
  }
  .game-details{
    font-size: 0.8em;
  }
}

@media screen and (max-width: 530px) {
  .play-board {
    height: 68%;
  }
  .play-board .food {
    background-size: 9px;
  }
  .wrapper {
    width: 95vmin;
  }
}

A continuación, vamos a describir brevemente lo que cada sección de código hace:

  • La primera sección establece los valores predeterminados de margen, relleno y caja para todos los elementos de la página, lo que asegura que no habrá margen o relleno no deseado que afecte la apariencia de los elementos.
  • La sección de «body» establece el estilo para el cuerpo de la página, como el color de fondo, la fuente y la alineación de contenido.
  • La sección de «wrapper» define el estilo para un contenedor específico utilizado en la página, incluyendo su tamaño, forma, color de fondo, sombra y otros aspectos visuales.
  • Las secciones de «game-details» y «play-board» establecen los estilos para las secciones específicas de la página que se utilizan en el juego, como la información del juego y el tablero de juego.
  • La sección «controls» define el estilo para los controles de juego, como las flechas que se usan para controlar la dirección de la serpiente.
  • La sección «titulo» establece el estilo para el título de la página.
juego snake en móvil
  • Finalmente, hay algunas secciones que utilizan «media queries» para cambiar los estilos en función del tamaño de la pantalla del dispositivo que se está utilizando para ver la página, lo que ayuda a asegurar que la página se vea bien en una amplia variedad de dispositivos y tamaños de pantalla.

Archivo Script.js

El código que vamos a guardar dentro de este archivo, en términos generales, comienza definiendo algunas variables y elementos de la página, como el tablero de juego, la puntuación y la posición de la comida. También establece algunas funciones para actualizar la posición de la comida, cambiar la dirección de la serpiente y finalizar el juego.

Después de definir las funciones, el código establece un intervalo de tiempo para actualizar el estado del juego y llamar a la función «iniciarJuego«. Dentro de esta función, se actualiza la posición de la serpiente y se comprueba si ha colisionado con la pared o con su propio cuerpo. Si la serpiente come la comida, se actualiza la puntuación y se genera una nueva posición para la comida.

const playBoard = document.getElementsByClassName("play-board")[0];
const scoreElement = document.getElementsByClassName("score")[0];
const highScoreElement = document.getElementsByClassName("high-score")[0];
const controls = document.querySelectorAll(".controls i");

let gameOver = false;
let foodX, foodY;
let snakeX = 5, snakeY = 5;
let velocityX = 0, velocityY = 0;
let snakeBody = [];
let setIntervalId;
let score = 0;

// Se busca la puntuación más alta del almacenamiento local
let highScore = localStorage.getItem("high-score") || 0;
highScoreElement.innerText = `Puntuación más alta: ${highScore} puntos`;

// Actualizamos la posición de la comida de forma aleatoria
const actualizarPosicionComida = () => {
    foodX = Math.floor(Math.random() * 30) + 1;
    foodY = Math.floor(Math.random() * 30) + 1;
};

// Final del juego
const finJuego = () => {
    clearInterval(setIntervalId);
    alert("Game Over! Pulsa Aceptar para volver a jugar...");
    location.reload();
};

// Cambiar la dirección de la serpiente
const cambiarDirection = e => {
    switch (e.key) {
        case "ArrowUp":
            if (velocityY !== 1) {
                velocityX = 0;
                velocityY = -1;
            }
            break;
        case "ArrowDown":
            if (velocityY !== -1) {
                velocityX = 0;
                velocityY = 1;
            }
            break;
        case "ArrowLeft":
            if (velocityX !== 1) {
                velocityX = -1;
                velocityY = 0;
            }
            break;
        case "ArrowRight":
            if (velocityX !== -1) {
                velocityX = 1;
                velocityY = 0;
            }
            break;
        default:
            break;
    }
};

// Llamar a cambiarDirection en cada clic y pasar el valor del conjunto de datos clave como un objeto
controls.forEach(button => button.addEventListener("click", () => cambiarDirection({ key: button.dataset.key })));

const iniciarJuego = () => {
    if (gameOver) return finJuego();
    let html = `<div class="food" style="grid-area: ${foodY} / ${foodX}"></div>`;

    // Comprobamos si la serpiente se come la comida.
    if (snakeX === foodX && snakeY === foodY) {
        actualizarPosicionComida();
        snakeBody.push([foodY, foodX]); // Empujamos la comida a la matriz del cuerpo de la serpiente, para que la serpiente crezca
        score++; // Se incrementa la puntuación en 1
        // Guardamos las puntuaciones
        highScore = score >= highScore ? score : highScore;
        localStorage.setItem("high-score", highScore);
        scoreElement.innerText = `Puntuación: ${score} puntos`;
        highScoreElement.innerText = `Puntuación más alta: ${highScore} puntos`;
    }
    // Actualización de la posición de la cabeza de la serpiente en función de la velocidad actual
    snakeX += velocityX;
    snakeY += velocityY;

    // Desplazando hacia adelante los valores de los elementos en el cuerpo de la serpiente en uno
    for (let i = snakeBody.length - 1; i > 0; i--) {
        snakeBody[i] = snakeBody[i - 1];
    }
    snakeBody[0] = [snakeX, snakeY]; // Configuración del primer elemento del cuerpo de la serpiente en la posición actual de la serpiente

    // Comprobando si la cabeza de la serpiente está fuera de la pared, si es así se configura gameOver en verdadero para finalizar el juego
    if (snakeX <= 0 || snakeX > 30 || snakeY <= 0 || snakeY > 30) {
        return gameOver = true;
    }

    for (let i = 0; i < snakeBody.length; i++) {
        // Se dibuja un div para cada parte del cuerpo de la serpiente.
        html += `<div class="head" style="grid-area: ${snakeBody[i][1]} / ${snakeBody[i][0]}"></div>`;
        // Comprobamos si la cabeza de la serpiente golpeó el cuerpo, si es así, establezca gameOver en verdadero
        if (i !== 0 && snakeBody[0][1] === snakeBody[i][1] && snakeBody[0][0] === snakeBody[i][0]) {
            gameOver = true;
        }
    }
    playBoard.innerHTML = html;
}
// Llamar a la actualización de la posición de la comida
actualizarPosicionComida();
setIntervalId = setInterval(iniciarJuego, 100);
document.addEventListener("keyup", cambiarDirection);

En resumen, este código va a crear un juego de serpiente básico en el que el jugador debe evitar chocar con obstáculos mientras intenta comer la comida para aumentar su puntuación.

Si quieres probar este juego antes de copiarlo a tu disco local, puedes hacerlo desde la sección de ejemplos en este enlace. En caso de que quieras descargarlo, sin tener que copiar todo este código, puedes hacerlo desde el repositorio en GitHub donde lo he alojado. Además, desde ahí podrás descargar también la imagen de la comida de la serpiente.

Y bueno, con esto espero que todo el que necesite un juego de este estilo, pueda crearlo sin mayor problema.

También te puede interesar ...

Deja un comentario

* Al utilizar este formulario, aceptas que este sitio web almacene y maneje tus datos.

2 Comentarios

Alguien 29 agosto, 2024 - 4:01 PM

A mi me funciona el movimiento y todo, pero no las manzanas, practicamente, la comida…

Respuesta
entreunosyceros 30 agosto, 2024 - 6:21 PM

Hola. Si lo que no te funciona es la comida, eso o bien tienes algún problema con la función Math o random, o es que la función // Actualizamos la posición de la comida de forma aleatoria
const actualizarPosicionComida = () => {
foodX = Math.floor(Math.random() * 30) + 1;
foodY = Math.floor(Math.random() * 30) + 1;
};
no la has copiado correctamente. Por que como se puede ver en el enlace de ejemplo, tal cual aparece en el post, funciona correctamente. Ten cuidado con los espacios o los ; del final de cada línea. Salu2.

Respuesta

Adblock Detectado!!

Ayúdanos deshabilitando la extensión AdBlocker de tu navegador para visitar esta web.
Si no sabes hacerlo en Chrome, consulta el siguiente enlace. Si utilizas Firefox, puedes consultar este otro enlace.
Esto mejorará tu experiencia en este sitio web.