> Manuales > Manual de Phaser 3

En este artículo vamos a seguir explicando las bases del motor de juegos Javascript Phaser, analizando el sistema de físicas y las colisiones entre elementos, sus rebotes, etc.

Colisiones y rebotes en Phaser

Con Phaser podemos realizar juegos Javascript de manera bastante sencilla. En artículos anteriores del Manual de Phaser ya vimos cómo organizar el código de un juego y cómo crear una escena, colocando código en sus diversos métodos del flujo de ejecución.

Nos estábamos acercando a realizar un sencillo juego del Arkanoid, pero nos falta algo fundamental, que es la bola!. Ésta nos servirá para aprender a gestionar colisiones y rebotes del elemento en la pantalla.

Cargar la imagen de la bola

Comenzamos con el preload() para cargar la bola. Es una imagen simple, como en otras ocasiones. El código ya te sonará.

this.load.image('ball', 'images/ball.png');

Esta línea la tienes que agregar al método preload() de la escena que tenemos en game.js.

Crear la bola y ajustar su movimiento

Ahora vamos con el método create(), en el que configuramos la escena. Vamos a introducirle unas cuantas líneas de código a este método para ir configurando el comportamiento de la bola.

En primer lugar tenemos que agregar la bola al juego, por lo que comenzaremos creando la imagen usando el sistema de físicas.

this.ball = this.physics.add.image(400, 30, 'ball');

De momento la bola comienza en la parte de arriba de la pantalla y caerá hacia abajo, porque es un elemento al que le afecta la gravedad. Sin embargo, si ejecutas el juego en este momento verás que la bola pasa por encima de la plataforma y no bota ni nada, es normal.

Para que rebote por los bordes del juego tenemos que configurar un par de cosas.

Con esta línea de código consigues que la bola se afecte por los bordes del juego, por lo que no se podrá salir del área del juego.

this.ball.setCollideWorldBounds(true);

Si ejecutas el juego ahora verás que la bola cuando cae, se queda pegada a la parte inferior de la pantalla.

Para conseguir que rebote tenemos que aplicar una línea de código extra, que permite configurar la fuerza del rebote.

this.ball.setBounce(1);

El valor que enviamos al método setBounce() indica la fuerza. Un valor igual a 1 indica que la fuerza será la misma con la que llegó al suelo, por lo que al rebotar llegará de nuevo hasta la misma altura que tenía la bola cuando comenzó a caer. Puedes probar con varios valores entre 0 y 1 en el setBounce() para ver lo que pasa.

Si ejecutas el juego en este punto verás que la bola rebota y rebota, permaneciendo siempre en la misma posición horizontal. Esto es un poco aburrido, por lo que vamos a agregarle un poco de desplazamiento a los lados.

Para que un elemento se mueva hacia la izquierda o la derecha es suficiente con asignarle un poco de velocidad en el eje de las "x". Esto lo consigues con el método setVelocity(), del elemento al que quieres asignar la velocidad, indicando como parámetro la velocidad en el eje de las "x" (horizontal) y la velocidad en el eje de las "y" (vertical).

this.ball.setVelocity(100, 10)

Un valor superior a cero en el eje de las x hace que se desplace hacia la derecha. Un valor superior a cero en el eje de las y hace que se desplace para abajo. Por tanto, al invocar setVelocity() sobre la bola, con los parámetros (100, 10) se moverá hacia la derecha y hacia abajo.

Existen dos métodos extra setVelocityX() y setVelocityY() que nos pueden servir si solamente queremos setear la velocidad en uno de los ejes en un momento dado. Otra cosa interesante a observar es que, en el caso de la velocidad en el eje de las Y, tienes que tener en cuenta que, si existe gravedad en el juego, también le afectará a la velocidad, haciendo que aumente la velocidad cuando cae el objeto, o que disminuya la velocidad cuando está subiendo.

En nuestro juego, para darle un poco de variabilidad, vamos a calcular la velocidad horizontal de manera aleatoria, para que unas veces vaya más rápido que otras. Además también vamos a "sortear" si va a desplazarse a la izquierda o la derecha. Una vez que hayamos calculado la velocidad y el sentido, la asignaremos mediante el método setVelocity().

let velocity = 100 * Phaser.Math.Between(1.3, 2);
if (Phaser.Math.Between(0, 10) > 5) {
  velocity = 0 - velocity;
}
this.ball.setVelocity(velocity, 10);

Espero que puedas entender el código. Fíjate que Phaser te entrega algunas fórmulas de matemáticas como la de crear valores aleatorios entre un mínimo y un máximo, como Phaser.Math.Between().

El condicional sirve para que la bola salga aleatoriamente unas veces a izquierda y otras a derecha. Para ello simplemente cambiamos el signo de la velocidad, de positivo a negativo, el 50% de las veces.

Por último lanzamos el método setVelocity() sobre la bola, que ajusta la velocidad de la misma. En la velocidad horizontal usamos el valor de velocidad generado aleatoriamente y en la vertical colocamos 10, que quiere decir que la bola caerá suavemente (un valor que será afectado por la gravedad configurada en el juego).

