2

Crear un busca minas, usando el método "requesAnimationFrame"

Angel
Angrub
27647

Ahora que has acabado el curso de programación básica, querrás probar tus conocimientos con algo mas desafiante y ¡divertido! ¿Y por qué no hacerlo con tu primer videojuego con javascript? En éste pequeño tutorial aprenderás el funcionamiento mas elemental de un motor de videojuegos e intentare ayudarte a crear tu propia versión del busca minas; un juego simple, pero que de seguro desafiara tus habilidades como programador.

IMPORTANTE: Solo mostrare algunas partes del código para ejemplificar cosas muy especificas, el punto de esto es que tu encuentres la solución a los diversos problemas que te planteare, de todos modos dejare el link del repositorio al final del tutorial. 😉

                                                                                            **¡Sin mas preámbulo empecemos! **

Primero, no te preocupes, el único concepto nuevo que usaremos es el método (o función) requestAnimationFrame(), éste sera una pieza fundamental en nuestro bucle de juego. Pero ¡¿A que me refiero con bucle de juego?!
Una manera simple de entender que es un videojuego es así, imagina un bucle, como el bucle While o el for, pero a diferencia de ellos éste se ejecutara 60 veces por segundo; o 30. En el lapso de ejecución detectara colisiones, registrara Input(entradas de teclado o ratón) y animara los elementos del juego. A este bucle también podríamos llamarle motor.

¡Espera!.. dije animar? ¡pues claro! Una animación es simplemente una sucesión de fotogramas, cuadros o imágenes, como quieras decirle es correcto. Esa sucesión sera mostrada por el Canvas; etiqueta html que ya aprendieron a usar en el curso.

Para no entrar en detalles, solo diré que crearemos una etiqueta <canvas> en tu documento HTML y por medio del método getElementById la traeremos a una constante, seguido de agregarle un contexto 2d. Una constante **const **es casi lo mismo a una variable var, la diferencia es que en la primera el valor agregado no podrá ser cambiado a lo largo del código; puedes usar var si quieres, esto no afectara al funcionamiento del código significativamente. Solo como recomendación intenta utilizar _let _ en ves de var, para evitar algún error.

const canvas = document.getElementById("Canvas");
const gContext = canvas.getContext("2d");

Bien, ya tienes tu canvas. El siguiente paso es crear el bucle de tu juego, esto lo haremos con una función recursiva (una función que se llama a si misma) yo la llamare frame. Y aquí entra el método requestAnimationFrame().

functionframe(){   
    			          /////////bucle de juego//
    requestAnimationFrame(frame);
}
frame();

Lo que sucede aquí es:

  1. Iniciara la función
  2. Ejecutara el código del bucle (colisiones, animación, etc)
  3. el método esperara 16 milisegundos y volverá a ejecutar la función frame
    Y así hasta el fin de los tiempos.

Ya tienes tu entorno de renderizado (canvas) y un bucle el cual ejecutara tu código casi 60 veces por segundo. Lo siguiente sera crear las funciones que animaran, detectaran colisiones y registraran los input. yo lo manejé de ésta manera:

<functionupdate(){
    ///input
}

functioncollisions(){
    //colisiones 
}
functiondraw(){
    //animar
}

functionframe(){   
    update();               //
    collisions();            ///////bucle de juego
    draw();                  //
    requestAnimationFrame(frame);
}

Tener cada cosa en una función, nos permitirá mantener el código de manera mas fácil; como cuando agreguemos funcionalidades nuevas.

A partir de ahora mostrare menos código, ya que me interesa que tu construyas tus propias soluciones, solo te guiare en la lógica del algoritmo. No te frustres, tu eres totalmente capaz de hacerlo, puedes ver el código fuente, pero sera mas enriquecedor si tu lo resuelves. !Animo¡

Lo primero sera la animación, para animar solo sera necesario dibujar y borrar. esto lo puedes hacer usando estos métodos.

gContext.clearRect(0,0,canvas.width,canvas.height); //para borrar
gContext.drawImage(image, posicionx, posiciony, ancho, alto); // para dibujar

