Cómo hacer colisiones más complejas, que implementan comportamientos personalizados en los juegos creados con el motor Javascript Phaser.
Si has seguido hasta aquí los artículos anteriores del Manual de Phaser 3 habrás podido construir un pequeño proyecto de juego inspirado en el clásico Arkanoid… aunque mucho más sencillo y orientado a conocer las principales características de este motor de juegos.
En este artículo vamos a seguir avanzando en este proyecto, agregando una acción un poco más elaborada, que se ejecutará cuando se produzca una colisión de la bola contra la plataforma.
Colisiones que ejecutan métodos
El mismo método collider() que habíamos usado para detectar las colisiones entre elementos lo podemos usar enviando parámetros adicionales, que nos sirven para indicarle extras, como métodos que se ejecutarán cuando se produzca esa colisión.
Es bastante sencillo. Simplemente tienes que cambiar el método con el que implementamos la colisión, para que tenga un código como este.
this.physics.add.collider(this.ball, this.platform, this.platformImpact, null, this);
Los dos primeros parámetros son necesarios siempre. El resto son opcionales. En detalle, estamos indicando lo siguiente:
- Objeto de colisión 1: el primer objeto sobre el que se configura la colisión.
- Objeto de colisión 2: el segundo objeto implicado en la colisión.
- Callback de colisión: es una función que se ejecutará cuando los dos elementos se han chocado. Esta función es la clave de la implementación del comportamiento, pues es donde se permite especificar el código del Javascript a ejecutar como consecuencia del impacto.
- Callback para decidir si hay colisión: esta sería una función que permite decidir si se debe ejecutar el comportamiento de colisión o no. Es una función que siempre debe devolver un boleano. Si le entregamos null, como en nuestro ejemplo, siempre que se toquen los elementos se producirá el comportamiento de colisión.
- El contexto sobre el que se ejecutarán los callback de colisión. Este contexto será habitualmente "this", para que dentro del código de la función, la variable this siga siendo igual a la escena sobre la que estamos trabajando. Aquí lo normal será pasarle "this", pero podríamos pasarle otro objeto cualquiera, para que "this" dentro del método callback de la colisión sea una referencia a ese objeto.
Tal como hemos configurado la colisión con elmétodo collider(), en cada impacto se ejecutará el método platformImpact() de la escena, que tendrá la responsabilidad de realizar las acciones que sean necesarias para poder procesar ese choque.
Quizás quede la duda en qué se diferencian los dos callback entregados al método collider() (aunque en nuestro caso solo hemos definido un callback, ya que al segundo simplemente hemos asignado "null". El primero define un código a ejecutar cuando se produce la colisión, pero el segundo decide si hubo realmente una colisión o no. Es decir, Phaser nos está dando una oportunidad de simplemente ignorar esa colisión (por ejemplo imagina que tu personaje tiene un poder especial y es inmune a ciertos enemigos temporalmente, entonces en este método podrías comprobar si está activo tal poder y devolver false para que el comportamiento de la colisión no se produzca). Dicho en código, si en el segundo callback tenemos una función que siempre devuelve false, sería como si esta colisión nunca se pudiera llegar a producir.
this.physics.add.collider(this.ball, this.platform, this.platformImpact, () => false, this);
Obviamente la configuración de la línea de código anterior no tendría ningún sentido, porque sería lo mismo que no hacer nada en ningún caso (estaríamos definiendo una colisión que nunca se va a procesar).
Inicializar una escena: Método init()
Enseguida vamos a ver el método de platformImpact(), que tendremos que implementar dentro de la clase Game. Pero antes de ello queremos pararnos en otro de los métodos del ciclo de vida de la escena: init().
Para el comportamiento que vamos a implementar necesitamos realizar una pequeña inicialización de la escena. La idea es inicializar una propiedad de la escena que llevará la puntuación del juego. El lugar adecuado para inicializar esa propiedad sería el método init(), para que esté lista en el momento que la escena comience su ejecución.
init() {
this.score = 0;
}
Recuerda que el método init() se ejecuta cada vez que la escena inicia o reinicia, por lo que si el juego reiniciase la escena (por ejemplo para jugar otra partida nueva), la puntuación volvería a su estado inicial.
Implementar el comportamiento de la colisión
Ahora nos podemos centrar en platformImpact(), que es el método que se ejecuta como callback cuando se produce una colisión. Más adelante ese método nos servirá para hacer cosas más complejas, pero de momento simplemente vamos a incrementar en uno una propiedad "score", para llevar la cuenta de las veces que hemos conseguido hacer que la bola rebote sobre la plataforma.
Ahora veamos el método que implementa el impacto contra la plataforma, que simplemente incrementa en 1 la propiedad score. Además de momento mostraremos ese valor simplemente en la consola de Javascript.
platformImpact() {
this.score++;
console.log(this.score);
}
Cómo mostrar mensajes de texto en el juego
Lo normal es que queramos ver la puntuación actual en el juego, en lo que sería un marcador. Para ello vamos a usar un objeto de texto, que es bastante sencillo de implementar. Como es texto plano, no necesitamos cargar ninguna imagen ni nada parecido. Directamente mostraremos el texto en el método create(), donde vamos a agregar este código.
this.scoreText = this.add.text(16, 16, 'PUNTOS: 0', {
fontSize: '20px',
fill: '#fff',
fontFamily: 'verdana, arial, sans-serif'
});
Gracias a ese código estamos colocando la puntuación cerca de la esquina superior izquierda, con el texto inicial 'PUNTOS: 0'. Además estamos agregando algo de estilo a este texto. Además, estamos guardando el objeto "text" dentro de una propiedad de la escena llamada "scoreText".
Ahora, para actualizar el texto del marcador simplemente tenemos que invocar el método setText() del objeto de texto. Esto lo vamos a realizar cada vez que se produzca una colisión.
platformImpact() {
this.score++;
this.scoreText.setText('PUNTOS: ' + this.score);
}
Así quedará nuestro marcador de puntuación. Es un texto sencillo, pero cumple su función.
Videotutorial de colisiones con Phaser
Para complementar lo aprendido en este artículo puedes acceder al videotutorial del manejo de colisiones en Phaser.
Además en este vídeo aprenderas una de las maneras de organizar tu código en la escena, creando nuevas clases que se encargan de implementar ciertos comportamientos, como el marcador de puntuación.
Conclusión
Ya está. Gracias a ese sencillo código somos capaces de llevar la cuenta de los puntos del juego y actualizar el marcador de puntuación cada vez que se agrega un nuevo punto a nuestro contador.
En este artículo hemos aprendido a generar comportamientos, a ejecutar cuando se producen colisiones entre los elementos del juego. Además hemos aprendido a colocar texto dentro de la escena, con el que vamos a llevar la puntuación del juego.
En el siguiente artículo iremos un poco más lejos, creando un comportamiento un poco más complejo, que modifique la trayectoria y velocidad de la bola en función del lugar de la plataforma donde impacte.
Miguel Angel Alvarez
Fundador de DesarrolloWeb.com y la plataforma de formación online EscuelaIT. Com...