Si ejecutas el juego hasta el momento verás como la bola permanece botando por siempre entre los límites de la pantalla. Siempre con la misma intensidad, que será variable cada vez que refresques la página.

Ajustar los límites en los que se deben producir rebotes de los elementos

Solo hay un detalle más. En nuestro juego la bola no tiene que botar en el suelo. Se tendría que perder por la parte de abajo si no conseguimos que rebote encima de la plataforma. Para ello tenemos que configurar el mundo. Con esta línea:

this.physics.world.setBoundsCollision(true, true, true, false);

Eso hace que los límites del mapa tengan colisión, todos menos el límite inferior. Ahora la bola se perderá por la parte de abajo cuando caiga por primera vez.

Gestionar colisiones

Ahora vamos a gestionar las colisiones. Phaser nos lo deja bastante fácil con sus funciones de físicas.

Simplemente vamos a agregar esta línea de código al método create(), justo debajo del todo, ya que para que funcione se deben haber generado tanto la bola como la plataforma.

Básicamente vamos a usar el método collider() perteneciente al set de comportamientos físicos. Ese método pertenece a la escena, que tiene su propiedad physics y add. Recibe dos parámetros que son los dos elementos sobre los que estamos implementando el comportamiento de la colisión.

this.physics.add.collider(this.ball, this.platform);

Así estamos indicando que se gestione la colisión entre la bola y la plataforma, por lo que ahora la bola no pasará por encima de la plataforma como antes, sino que chocará con ella.

Prueba el juego y verás lo que pasa… al chocar la bola contra la plataforma hace que ésta se desplace hacia abajo. Es normal ¿no? así ocurre también en la vida real. Al chocarse un elemento contra el otro hace que el elemento contra el que se ha estrellado se mueva. Sin embargo, no es lo que nosotros querríamos, puesto que la plataforma debería seguir en el mismo lugar. Eso lo conseguimos aplicando una propiedad nueva a la plataforma.

Al crear la plataforma indicaremos que sea inamovible, o lo que es lo mismo, que no se pueda desplazar por otros actores del juego.

this.platform = this.physics.add.image(400, 460, 'platform').setImmovable();

La novedad es la invocación del métoodo setImmovable() al crear la plataforma.

Ahora el juego responderá más o menos a nuestras necesidades de comportamiento de la colisión.

Si quieres puedes probar el juego hasta este punto. Está bastante chulo, porque ya es jugable. De momento hemos creado un juego para hacer rebotar una bola sobre la plataforma y cada vez que rebota vuelve a caer y tenemos que recuperarla… hasta que se nos cae y el juego acaba!

Mostrar la imagen de Game over

Vamos a hacer un pequeño comportamiento para mostrar la imagen de game over cuando el juego acaba, porque la bola se pierda.

Este comportamiento lo tendremos que realizar en el método update() que, recuerda, se ejecuta en bucle por siempre en el juego. En este método buscaremos el instante en el que la bola ha llegado a los límites inferiores, lo que querrá decir que no hemos conseguido que bote encima de la plataforma.

Para saber cuándo la bola ha sobrepasado los límites del juego podemos acceder a su propiedad "y", que es la posición en la vertical. Como el área del juego tiene una altura de 500 píxeles, cualquier valor de y superior a 500 significa que la bola se ha perdido.

En ese caso haremos que se muestre la imagen de game over con su propiedad "visible".

if (this.ball.y > 500) {
      console.log('fin');
      this.gameoverImage.visible = true;
}

Sin embargo, ocurre algo que no nos gusta demasiado. El bucle del update sigue ejecutándose por siempre y la verdad es que, si el juego ha acabado no necesitaría mantenerse activo de esa manera.

Esto lo podemos ver porque hemos colocado un "console.log()" que dice "fin" y ese console.log se queda repitiéndose de manera indefinida.

Para evitarlo, una idea sería simplemente pausar la escena, lo que podemos hacer con el método this.scene.pause(). Nuestro código quedaría ahora así.

if (this.ball.y > 500) {
      console.log('fin');
      this.gameoverImage.visible = true;
      this.scene.pause();
}

Una vez ejecutado verás que solo se produce una vez el console.log('fin').

Conclusión

Tenemos ya un jueguecito sencillo, simple diría yo, pero al menos jugable. ¿Cuántas veces consigues que la bola rebote sin que se caiga?

Hemos aprendido lo básico de las colisiones, pero aún nos queda más para poder sacarle partido a las posibilidades de Phaser y acercarnos un poco más a los objetivos finales de este juego.

El código tal como lo tenemos hasta este punto lo puedes encontrar en este enlace de Github: https://github.com/deswebcom/ball-game-phaser/tree/Rebote

En el siguiente artículo seguiremos trabajando con las colisiones, porque de momento hemos visto solo el principio. Piensa que en los juegos habitualmente querrás que pasen cosas cuando se produzca un impacto ¿no?. Así que seguiremos mejorando el juego para añadir comportamientos a las colisiones.

Miguel Angel Alvarez

Miguel es fundador de DesarrolloWeb.com y la plataforma de formación online Escu...

Manual