Lo segundo es detectar colisiones. Mucha gente se rompe la cabeza con ésto, de manera simple: una colisión ocurre cuando una **entidad_1 ** esta entre las coordenadas **x ** y y, y el ancho y alto de la entidad_2. por ejemplo, tengo un cuadrado que mide 20 pixeles y esta ubicado en la coordenada 40,120, por lo que podemos decir que el área que abarca es: 40 a 60 en x y 120 a 140 en y, si las coordenadas de una entidad son iguales a alguna dentro del cuadrado; 45,139 por ejemplo, se puede decir que hay una colisión. La única colisión que habrá en el juego sera la del mouse y la de las celdas del juego.

El tercer paso es agregar eventos de click, esto es parte de los input. Como ya sabrás esto se hace agregando escuchadores:

canvas.addEventListener(evento, funcion_a_ejecutar())

un tip, puedes hacer esto para obtener las coordenadas del mouse:

canvas.addEventListener("mousemove",(coordinates)=>{  // ésta es una función flecha, es lo mismo que escribir esto: function  obtenerCoor(cordinates)mouse.x = coordinates.layerX;mouse.y = coordinates.layerY;
})

Bueno hasta aquí quedaría el tutorial, ahora en base a esto deberás construir:
1. la generación aleatoria de minas (te recomiendo que lo guardes en una array bidimensional)

functionloadMatriz(){
    for(let x=0; x<columns; x++){       		//  lo puedes hacer usando el método push y creando un arreglo dentro de otro
            matriz.push(newArray(rows));		//new Array crea una nueva array, quien lo diría no?for(let y=0; y<rows; y++){
                let bomb = random();			// un ejemplo de la generación de minas
                matriz[x][y] = new Cell(x*32, y*32, bomb); 	//Cell es un objeto que yo cree para guardar los valores de cada celda if(x==20 || y==25) matriz[x][y].bomb = 0
            }
        }
        cellValue(); 
} 

2. hacer una función que dibuje todas esas celdas, yo vi mas apropiado que las dibujara dependiendo del estado de ésta. 0 tapada, 1 descubierta, 2 bandera y también del valor, por ejemplo si la casilla vale 3 y esta destapada, que dibuje un 3.

function drawMatriz(){
    for(let x=0; x<columns; x++){for(let y=0;y<rows;y++){
            let cell = matriz[x][y];if(cell.state == 2){
                let coordinates = tileID[9]; 	// yo en vez de dibujar cuadrados use imagenes o mejor dicho un mapa de tiles
                drawTile(coordinates,cell);	//tileID tiene las coordenadas de cada tile
            }elseif(cell.state == 0){
                let coordinates = tileID[0];
                drawTile(coordinates,cell);  
            }elseif(cell.bomb == true){               
                let coordinates = tileID[8];
                drawTile(coordinates,cell);
            }elseif(cell.value > 0){
                switch(cell.value){
                    case1: drawTile(tileID[1],cell);break;case2: drawTile(tileID[2],cell);break;case3: drawTile(tileID[3],cell);break;case4: drawTile(tileID[4],cell);break;case5: drawTile(tileID[5],cell);break;case6: drawTile(tileID[6],cell);break;case7: drawTile(tileID[7],cell);break;
                }    
            }else {
                gContext.fillStyle ="gray";
                gContext.fillRect(cell.x, cell.y, cell.size, cell.size);   
            }    
        }    
    }
}

mapa de tiles

3. Necesitaras limpiar áreas dependiendo si haces click en una celda vacía y sin valor o viceversa; también del modo de juego. Recuerda que en busca minas puedes jugar de dos maneras, pinchando celdas o números.

4. Lo ultimo seria la reacción en cadena que sucede al destapar una celda sin valor y sin mina que tiene al rededor celdas sin valor y sin mina, te lo dejo a tu imaginación.

En si seria todo, espero que te haya servido el tutorial, no abarque todos los aspectos del juego ya que me tomaría mas de un tutorial, así que dejare el link del código abajo

proyecto acabado: https://www.instagram.com/p/Btpe66fH0qF/?utm_source=ig_web_button_share_sheet
código: https://github.com/Angrub/BuscaMInas

Escribe tu comentario
+ 2