Hola amigos! Hoy vamos a mover un poco el canvas que dejamos tan oscuro en nuestra última entrega. Nuestro objetivo será crear un juego tipo Breakout, de esos de romper ladrillos. Es un juego muy antiguo, pero no por eso ha dejado de ser tan adictivo como siempre ha sido. Para nuestro ejercicio vamos a necesitar un par de archivos que podéis descargar desde el enlace que os dejo y colocarlos en la carpeta assets de nuestro juego:
https://www.dropbox.com/s/mzpx7pf869wx4it/assets.rar?dl=0
Con lo que deberíamos tener una estructura como se muestra en esta captura.
Las ilustraciones representan la pala que moveremos de izquierda a derecha y la pelota que vamos a hacer golpear contra el techo y las paredes. Más tarde vendrán los ladrillos que deberemos romper.
En el capítulo anterior vimos que una escena de Phaser tiene varios métodos propios como el método init, create, preload o update. Vamos a ir viendo alguno de ellos. Pero antes de eso, vamos a cambiar la estructura de nuestro código. El ejemplo anterior era extremadamente simple pero hoy vamos a utilizar métodos propios de Phaser y vamos a definir nosotros un método para nuestras tareas, por lo que vamos a crear la escena de una forma más profesional y más de acuerdo a la programación orientada a objetos. Este sería el resultado del nuevo código:
class Rompeladrillos extends Phaser.Scene {
constructor() {
super({ key: 'Rompeladrillos' });
}
}
const config = {
width: 800,
height: 600,
// parent: "contenedor",
type: Phaser.AUTO,
physics: {
default: 'arcade'
},
};
const game = new Phaser.Game(config);
game.scene.add( 'Rompeladrillos', Rompeladrillos);
game.scene.start('Rompeladrillos');
Con esto, hemos creado la clase “Rompeladrillos” que hereda de la clase Phaser.Scene. En su constructor hemos definido su clave con el nombre de la clase y después, al inicar el juego, llamamos a dos métodos de la clase Scene para cargar la escena primero y para que se inicie después. En la variable de configuración del juego hemos añadido el miembro “physics” que le va a indicar a Phaser cómo debe interpretar las físicas del juego (gravedad, elasticidad, etc)
La función Preload
Los juegos suelen necesitar gran cantidad de contenido audiovisual para garantizar una grata experiencia al usuario y todos esos contenidos hay que cargarlos para poder utilizarlos. Esos contenidos es lo que llamaremos a partir de ahora “assets”
El método “preload” sirve precisamente para cargar todos esos assets antes de que comience la actividad del juego. Utilizar este método tiene sus ventajas ya que nos permitiría saber el porcentaje de carga que llevamos en cada momento si quisiéramos indicárselo al usuario mediante la típica barra de carga o ese icono que da vueltas.
Para nuestro juego vamos a cargar las dos imágenes que hemos colocado en la carpeta assets en la función preload, que la añadimos justo después del constructor:
preload() {
this.load.image('ball', './assets/steemball.png');
this.load.image('pala', './assets/pala.png');
}
Con estas dos líneas estamos indicando a Phaser que cargue los archivos png desde la ruta indicada y que a cada uno de esos objetos cargados se le asigne una etiqueta, o clave, para poder referirnos a ellos en el futuro.
Función Create
En este método es donde vamos a crear la escena. Queremos mover la pala de izquierda a derecha al mismo tiempo que movemos el puntero del mouse, por lo que vamos a necesitar capturar los eventos del mouse. La bola estará siempre en movimiento, golpeando contra las paredes, techo y bloques pero podrá perderse por el “suelo”, con lo que habremos perdido.
Para que los objetos en movimiento de nuestro canvas no se salgan de los límites necesitamos establecer unos límites:
this.physics.world.setBoundsCollision(true, true, true, false);
Fíjate que los parámetros activan la colisión a la izquierda, derecha, arriba, pero no abajo.
Colocamos en escena la pala y la pelota como objetos con físicas. La pala no debe de reaccionar a las colisiones, será un objeto inamovible, pero la pelota deberá rebotar contra los objetos que choque y deberá tener en cuenta los límites antes definidos para el canvas.
this.pala = this.physics.add.image(400, 580, 'pala')
.setImmovable(); // no reaccionará a las colisiones
this.ball = this.physics.add.image(400, 530, 'ball')
.setCollideWorldBounds(true) // reacciona a los límites del canvas
.setBounce(1); // rebota con la misma velocidad de inicio
Mientras el juego no empiece, vamos a hacer que la pelota se mueva con la pala. Para diferenciar si el juego ha empezado o aún estamos esperando necesitaremos un switch, o flag, que guardaremos como un miembro más del objeto de la pelota. Recordad que en Javascript todo son objetos con sus miembros y métodos.
Y configuramos la reacción a las colisiones entre la bola y la pala. Cuando definimos una colisión le indicaremos a Phaser qué objetos se ven involucrados y qué función se encargará de gestionar la colisión. En nuestro ejemplo, cada vez que la pelota colisione con la pala se llamará a la función “this.raquetazo”.
Golpeando la pelota
Definimos el método “raquetazo” como miembro de la clase “Rompeladrillos”.
Cuando la pelota golpea la pala, esta rebotará con un ángulo que vamos a determinar dependiendo de en qué parte de la pala ha golpeado. Si golpea por la mitad izquierda, la pelota irá hacia la izquierda, y si golpea por la mitad derecha, rebotará hacia la derecha. Además, queremos que el ángulo sea más bajo cuanto más hacia los extremos golpee.
Si golpea justo en el centro, calcularemos un pequeño valor aleatorio para que le de un poco de emoción a la dirección del rebote.
raquetazo(ball, pala) {
let lado = 0;
// Si la pelota golpea por la izquierda de la mitad de la pala
if (ball.x < pala.x) {
lado = pala.x - ball.x;
ball.setVelocityX(-10 * lado);
// si la pelota golpea por la derecha de la mitad
} else if (ball.x > pala.x) {
lado = ball.x - pala.x;
ball.setVelocityX(10 * lado);
// si la pelota golpea justo en el centro de la pala
} else {
ball.setVelocityX(Math.floor(Math.random() * 10) - 5);
}
}
Como ya recordé antes, en Javascript todo son objetos. Las imágenes que cargamos en la función “preload” están contenidas en un objeto que entre sus miembros dispone de los elementos "x" e "y" para indicarnos sus coordenadas en la pantalla. Modificando esos valores podemos cambiar la posición del objeto en pantalla.
Moviendo la pala
Volvamos a la función Create. Ahora vamos a capturar los eventos del mouse, concretamente el evento de movimiento para desplazar la pala, y el evento de hacer click para empezar la partida. Bueno, para asegurarnos vamos a capturar el momento en el que soltamos el dedo del botón del mouse.
// obtenemos la coordenada x del mouse
this.posicion = pointer.position.x;
// ajustamos los límites laterales para que la pala no sobresalga de los bordes
if (this.posicion < 40) this.posicion = 40;
if (this.posicion > 760) this.posicion = 760;
// modificamos la posición de la pala según la posición horizontal del puntero del mouse
this.pala.x = this.posicion;
// si la partida aún no ha empezado, la pelota acompaña a la pala
if (this.ball.inicio) {
this.ball.x = this.posicion;
}
});
Después de capturar la coordenada X del mouse en la variable this.posicion, ajustamos los límites para que la pala no se nos escape por los bordes del canvas. Como la pala mide 80 pixeles de ancho, tendremos en cuenta un desplazamiento de 40 pixeles por cada lado para realizar la corrección. El ancho del canvas es de 800 pixeles, por lo que el centro de la pala debe de moverse entre el pixel 40 y el 760.
Al mismo tiempo y mientras movemos la pala, si el flag que indica el inicio de la partida aún dice que no ha empezado, forzamos a que la pelota acompañe a la pala como si fueran el mismo objeto.
¡Y comienza la partida al hacer click y soltar el botón del mouse!
this.input.on(Phaser.Input.Events.POINTER_UP, () => {
if (this.ball.inicio) {
this.ball.setVelocity((Math.floor(Math.random() * 100) - 50, -300));
this.ball.inicio = false;
}
});
Comprobamos el estado del flag y si dice que aún no ha empezado la partida, asignamos una velocidad inicial a la pelota, con lo que se empezará a mover, y cambiamos el estado del flag para que indique que ya empezó la partida.
Con esto ya podemos empezar a practicar a golpear la pelota contra las paredes. Puedes descargar el código del juego en este enlace para comprobar si has seguido correctamente las instrucciones. Recuerda que para ejecutar el programa debes iniciar el servidor virtual "Go Live" de Visual Studio Code.
https://www.dropbox.com/s/7lhoburodpq6qjp/SteemBreakout.rar?dl=0
Nos vemos con la próxima entrega. Si te gusta mi trabajo no dudes en apoyarme con tu voto como Witness.
Esto lo tengo que probar mañana. Gracias por estos tutoriales!
Downvoting a post can decrease pending rewards and make it less visible. Common reasons:
Submit
Excelente tutorial y un gran trabajo. Te dejamos nuestro apoyo por aqui, querido @marcosdk.
Downvoting a post can decrease pending rewards and make it less visible. Common reasons:
Submit