> Manuales > Manual de Phaser

En este artículo aprenderás a realizar grupos de elementos con el motor de juegos Javascript Phaser 3 y aplicarles comportamientos y configuraciones comunes.

Grupos de elementos en Phaser

En el Manual de Phaser hemos ido aprendiendo muchas cosas sobre el motor de juegos Javascript. Ahora ha llegado el momento de colocar los bloques que se deben romper con la bola en nuestro juego clon del Arkanoid, lo que nos permitirá llegar a un estado del juego casi definitivo.

Esta parte nos ayudará a entender una de las posibilidades más útiles de Phaser, que es la creación de grupos de elementos. Estos grupos son tan interesantes porque permiten definir de una única vez acciones y comportamientos que compartirán todos los elementos del grupo, lo que ayuda mucho a la hora de codificar.

Por supuesto, en nuestro juego del Arkanoid (también conocido como Breakout), todos los bloques formarán parte de un mismo grupo, ya que su función es exactamente igual.

Cómo crear un grupo

Los grupos en Phaser se crean en el método create() de las escenas. Para ello existen diversos métodos según el tipo de elementos que queremos insertar en los grupos.

En nuestro caso los bloques son elementos estáticos, que no van a moverse, solamente aparecer y desaparecer cuando la bola choque con ellos. Como tienen que responder a las colisiones necesitamos hacelos mediante los métodos de físicas.

Este sería un modo de hacer un grupo.

this.miGrupo = this.physics.add.staticGroup();

Luego podemos insertar elementos dentro del grupo, con el método create, pero asociado al grupo que se acaba de generar.

this.miGrupo.create(54, 44, 'elementocargado');
this.miGrupo.create(75, 32, 'elementocargado');

Como puedes ver, indicamos la posición del elemento que va a colocarse dentro del grupo y luego el identificador del elemento que hemos cargado previamente en el preload().

Crear grupos y sus elementos mediante un mismo método

Sin embargo, en muchas ocasiones no es práctico crear todos los elementos de esta manera, agregando uno a uno al grupo. Simplemente porque habitualmente los elementos se colocan según un patron determinado. En estos casos podemos usar una estrategia distinta, que consiste en crear el grupo y sus elementos en un único paso.

Esto es justamente lo que hemos hecho para colocar de una vez todos los bloques de nuestro juego.

this.bricks = this.physics.add.staticGroup({
  key: ['bluebrick', 'orangebrick', 'greenbrick', 'blackbrick'], 
  frameQuantity: 10,
  gridAlign: { 
    width: 10, 
    height: 4, 
    cellWidth: 67, 
    cellHeight: 34, 
    x: 112, 
    y: 100
  }
});

Como puedes ver, el método para crear el grupo es el mismo: staticGroup(), sin embargo esta vez lo hemos configurado mediante un objeto. Ese objeto podría tener varios formatos distintos, así que toma este ejemplo como una de las muchas posibilidades que este método nos ofrece.

Explico los distintos elementos de este objeto de configuración del grupo:

Hay un tema Importante con respecto a las imágenes declaradas en el array "key": las debemos haber declarado en el método preload(), con un código como este:

this.load.image('bluebrick', 'images/brickBlue.png');
this.load.image('blackbrick', 'images/brickBlack.png');
this.load.image('greenbrick', 'images/brickGreen.png');
this.load.image('orangebrick', 'images/brickOrange.png');

Colisiones con el grupo

Como habíamos comentado, lo bueno de los grupos es que permiten configuraciones comunes para todos los elementos. Es el caso de las colisiones: en vez de definir la colisión para cada bloque a romper, podemos definirla de una única vez para todo el grupo.

Por lo demás, las colisiones se definen con el mismo método collider que ya conocemos, en este caso entre la bola y el grupo de bricks que acabamos de crear.

this.physics.add.collider(this.ball, this.bricks, this.brickImpact, null, this);

El método brickImpact() se encargará de definir qué pasa cuando se produzca una colisión. Nuestro objetivo es simplemente borrar el bloque que acaba de impactar la bola.

