36

Crea tu propio Duck Hunt usando solo tecnologías web

49072Puntos

hace 2 años

En un blogpost anterior, escribí acerca de una increíble herramienta para hacer videojuegos en JavaScript llamada Three.js, pero déjame decirte que no dependes de ella para crear videojuegos en el navegador.

Para mostrarte que esto es posible, aquí te enseñaré a crear uno de los videojuegos más famosos de Nintendo, tu propio Duck Hunt.

Si no lo conoces, te explico un poco como se juega. Es un videojuego en el cual tu objetivo es cazar a la mayor cantidad de patos posibles, pero conforme avances se tornará cada vez más difícil.

Abre tu Visual Studio Code y pongamos manos a la obra siguiendo estos pasos 👇🏻

1️⃣ Creando tu escena para Duck Hunt

En este repositorio podrás descargar las imágenes.

Necesitamos un escenario bonito en el cual sucederá todo el show, y esto lo haremos con solo HTML y CSS. Comienza creando tu archivo indes.html con el siguiente código:

<htmllang="en"><head><metacharset="UTF-8" /><metahttp-equiv="X-UA-Compatible"content="IE=edge" /><metaname="viewport"content="width=device-width, initial-scale=1.0" /><title>Duck huntertitle><linkrel="stylesheet"href="./src/styles/main.css" />head><body><divid="scene"><divid="scene--information"><buttononclick="startGame(event)">Startbutton><divclass="container"><pid="bullets">Bulletsp><p>shotp>div><divclass="container"><divclass="scene--ducks_remaining"><span>Hitspan><ul><li><imgsrc="./duck_small.svg"class="remaining_duck white-duck"/>li><li><imgsrc="./duck_small.svg"class="remaining_duck white-duck"/>li><li><imgsrc="./duck_small.svg"class="remaining_duck white-duck"/>li><li><imgsrc="./duck_small.svg"class="remaining_duck white-duck"/>li><li><imgsrc="./duck_small.svg"class="remaining_duck white-duck"/>li><li><imgsrc="./duck_small.svg"class="remaining_duck white-duck"/>li><li><imgsrc="./duck_small.svg"class="remaining_duck white-duck"/>li><li><imgsrc="./duck_small.svg"class="remaining_duck white-duck"/>li><li><imgsrc="./duck_small.svg"class="remaining_duck white-duck"/>li><li><imgsrc="./duck_small.svg"class="remaining_duck white-duck"/>li>ul>div>div><divclass="container"><pid="points">0p><p>scorep>div>div>div>body>html>

Ya tenemos todo lo necesario del lado del HTML, pero esto no tiene forma 😵, pasemos a los estilos con CSS.

/* styles.css *//* Normalizamos los estilos para que nada se rompa */
* {
  padding: 0;
  margin: 0;
  box-sizing: border-box;
}

body {
  font-family: sans-serif;
}

ul {
  list-style: none;
}

/* Es importante que nuestra escena tenga ese position y dicho background que te dejé arriba */#scene {
  position: relative;
  width: 100%;
  height: 100vh;
  background: url("./bg-duck-hunter.png") no-repeat;
  background-size: cover;
  background-position: bottom;
  cursor: crosshair;
}

/* Aunque nuestro pato aún no sale a escena vamos a dejar los estilos hechos */.duck {
  width: 90px;
  height: 90px;
  object-fit: contain;
  position: absolute;
  left: 20px;
}

.scene--information {
  display: flex;
  width: 90%;
  margin: 10px auto;
  justify-content: center;
  position: absolute;
  bottom: 0;
}

.scene--information.container {
  padding: 5px;
  border: 3px solid #93c43d;
  margin: 020px;
  background-color: black;
  color: white;
  font-size: 26px;
  border-radius: 10px;
  text-transform: uppercase;
}

/* Cambiar los colores de un svg es un tanto complicado, estás 2 clases te ayudarán *//* A cambiarlo de blanco a rojo y viceversa */.white-duck {
  filter: invert(100%) sepia(23%) saturate(0%) hue-rotate(122deg) brightness(
      110%
    ) contrast(101%);
}

.red-duck {
  filter: invert(30%) sepia(66%) saturate(7290%) hue-rotate(351deg) brightness(
      86%
    ) contrast(141%);
}

.scene--ducks_remaining {
  display: flex;
}

.scene--ducks_remainingul {
  display: flex;
}

.scene--ducks_remainingulli {
  margin: 05px;
  height: 30px;
  width: 30px;
}

Si juntamos ambas partes vamos a tener como resultado lo siguiente 👇🏻.

https://static.platzi.com/media/user_upload/Screenshot%202022-07-07%20at%2010-55-09%20Duck%20hunter-92d58aa5-8b61-4c3e-93ea-0ebafc0809e1.jpg

2️⃣ Poniendo al pato en escena

Aquí es donde JavaScript entra como protagonista para darle vida a nuestro videojuego.

// nodes.js//Dentro de este archivo guardaremos todos los elementos del dom para que nuestro código quede más limpioconst scene = document.querySelector("#scene");
const duck = document.querySelector("#duck");
const bulletsParagraph = document.querySelector("#bullets");
const points = document.querySelector("#points");
// En esta parta es importante usar querySelectorAll ya que// estaremos llamando elementos por clase y no por IDconst remainingDucks = document.querySelectorAll(".remaining_duck");
const width = scene.clientWidth;
const height = scene.clientHeight;

Ahora sí, patos a la obra 🦆

// index.js// Antes de crear a nuestro pato haremos un par// de funciones para que todo salga bien// En esta variable guardaremos el número de patos// que hemos cazadolet ducksHunted = 0;
// y hacemos lo mismo para los points y bulletslet bullets = 3;
let currentPoints = 0;

// Nuestro pato aparecerá en lugares aleatorios// por lo que ocuparemos la siguiente funciónfunctionsetVerticalPosition(duck) {
  const max = height - duck.height * 1.5;
  const min = duck.height * 1.5;

  // de esta manera evitamos que el alto sea mayor// al de nuestra escena y así no se sale
  duck.style.top = `${Math.floor(Math.random() * (max - min + 1) + min)}px`;
}

// La función hunted nos ayudará a sacar al pato// de escena cuando hagamos click sobre elfunctionhunted(event) {
  // Primero verificamos que tengamos balasif (bullets >0) {
    const target = event.target;

    // por cada pato que cazemos tendremos 100 puntos// más en el contador por lo que es necesario// aumentarlo
    points.innerText = currentPoints += 100;
    // Después simplemente eliminamos al pato de la escena
    target.remove();

    // dependiendo el número de patos cazados serán// los patos que se pintarán en la sección inferior
    remainingDucks[ducksHunted].classList.remove("white-duck");
    remainingDucks[ducksHunted].classList.add("red-duck");

    ducksHunted += 1;
    // La mecánica inicial del juego implica que cada// que acertemos un tiro tendremos de nuevo 3 bullets

    bullets = 3;

    // Al final creamos una lógica que no permita// saber si debemos continuar creando patos o noif (ducksHunted === 10) {
      console.log("Pasaste de nivel");
    } else {
      setTimeout(() => {
        createDuck();
      }, 300);
    }
  }
}

// Los ids no servirán más adelante para eliminar// el movimiento de los patosfunctionrandomId() {
  // Con math.random generamos un id aleatorio para cada patoconst id = Math.random().toString(36).substr(2, 9);
  return id;
}

functioncreateDuck() {
  // Ahora juntamos todo esto para crear nuestro patoconst duck = document.createElement("img");
  duck.src = "./src/images/duck_fly.png";
  duck.classList.add("duck");
  scene.appendChild(duck);
  setVerticalPosition(duck);
  duck.addEventListener("click", hunted);
  duck.id = randomId();
}

// y creamos la función de startgamefunctionstartGame() {
  createDuck();
}

Agregamos la etiqueta de script con todo el código y tendremos el siguiente resultado 👇🏻

https://i.imgur.com/FDgSOwf.gif

3️⃣ Haciendo que nuestro pato se mueva

Para agregarle ese grado de dificultad, ¿qué te parece si le damos movimiento para que sea difícil el darle caza a los patos?

// Vamos a crear un array en el cual guardaremos todos los patos que se estén moviendo// ya que debemos de borrar su intervalo una vez estén cazadoslet ducksMoving = [];

// La velocidad será importante ya que después de cada nivel será más veloces los patoslet velocity = 40;

// Pero antes de hacer la función de movimiento, debemos de asegurarnos que cuando toque el borde// de la pantalla, el pato de la vuelta y regresefunctiontouch(duck) {
  switch (true) {
    case duck.x >= width - duck.width:
      duck.style.transform = "rotateY(180deg)";
      returntrue;
    case duck.x <= 0:
      duck.style.transform = "rotateY(0deg)";
      returntrue;
  }
}

functionmoveDuck(duck) {
  let spaceLateral = 0;
  // en caso de ser verdadero el movimiento se moverá a la derecha, de ser negativo// este se moverá a la izquierdalet movement = true;
  // Cada pato se va a mover en un intervalo definida por la velocidad// lo ponemos en un array para que en caso de tener más patos en escena sea posible// borrar su intervalo y tener problemas de memoria
  duckMoving.push({
    interval: setInterval(() => {
      if (touch(duck)) {
        movement = !movement;
      }
      if (movement) {
        spaceLateral += 10;
      } else {
        spaceLateral -= 10;
      }
      // La manera en la que se moverá es mediante los estilos
      duck.style.left = `${spaceLateral}px`;
    }, velocity),
    id: duck.id,
  });
}