brickImpact(ball, brick) {
  brick.disableBody(true, true);
}

Gracias a que el método callback que se ejecuta por la colisión nos entrega como parámetro el brick implicado en esta colisión, podemos eliminarlo del juego con el método disableBody().

Agregar puntos a nuestro marcador cuando quitas bloques

Ahora vamos a hacer un poco más complejo el método que implementa la colisión, para asignar nuevos puntos al marcador de puntos del juego.

Aunque para facilitarlo hemos creado un método especial que incrementa los puntos y actualiza el marcador.

brickImpact(ball, brick) {
  brick.disableBody(true, true);
  this.increasePoints(10);
}

El método increasePoints(), que también vamos a crear dentro de la clase Game, sería el siguiente.

increasePoints(points) {
  this.score += points;
  this.scoreText.setText('PUNTOS: ' + this.score);
}

No tiene nada nuevo que contar, pues ya vimos cómo gestionar mensajes de texto en el juego.

Mostrar un mensaje cuando se eliminan todos los bloques

Para acabar, mostraremos un mensaje de felicitaciones cuando el jugador haya conseguido terminar con todos los bloques.

Lo haremos por medio de una imagen, de manera similar a como mostrábamos el mensaje de game over. Así que debemos comenzar por ver la precarga de esa imagen en el método preload():

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

Luego la colocación de la imagen en el método create(), teniendo cuidado de hacer que la imagen no sea visible de entrada:

this.congratsImage = this.add.image(400, 90, 'congratulations');
this.congratsImage.visible = false;

Por último mostramos la imagen cuando ya no quedan bloques por destruir en el juego. Para saberlo tenemos un método en el grupo que nos dice cuántos bloques están activos: countActive().

brickImpact(ball, brick) {
  brick.disableBody(true, true);
  this.increasePoints(10);
  if (this.bricks.countActive() === 0) {
    this.congratsImage.visible = true;
    this.scene.pause();
  }
}

Apreciarás que, además de mostrar el mensaje de felicitaciones, pausamos la escena para que la bola no siga moviéndose por la pantalla.

Ocultar todos los elementos de un grupo a la vez

Hay otra modificación del código del juego que merece la pena comentar. Resulta que, cuando perdemos el juego y nos aparece la imagen de game over deseamos ocultar de una vez todos los ladrillos, porque si no la imagen queda un poco extraña al aparecer encima de los ladrillos. Es solo un detalle, pero que nos sirve para aprender algo nuevo.

Básicamente, cuando tenemos un grupo y queremos que todos sus elementos se vuelvan invisibles, no te vale con modificar una propiedad "visible" como pasaba con una imagen simple. Ahora hay un método específico para hacer esta labor.

this.bricks.setVisible(false);

Esta línea de código la hemos colocado cuando se da el juego por perdido, es decir, al irse la bola por la parte inferior de la pantalla.

Conclusión

El juego ha avanzado una barbaridad. Ahora sí que es un auténtico Arkanoid o Breakout. Espero que los resultados estén siendo satisfactorios para ti.

Para tu referencia, con todas las mejoras incorporadas en este artículo, el código nos ha quedado como podemos ver en este enlace de gitHub.

Ahora mismo ya tenemos un juego que podríamos casi terminado, aunque lo cierto es que nos quedan muchas ideas de mejoras que nos darán pie a aprender más sobre Phaser. En el siguiente artículo veremos como crear algunos comportamientos extra que incluyen el trabajo con la escena del juego, lo que nos permitirá reiniciarla cuando la partida acabe.

Videotutorial grupos de sprites en Phaser

En este videotutorial veremos de manera práctica todo lo explicado en este artículo, para los que os gusta aprender de manera visual. Veremos cómo crear grupos de sprites en Phaser y cómo gestionar las colisiones para implementar sus comportamientos.

Miguel Angel Alvarez

Fundador de DesarrolloWeb.com y la plataforma de formación online EscuelaIT. Com...

Manual