Ahora juntamos esto con nuestra función de createDuck() y editamos a hunted() para que quede de la siguiente forma 👇🏻.

functioncreateDuck(){
  ...
  // Aquí solo baste con agregar la función de moveDuck al final de la función
  moveDuck(duck);
}

functionhunted(event){
  if (bullets >0) {
    ...
    // en esta función solo debemos agregar la lógica para borrar el intervalo
    clearInterval(
      ducksMoving.filter((elem) => elem.id === target.id)[0].interval
    );

    // al igual que borrarlo del array
    ducksMoving = ducksMoving.filter((el) => {
      return el.id !== target.id;
    });
    ...
  }
}

Con todo y el movimiento tenemos lo siguiente 👀

https://i.imgur.com/cx5K2iI.gif

4️⃣ Pasando de nivel

Ahora falta la cereza del pastel, ¿qué pasa cuando pasamos de nivel? La dificultad aumentará con más velocidad en los patos, por lo que debemos hacer una función que se ejecute cuando tengamos 10 patos cazados.

// vamos a crear un función super sencillafunctionnextLevel() {
  // los patos de la parte inferior ahora serán todos blancos como al inicio
  remainingDucks.forEach((item) => {
    item.classList.remove("red-duck");
    item.classList.add("white-duck");
  });

  // Vamos a aumentar la velocidad de movimiento// pero debemos de bajar los milisegundos por eso// le restamos 5
  velocity -= 5;

  // y los patos cazados ahora son 0
  ducksHunted = 0;
}

¡Pero! ¡Hey!, nos hace falta indicarle esto a nuestro usuario.

¿Qué te parece si creamos un modal para indicarle eso?

functioncreateModal(text) {
  // creamos nuestro modal con un divconst modal = document.createElement("div");
  // y un contenedor para que se vea bien con los estilosconst container = document.createElement("div");
  const paragraph = document.createElement("p");
  // también un botón para cerrar el modalconst buttonClose = document.createElement("button");
  buttonClose.addEventListener("click", () => {
    modal.remove();
  });
  // le ponemos estilos
  modal.classList.add("modal");

  paragraph.innerText = text;
  buttonClose.innerText = "x";
  container.append(paragraph, buttonClose);
  modal.appendChild(container);
  // y lo desplegamos en el bodydocument.body.appendChild(modal);
}

Por último modificamos la función de hunted (una vez más).

functionhunted(event){
  ...
   if (ducksHunted === 10) {
      createModal("Pasaste de nivel");
      nextLevel();
    } else {
      setTimeout(() => {
        createDuck();
      }, 300);
    }
}

Y aquí está la obra maestra. 🧐

https://i.imgur.com/8vPOncV.gif

💻 El repo con el código completo, sin comentarios aquí mismo.

¿Qué más puedes añadir a tu juego de Duck Hunt?

¿Quieres seguir con este videojuego?

Hay muchas oportunidades de mejora, te dejo aquí todo lo que puedes mejorar:

  1. Mover al pato en diagonal
  2. Mejorar los estilos
  3. Mostrar las balas restantes
  4. Cada que atrapes un pato te despliegue los puntos
  5. Desplegar múltiples patos en pantalla
  6. Agregar una tabla de posiciones donde puedas ver tus puntuaciones
  7. Agregar una barra que te diga la dificultad actual
  8. Hacer que los patos salgan del medio hacia alguna dirección aleatoria

Solo por mencionar algunas, imagínate agregar un jefe final al pasar determinada cantidad de niveles 🔥

Si logras completar 1 o todas pon tu repositorio y tu demo live en los comentarios, estaremos muy contentos de jugar con tu propia versión de Duck Hunt.

También te invitamos a la comunidad de la Escuela de Videojuegos en Telegram para que nos compartas todas tus dudas y estés al tanto de todo lo nuevo que hay para desarrollo de videojuegos con JavaScript.

¿Quieres empezar tu camino desarrollando videojuegos? Empieza por aquí 👇🏻

Leonardo de los angeles
Leonardo de los angeles
LeoCode0

49072Puntos

hace 2 años

Todas sus entradas
Escribe tu comentario
+ 2
Ordenar por:
2
22Puntos

Super genial, Gracias

2
19269Puntos

Está brutal el ejercicio para sumar algo en el protafolio

2
15115Puntos

De verdad que todos estos blogs son una absoluta locura, muchas thanks

2
33399Puntos

Genial Leo!! 💥🦆

1
235Puntos

Busca un buen casino en línea, pero no puedes encontrar nada y no sabes qué hacer? En este caso, le recomiendo encarecidamente que intente llamar su atención aquí en este sitio https://www.bets.io/es/category/jackpot , ya que aquí es donde definitivamente puede encontrar la mejor solución. Estoy seguro de que estará satisfecho.