0% encontró este documento útil (0 votos)
1K vistas88 páginas

Manual de Phaser 3

Este documento presenta un manual para el framework Phaser 3 para el desarrollo de juegos HTML5. Explica que Phaser es un motor de juegos 2D basado en JavaScript que permite crear juegos para navegador y móviles de manera sencilla. El manual enseñará conceptos básicos de Phaser y cómo desarrollar ejemplos prácticos de juegos paso a paso para aprender a usar sus funciones.

Cargado por

Dante Rodriguez
Derechos de autor
© © All Rights Reserved
Nos tomamos en serio los derechos de los contenidos. Si sospechas que se trata de tu contenido, reclámalo aquí.
Formatos disponibles
Descarga como PDF, TXT o lee en línea desde Scribd
0% encontró este documento útil (0 votos)
1K vistas88 páginas

Manual de Phaser 3

Este documento presenta un manual para el framework Phaser 3 para el desarrollo de juegos HTML5. Explica que Phaser es un motor de juegos 2D basado en JavaScript que permite crear juegos para navegador y móviles de manera sencilla. El manual enseñará conceptos básicos de Phaser y cómo desarrollar ejemplos prácticos de juegos paso a paso para aprender a usar sus funciones.

Cargado por

Dante Rodriguez
Derechos de autor
© © All Rights Reserved
Nos tomamos en serio los derechos de los contenidos. Si sospechas que se trata de tu contenido, reclámalo aquí.
Formatos disponibles
Descarga como PDF, TXT o lee en línea desde Scribd
Está en la página 1/ 88

http://desarrolloweb.

com/manuales/phaser3 Página 1 de 88
Introducción: Manual de Phaser 3

Este es el Manual de Phaser 3, con el que aprenderás a crear tus propios juegos usando un
completo motor basado en Javascript. Los juegos que podrás realizar con Phaser se pueden
ejecutar en el navegador y también en móviles. Incluso podrías hacer aplicaciones usando un
Webview, con Cordova o Capacitor (Ionic).

Phaser tiene innumerables posibilidades, desde la creación de diversas escenas de juego, la


capacidad para realizar animaciones de diversos modos, usar distintas cámaras para hacer
gestión del scroll, usar tanto WebGL como Canvas para mover los gráficos, trabajar con
partículas y mucho más.

Existen diversos motores de Juegos en Javascript y Phaser es el más usado por la comunidad.
No decimos que sea el mejor o el más completo de los frameworks existentes en el mercado,
pero sí lo suficientemente sencillo como para que puedas dar tus primeros pasos en el
desarrollo de juegos, de una manera asequible para programadores de todos los niveles.

En este manual aprenderás a trabajar con el framework Phaser de una manera práctica,
desarrollando ejemplos de juegos que permitan ir conociendo las funciones más usadas para
crear actores, animaciones, usar sonidos, manejar la interactividad y mucho más. Esperamos
que la experiencia de aprendizaje sea atractiva para ti y puedas divertirte mucho aprendiendo
con este manual y creando tus propios juegos.

Estamos escribiendo el manual y publicaremos temas nuevos con frecuencia. Vuelve pronto
para encontrar más contenidos!!

Encuentras este manual online en:


http://desarrolloweb.com/manuales/phaser3

http://desarrolloweb.com/manuales/phaser3 Página 2 de 88
Autores del manual

Las siguientes personas han participado como autores escribiendo artículos de este manual.

Miguel Angel Alvarez

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


EscuelaIT. Comenzó en el mundo del desarrollo web en el año 1997,
transformando su hobby en su trabajo.

http://desarrolloweb.com/manuales/phaser3 Página 3 de 88
Bases del framework Javascript para
juegos HTML5

Comenzamos el Manual de Phaser con algunos artículos esenciales para empezar a entender
cómo funciona el framework Phaser. Será un manual bien práctico y lo explicaremos
directamente sobre un proyecto que iremos realizando a lo largo de distintos artículos.

Introducción a Phaser
Introducción al motor de juegos Javascript Phaser y pasos para crear tu primer juego HTML5
con este potente framework de desarrollo.

Phaser es un motor que permite crear juegos HTML5 de una manera sencilla pero poderosa.
En este artículo encontrarás una primera introducción y comenzaremos a realizar un juego
elemental, con el que dar nuestros primeros pasos con Phaser.

Qué es Phaser

Phaser es un motor de juegos 2D realizado sobre Javascript. Es simplemente un framework o


biblioteca de código Javascript que puedes incluir en tu página y mediante la cual puedes
desarrollar juegos de diferentes tipos. Soporta tanto WebGL como Canvas, de manera
automática. Esto quiere decir que, siempre que sea posible usará el estándar de WebGL y, si el
navegador del usuario no dispone de esta tecnología, usará un elemento Canvas común.

Encontraremos en Phaser una completa cantidad de características útiles, como funciones de


física, sprites, animaciones basadas en sprites y otras tecnologías más avanzadas, distintas
cámaras (con las que puedes realizar juegos con scroll y diversos efectos, sonidos, escalado
según los dispositivos, grupos para asignar propiedades a muchos elementos a la vez, y mucho
mas… además existen plugins que se pueden incluir para mejorar las posibilidades todavía

http://desarrolloweb.com/manuales/phaser3 Página 4 de 88
más.

Se trata de un framework muy completo, aunque a decir verdad, existen otros motores de
juegos para Javascript todavía más avanzados. Sin embargo es una excelente alternativa para
comenzar en el desarrollo de juegos HTML5, por ser lo suficientemente poderoso como para
ofrecer las herramientas necesarias en la mayoría de juegos 2D y lo suficientemente sencillo
como para que no nos desanimemos cuando estamos dando los primeros pasos.

Además cuenta con una excelente comunidad, algo que resulta muy de agradecer cuando
estamos desarrollando y tenemos cualquier tipo de duda. Aparte de una buena documentación,
podemos encontrar numerosos ejemplos de juegos realizados con Phaser, mediante los cuales
es posible aprender a hacer muchas cosas, viendo el código implementado para resolver
problemas similares a los nuestros. Quizás la única pega en este área es que a veces resulta un
poco lío bucear entre la documentación y los ejemplos, pues es fácil acabar cayendo en
ejemplos o artículos correspondientes a diferentes versiones del framework, que no funcionan
en la versión en la que te encuentras.

Cómo instalar Phaser

Phaser es un framework muy sencillo de usar. Gracias a ello, resulta incluso apropiado para
desarrolladores de Javascript de nivel medio. Uno de los puntos donde vemos esta facilidad es
a la hora de instalar el framework, ya que es tan sencillo como incluir el script con el código de
Phaser, que podemos traernos incluso de un CDN.

<script src="//cdn.jsdelivr.net/npm/[email protected]/dist/phaser.js"></script>

Podemos también descargar Phaser desde su página web: https://phaser.io/download/stable o


usar npm si lo preferimos. Una vez incluido este script, dispones de la variable global "Phaser"
que puedes usar para acceder a todos los recursos que te ofrece el framework para construir tu
juego.

Cualquier alternativa es válida para instalar Phaser. Nosotros por facilidad usaremos el CDN
que es la manera más rápida y directa para disponerlo en nuestras prácticas.

Servidor web

El único requisito que necesitamos para que Phaser funcione correctamente es usar un
servidor web para arrancar el juego. Vale cualquier servidor web que podamos tener o usar
habitualmente.

Seguro que los desarrolladores más avanzados de Javascript saben a lo que nos referimos, pero
sería simplemente asegurarnos que accedemos a nuestro servidor donde se encuentra el juego
a través de http:// en lugar de file://.

Este requisito se debe a que Phaser hace la carga de todas las imágenes, sonidos y recursos
necesarios para mover el juego mediante Ajax, por lo que es necesario abrir la página a través
de http, aunque vayamos a acceder a un archivo que está en nuestra propia máquina local.

http://desarrolloweb.com/manuales/phaser3 Página 5 de 88
Puedes hacerte con un servidor web de muchas maneras. Por si no lo sabes, vamos a dar unas
pautas:

Si trabajas con lenguajes como PHP seguro que ya tienes un servidor de desarrollo
montado.
Si trabajas con el desarrollo frontend posiblemente tengas algo como Webpack dev
server o en nodeJS algo como el package npm http-server, que te permite lanzar un
servidor de desarrollo con un comando desde cualquier carpeta.
Puedes ver más alternativas en la FAQ: Probar páginas por http.

Primeros pasos con Phaser

Abordada la necesaria introducción y los requisitos para comenzar, pasamos directamente a la


práctica, para crear la estructura de archivos y carpetas para nuestro primer juego Javascript.

El HTML

Básicamente vamos a tener un archivo HTML en el que incluiremos dos scripts Javascript, el
propio framework Phaser y el código del juego que vayamos a construir.

Este archivo le podríamos llamar con cualquier nombre que queramos, pero comúnmente
usarás "index.html" para que sea el archivo inicial de tu proyecto.

<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Juego de la bola</title>
</head>
<body>

<script src="//cdn.jsdelivr.net/npm/[email protected]/dist/phaser.js"></script>
<script src="index.js" type="module"></script>
</body>
</html>

Como puedes comprobar, este archivo no tiene nada de contenido, ya que no nos hace falta
ninguna etiqueta en particular para mostrar el juego. Simplemente colocaremos los
mencionados scripts y Phaser se encargará de inyectar la correspondiente etiqueta para que se
vea el juego dentro de la página.

El Javascript

Ahora vamos a comenzar a ver el corazón de Phaser, echando un vistazo a nuestro Javascript.

Lo primero, veamos que el archivo que hemos incluido en el HTML por medio de la etiqueta
SCRIPT lo hemos cargado como módulo (fíjate en el type="module").

<script src="index.js" type="module"></script>

http://desarrolloweb.com/manuales/phaser3 Página 6 de 88
El type="module" no es en realidad una restricción de Phaser, es decir, en realidad no es
necesario para que el juego funcione. Lo hemos hecho así para poder hacer imports dentro del
código Javascript del juego, así podemos crear distintos archivos de código con las distintas
partes, de modo que podremos tener un código más mantenible.

Debes tener en cuenta que si usas type="module" estás restringiendo tu Javascript para
poder usarlo solamente con navegadores que tengan la capacidad de usar ES6 modules.
Actualmente todos los navegadores lo soportan, pero si quisieras que el juego funcionase en
Internet Explorer, no sería una alternativa, o tendrías que traspilar el código con algo como
Webpack, lo que de momento queda fuera de nuestra intención. Pero vamos, que hoy
trabajar con ES6 modules es de lo más normal y muy aconsejable.

Ahora veamos el código de nuestro archivo index.js:

import { Game } from './game.js';

const config = {
type: Phaser.AUTO,
width: 800,
height: 500,
scene: [Game],
physics: {
default: 'arcade',
arcade: {
gravity: { y: 400 },
debug: false
}
}
}

var game = new Phaser.Game(config);

Primero importamos una declaración llamada "Game" desde el archivo "game.js". En seguida
veremos el código de ese archivo, pero os adelantamos que aquí tendremos una clase (de
programación orientada a objetos) que implementa la escena principal del juego.

Luego tenemos toda una declaración de la configuración del juego. En ella encontramos varios
detalles interesantes:

type: Phaser.AUTO,

Esto indica que Phaser podrá usar tanto canvas como WebGL, según el navegador sea o no
compatible con uno u otro. WebGL es mucho más potente y ofrece un mayor rendimiento,
pero no está todavía disponible en todos los navegadores. Los más antiguos, usarán canvas.

Luego encontramos los valores de anchura y altura del juego. Pueden ser fijos como en esta
ocasión, aunque Phaser también puede hacer un escalado en función del dispositivo.

width: 800,
height: 500,

http://desarrolloweb.com/manuales/phaser3 Página 7 de 88
A continuación encontramos una declaración de todas las escenas que componen el juego.
Cada escena puede funcionar de manera más o menos autónoma y, por supuesto, podemos
compartir datos y enviar mensajes de una a otra.

scene: [Game],

De momento tenemos una única escena en el juego, que será suficiente para comenzar. Como
puedes ver, las escenas se entregan en un array. En ese array colocamos cada una de las clases
que tienen nuestras escenas. En este caso enviamos en el array una casilla llamada "Game",
que es la clase importada en la primera línea.

También es muy interesante la declaración de las físicas del juego,

physics: {
default: 'arcade',
arcade: {
gravity: { y: 400 },
debug: false
}
}

En ella estamos indicando que la física será por defecto "arcade", que es la más fácil para
comenzar a desarrollar nuestro juego. Arcade incluye movimiento, colisiones, gravedad y en
general todo lo que necesitarías para un juego de plataformas. De todos modos, Phaser admite
hasta 4 tipos distintos de física.

La gravedad en nuestro caso la hemos ajustado a 400, que será el empuje de los elementos
hacia el suelo, en la vertical. A mayor valor, más rápido caen los objetos al suelo, aunque la
velocidad también dependerá de la otros factores, como un hipotético desplazamiento hacia
abajo, la fuerza de un salto, etc.

Por último tenemos la creación del juego propiamente dicha, que recibe simplemente el objeto
de configuración del mismo.

var game = new Phaser.Game(config);

Con esto arrancará el juego, mostrando la escena que tengamos definida en la primera casilla
del array "scenes" que hemos entregado en la configuración.

Una escena sencilla

Para poder ver algo, y comprobar que el juego está funcionando, vamos a definir una escena
rápidamente, aunque solamente con un par de imágenes estáticas por el momento.

export class Game extends Phaser.Scene {

constructor() {

http://desarrolloweb.com/manuales/phaser3 Página 8 de 88
super({ key: 'game' });
}

preload() {
this.load.image('background', 'images/background.png');
this.load.image('gameover', 'images/gameover.png');
}

create() {
this.add.image(410, 250, 'background');
this.gameoverImage = this.add.image(400, 90, 'gameover');
}

Vamos comentando cada parte de esta clase para que lo entendamos mejor.

export class Game extends Phaser.Scene {

Aquí usamos "export" para que esta clase se pueda conocer desde fuera. Además creamos una
nueva clase que se llama "Game" que extiende por herencia la clase Phaser.Scene.

Phaser.Scene contiene una escena básica y nosotros la extendemos para darle la funcionalidad
que requiere nuestro juego.

constructor() {
super({ key: 'game' });
}

El constructor hace una llamada al constructor de la clase padre, pasando el nombre de la


escena. Este nombre nos servirá para referirnos a la escena siempre que sea necesario, por
ejemplo para escuchar mensajes, para cambiar la escena activa, etc.

preload() {
this.load.image('background', 'images/background.png');
this.load.image('gameover', 'images/gameover.png');
}

El método preload es uno de los métodos básicos del ciclo de vida de una escena. En él
podemos hacer la precarga de todos los elementos que requiere esta escena para funcionar,
como imágenes, sonidos, etc. Incluso podemos hacer la precarga de los elementos que van a
requerir otras escenas más adelante, así evitamos esperas durante el juego, porque tendremos
cargadas todas las cosas que serán necesarias.

Es importante darse cuenta que cada uno de los recursos cargados tiene un nombre. Por
ejemplo en este caso tenemos dos imágenes. A la primera la hemos llamada "background" y a
la segunda "gameover". Estos nombres son únicos y podemos acceder a los recursos asociados
a lo largo de todo el juego.

create() {
this.add.image(410, 250, 'background');

http://desarrolloweb.com/manuales/phaser3 Página 9 de 88
this.gameoverImage = this.add.image(400, 90, 'gameover');
}

El método create se ejecuta cuando ya se han cargado todos los elementos y tiene el código
necesario para crear la escena. En este método se colocarán todos los elementos necesarios,
como el jugador, las plataformas, enemigos, decorados, etc.

En nuestro caso simplemente hemos agregado dos imágenes. Fíjate que algunas ocasiones las
imágenes las tenemos que almacenar en propiedades del objeto, porque así podremos
referirnos a ellas más adelante, mostrarlas, ocultarlas, cambiar su posición, definir
colisiones.... De hecho, lo normal es justamente que las guardemos como propiedades del
objeto escena, porque generalmente necesitamos acceder a ellas para muchas cosas. Sin
embargo, hay elementos como el fondo que quizás no hace falta hacer nada con él, porque
siempre va a estar ahí y por eso simplemente lo hemos añadido y punto.

Sobre los elementos hay que comentar que se colocan en una posición, que tiene como eje de
coordenadas la esquina superior izquierda. Pero el eje de referencia que posicionamos es el
centro de la imagen. Por ejemplo, en el caso de la imagen de "gameover" estamos colocándola
para que el centro de esta imagen se sitúe en el punto 400x90 con respecto a la esquina
superior izquierda. Como el juego mide 800 x 500, la imagen aparecerá centrada, y bastante
cerca del borde superior.

Tal cual está este proyecto observaríamos la imagen del fondo y encima otra imagen con el
motivo del "game over".

Archivos y carpetas hasta el momento

Para que puedas verificar la marcha de tu trabajo, puedes ejecutar tu juego ahora mismo. Pero
claro, necesitarás las imágenes y que estén en la carpeta correcta. Para tu referencia este es el

http://desarrolloweb.com/manuales/phaser3 Página 10 de 88
árbol de archivos y carpetas hasta el momento.

En este enlace de Github te hemos dejado el código y las imágenes del juego, tal como lo hemos
montado hasta este punto.

En el siguiente artículo profundizaremos en la escena del juego, para aportarle más elementos
y, por supuesto, algo de interactividad.

Videotutorial de Phaser

Te presentamos un videotutrial de Phaser 3 que hemos publicado en Youtube, que cubre los
tres primeros capítulos del Manual de Phaser, en los que crearemos un juego, una plataforma
que podremos mover con los cursores del ratón y una bola para hacerla botar sobre la
plataforma. Algo sencillo pero jugable!, que te servirá para reforzar las explicaciones de estos
artículos.

Para ver este vídeo es necesario visitar el artículo original en:


https://desarrolloweb.com/articulos/introduccion-phaser

Este artículo es obra de Miguel Angel Alvarez


Fue publicado / actualizado en 03/09/2020
Disponible online en https://desarrolloweb.com/articulos/introduccion-phaser

Ciclo de vida de una escena de Phaser


Cómo es el ciclo de vida de una escena con el motor de juegos Javascript Phaser, con sus
principales métodos para realizar sus funciones básicas: inicialización. precarga, creación y
update.

http://desarrolloweb.com/manuales/phaser3 Página 11 de 88
En el anterior artículo pudimos entender cómo se realiza un juego HTML5 usando el motor
Javascript Phaser. Vimos cómo iniciar un proyecto, como instalar el framework y cómo crear
un juego con una escena simple.

Lo cierto es que hasta el momento no hemos hecho gran cosa, por lo que sería mucho decir que
hemos construido un juego, sin embargo en este artículo vamos a comenzar a aportar algo de
interactividad, por lo que nuestro ejemplo mejorará un poco. Lo haremos a partir de la
programación de una escena y la implementación de los métodos del ciclo de vida.

Nota: En todo momento vamos a usar el modelo de creación de escenas basado en una clase
ES6, sin embargo, conviene decir que Phaser 3 hereda un método de creación de escena
basado en un objeto, que es el que se usaba en Phaser 2, que si lo prefieres podrías usar
también.

Constructor

El constructor es un método que resume las tareas de inicialización de los objetos. En Phaser
es el lugar adecuado para colocar código de configuración de la escena, que se ejecutará una
única vez.

Lo más básico es asignarle un nombre a la escena, como vimos en el artículo anterior.

constructor() {
super({ key: 'game' });
}

Pero podríamos agregar más configuración a la escena como por ejemplo si está activa desde el
principio, si tiene una física particular, las cámaras y mucho más.

Método init()

El método init() sirve para realizar tareas de inicialización que se deben ejecutar cada vez que
la escena se pone en marcha.

http://desarrolloweb.com/manuales/phaser3 Página 12 de 88
Una escena puede arrancarse y detenerse cualquier número de veces. En el arranque inicial o
en cualquier sucesivo rearranque se ejecutará el código que tengamos en init().

Nota: Una escena puede pausarse y reanudar su ejecución. En esos casos no se ejecutaría el
método init(), porque la escena solamente se detuvo unos instantes. Este por ejemplo sería
un hipotético control de pausar el juego. Los casos en los que se arrancará de nuevo la
escena, ejecutando el método init(), y otros métodos que le siguen, son aquellos en los que
la escena se aborte completamente, por ejemplo cuando se inicia de nuevo el juego porque
el jugador perdió la partida. Esto lo vermos con detalle más adelante, porque es materia
más adecuada de tratar en el momento en el que tengamos varias escenas en el juego.

Método preload()

Después del método init() se ejecutará el código del método preload(). En este método
colocamos todas las prepargas de archivos que se requieran para esta escena (o también si lo
deseamos para escenas siguientes). Obviamente, si un recurso ya estaba cargado, porque ya se
había precargado en una ejecución anterior, no se vuelve a solicitar.

Vamos a modificar un poco este método con respecto al que teníamos en el artículo anterior,
para colocarle una nueva imagen que vamos a precargar.

preload() {
this.load.image('background', 'images/background.png');
this.load.image('gameover', 'images/gameover.png');
this.load.image('platform', 'images/platform.png');
}

Hemos introducido una nueva imagen, a la que hemos asignado el identificador "platform". Es
una plataforma que nosotros vamos a mover con el teclado, como en el clásico juego de los
años 80, "Arkanoid".

Todos los recursos precargados se obtienen del servidor. Hasta el momento que no se han
cargado completamente, y no se disponen de todos listos para usarse, la escena no continúa
ejecutando los métodos siguientes.

Solo para dejarlo muy claro, que hayas precargado un recurso no significa que se vaya a ver
todavía en ningún lugar. El siguiente método nos servirá para eso.

Método create()

El flujo de ejecución de la escena continúa con el método create(). Este sirve para componer
todo el escenario y los actores que forman la escena.

En este método iremos colocando imágenes, asignando comportamientos, definiendo


colisiones, animaciones y cosas similares.

Este era el código del método hasta este punto.

http://desarrolloweb.com/manuales/phaser3 Página 13 de 88
create() {
this.add.image(410, 250, 'background');
this.gameoverImage = this.add.image(400, 90, 'gameover');
}

Ahora le vamos a ir agregando más líneas para ir añadiendo la plataforma. Pero antes vamos a
hacer que la imagen de "game over" desaparezca, por motivos obvios. Esto lo conseguimos con
la propiedad "visible" del objeto image.

this.gameoverImage.visible = false;

Ahora agregamos la plataforma en la parte inferior de la página. Vamos a comenzar con un


código como este para ver lo que pasa.

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

Si te fijas, la imagen de la plataforma aparece en la parte de abajo y se cae nada más arrancar,
como consecuencia de la gravedad. Nuestra plataforma debería quedarse en el sitio, por lo que
la gravedad no le debería afectar. Esto lo conseguimos con la siguiente línea:

this.platform.body.allowGravity = false;

Por último dentro del método create vamos a incorporar una línea extra, que nos permite
acceder al estado de las teclas del cursor.

this.cursors = this.input.keyboard.createCursorKeys();

Estas teclas las vamos a usar luego para mover la plataforma. En este caso solamente hemos
creado un objeto "cursors" que nos dirá si estas teclas se encuentran pulsadas en un momento
dado.

Método update()

Este método es el corazón del juego. Es un método que se ejecuta constantemente y nos
permite especificar el código que debe estar pendiente de las acciones del usuario.

Básicamente aquí vamos a preguntarnos si el usuario ha pulsado uno de los botones del cursor,
en cuyo caso reaccionaremos de distintas maneras.

update() {
if (this.cursors.left.isDown) {
this.platform.setVelocityX(-500);
}
else if (this.cursors.right.isDown) {
this.platform.setVelocityX(500);
}
else {

http://desarrolloweb.com/manuales/phaser3 Página 14 de 88
this.platform.setVelocityX(0);
}
}

El método update se ejecuta muy de seguido, muchas veces por segundo. Entonces le
preguntamos si el usuario tiene pulsado la tecla izquierda del cursor. En ese caso le asignamos
una velocidad a la plataforma de valor -500, en el eje de las "x", lo que hace que se mueva en la
dirección de la flecha pulsada. En el caso que la tecla de la derecha esté pulsada, entonces la
velocidad en el eje de las "x" será de 500.

El valor de la velocidad es como el de la gravedad, a mayor valor más rápido se mueve.

En caso que no se esté pulsando ni izquierda ni derecha, entonces la velocidad en el eje de las
"x" se pone a cero.

Con esto ya puedes ejecutar el juego tal como lo tenemos ahora. Verás como las teclas del
cursor, izquierda y derecha, hacen que se mueva la plataforma.

El método update() se ejecutará constantemente... muchas veces por segundo, con ello se
consigue que los juegos sean muy fluidos, porque están en constante actualización de sus
elementos. Como hemos colocado el control de las teclas del cursor en el método update(), el
juego responderá constantemente a los cursores y así podremos mover la plataforma con el
teclado mientras la escena esté activa.

Ten en cuenta que el foco de la aplicación tiene que estar en el elemento canvas de la
página, es decir, en el lienzo del juego. Asi que, si no hacen nada las teclas del cursor,
simplemente haz clic encima del juego y prueba a mover de nuevo la plataforma. No
obstante, Phaser se suele encargar de poner el foco en el juego al cargar la página, por lo
que no debería darte problema este punto.

Existen métodos de control de ese flujo de ejecución, como cuando una escena se pausa o se
detiene, en cuyo caso deja de producirse ese bucle de ejecución del update(). Más adelante
explicaremos cómo podemos parar las escenas, movernos entre unas escenas y otras y cosas
similares.

Con lo que hemos hecho hasta el momento, hemos conseguido una pequeña mejora al juego,
todavía básica pero bien representativa, porque ahora es capaz de atender las acciones del
usuario.

Todavía no es muy espectacular, pero ya podemos ver algo más y usar el cursor del teclado
para mover un elemento por la pantalla. Tal como está el juego verás algo como esto:

http://desarrolloweb.com/manuales/phaser3 Página 15 de 88
Código completo de la escena

Para que quede constancia, aquí encuentras el código completo de la escena que hemos venido
desarrollando en esta práctica.

export class Game extends Phaser.Scene {

constructor() {
super({ key: 'game' });
}

preload() {
this.load.image('background', 'images/background.png');
this.load.image('gameover', 'images/gameover.png');
this.load.image('platform', 'images/platform.png');
}

create() {
this.add.image(410, 250, 'background');
this.gameoverImage = this.add.image(400, 90, 'gameover');
this.gameoverImage.visible = false;

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


this.platform.body.allowGravity = false;

this.cursors = this.input.keyboard.createCursorKeys();
}

update() {
if (this.cursors.left.isDown) {
this.platform.setVelocityX(-500);
}
else if (this.cursors.right.isDown) {
this.platform.setVelocityX(500);
}
else {
this.platform.setVelocityX(0);
}
}

El código del juego completo y las imágenes que hemos usado hasta ahora, lo puedes ver en
este enlace a GitHub.

En el próximo artículo veremos cómo insertar nuevos actores en el juego, como una bola que
rebote por la pantalla y en la plataforma, para que nuestra práctica vaya tomando algo más de
sentido. De esa manera podremos comenzar a trabajar con el sistema de físicas de Phaser, para
detectar colisiones conseguir que rebote la bola.

http://desarrolloweb.com/manuales/phaser3 Página 16 de 88
Este artículo es obra de Miguel Angel Alvarez
Fue publicado / actualizado en 04/09/2020
Disponible online en https://desarrolloweb.com/articulos/ciclo-vida-escena-phaser

http://desarrolloweb.com/manuales/phaser3 Página 17 de 88
Trabajo con el sistema de físicas en Phaser

En los siguientes artículos vamos a abordar varias prácticas con el sistema de físicas del
framework, tocando temas como la gravedad, las colisiones, la velocidad y cosas similares.

Colisiones y rebotes en Phaser


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.

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

http://desarrolloweb.com/manuales/phaser3 Página 18 de 88
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

http://desarrolloweb.com/manuales/phaser3 Página 19 de 88
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);

http://desarrolloweb.com/manuales/phaser3 Página 20 de 88
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

http://desarrolloweb.com/manuales/phaser3 Página 21 de 88
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.

http://desarrolloweb.com/manuales/phaser3 Página 22 de 88
Este artículo es obra de Miguel Angel Alvarez
Fue publicado / actualizado en 10/09/2020
Disponible online en https://desarrolloweb.com/articulos/colisiones-rebotes-phaser

Colisiones con comportamientos personalizados en Phaser


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:

1. Objeto de colisión 1: el primer objeto sobre el que se configura la colisión.

http://desarrolloweb.com/manuales/phaser3 Página 23 de 88
2. Objeto de colisión 2: el segundo objeto implicado en la colisión.
3. 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.
4. 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.
5. 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() {

http://desarrolloweb.com/manuales/phaser3 Página 24 de 88
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);
}

http://desarrolloweb.com/manuales/phaser3 Página 25 de 88
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.

Para ver este vídeo es necesario visitar el artículo original en:


https://desarrolloweb.com/articulos/colisiones-comportamientos-personalizados-phaser

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

http://desarrolloweb.com/manuales/phaser3 Página 26 de 88
plataforma donde impacte.

Este artículo es obra de Miguel Angel Alvarez


Fue publicado / actualizado en 17/09/2020
Disponible online en https://desarrolloweb.com/articulos/colisiones-comportamientos-
personalizados-phaser

Posición, velocidad y gravedad en juegos con Phaser


En este artículo vamos a modificar de manera dinámica la velocidad del movimiento de actores
del juego atendiendo a su posición. Cambiaremos también la fuerza de la gravedad.

El objetivo final de este juego es realizar un clon del Arkanoid, o el muro, que era el nombre de
otro popular juego en España. En este juego la pelota va rompiendo ladrillos y va pasando por
diversas pantallas cuando los ha roto todos, ganando poderes, etc.

Obviamente, nos queda bastante trabajo por delante para conseguir acercarnos al objetivo,
pero vamos bastante bien. En este artículo nos acercaremos un poco más al objetivo buscado,
creando las condiciones adecuadas para la bola y la plataforma, de modo que se comporten
como necesitamos para cubrir los aspectos del juego.

Básicamente nos vamos a encargar de estas cosas:

1. Manipular la dirección de la bola cuando se producen las colisiones, para que el usuario
la pueda dirigir hacia donde desea, haciendo que impacte más a la derecha o la
izquierda de la plataforma.
2. Ajustar la gravedad del juego, para anularla, ya que no la vamos a necesitar realmente.
3. Implementar la posición inicial de la bola, que debería comenzar pegada a la
plataforma, hasta que el usuario pulsa la tecla para que salga disparada.

Modificar la dirección y velocidad de la bola

Vamos a conseguir un comportamiento más avanzado en el rebote de la bola, parecido al del


clásico Arkanoid.
http://desarrolloweb.com/manuales/phaser3 Página 27 de 88
No sé si recuerdas que la bola se orienta hacia el lugar donde había impactado con la
plataforma. Conseguir este efecto es fácil, pero para ello tenemos que volver sobre un punto
que ya vimos de pasada en el artículo anterior, que permite analizar la posición de los
elementos en el juego.

Averiguar la posición de los elementos del juego

Cada uno de los objetos de la escena en Phaser tiene unas propiedades para saber su posición.
Esa posición la podemos obtener con las propiedades "x" e "y" del objeto que nos interesa
saber dónde se encuentra.

Nos interesan las posiciones en la horizontal, por tanto, this.ball.x nos dirá la posición de la
bola y this.platform la de la plataforma. Si a la posición de la bola le restamos la posición de la
plataforma y nos salen valores positivos es que la bola ha impactado en la parte derecha de la
plataforma. Si salen valores negativos, es que la bola impactó más a la izquierda y si da cero es
que la bola se chocó en el mismo medio.

Una vez sabemos la posición relativa de ambos elementos, podemos modificar la velocidad de
la bola a la izquierda o la derecha.

En código este comportamiento podría ser más o menos así.

platformImpact(ball, platform) {
this.score++;
this.scoreText.setText('PUNTOS: ' + this.score);
let relativeImpact = ball.x - platform.x;
if(relativeImpact > 0) {
console.log('derecha!');
ball.setVelocityX(10 * relativeImpact);
} else if(relativeImpact < 0) {
console.log('izquierda!');
ball.setVelocityX(10 * relativeImpact);
} else {
console.log('centro!!');
ball.setVelocityX(Phaser.Math.Between(-10, 10))
}
}

Como puedes ver, los métodos callback de la colisión reciben dos parámetros, que son los
objetos involucrados en el choque. Podemos usarlos por facilidad para hacer nuestros cálculos,
pero como estamos en un método del objeto, también podríamos haber accedido a estos
mismos objetos con this.ball o this.platform.

Ahora nuestra bola cambia el sentido de su bote, según donde haya tocado la plataforma,
yendo en una trayectoria más o menos inclinada dependiendo de dónde impactó.

El código hasta el momento lo puedes ver en Github. https://github.com/deswebcom/ball-


game-phaser/tree/Comportamiento_colisiones

Eliminar la gravedad del juego

Ahora vamos a hacer un comportamiento ligeramente distinto, que se va a parecer un poco

http://desarrolloweb.com/manuales/phaser3 Página 28 de 88
más al juego que intentamos emular, el Arkanoid.

Si lo recuerdas, el Arkanoid comenzaba con la bola pegada a la plataforma y cuando pulsabas


un botón, entonces se despegaba y comenzaba a rebotar por la pantalla y romper ladrillos. Las
modificaciones que queremos hacer para conseguirlo implican tres porciones de código.

Configuración

El comportamiento del Arkanoid será más fácil si no tenemos gravedad en el juego, ya que si
existe gravedad la bola caerá siempre hacia abajo.

Así que vamos a cambiar la configuración inicial del juego para que sea como esta:

const config = {
type: Phaser.AUTO,
width: 800,
height: 500,
scene: [Game],
physics: {
default: 'arcade',
arcade: {
debug: false
}
}
}

Simplemente hemos quitado la configuración de la gravedad.

Forzar el inicio de la bola pegada a la plataforma

Ahora podemos hacer que la bola aparezca pegada en el inicio a la plataforma. Esto se consigue
simplemente colocándola de inicio más cerca de la plataforma. Sin embargo, esta modificación
implica varios cambios en el código de la escena principal del juego.

Método create()

Comenzamos viendo las configuraciones nuevas en el método create() de la escena del juego.

this.ball = this.physics.add.image(385, 430, 'ball');

Recuerda que en el método create() la plataforma estaba en la posición (400, 460), por lo que
la bola ha quedado bastante próxima.

Además vamos a asociar un dato a la bola, para indicar que está pegada a la plataforma:

this.ball.setData('glue', true);

Con el método setData() podemos almacenar valores en distintas claves. En este caso la clave

http://desarrolloweb.com/manuales/phaser3 Página 29 de 88
es "glue" y el valor es true. Este dato no modifica en nada el comportamiento de la bola. En
realidad tenemos que implementar nosotros este "pegamento". Para ello, tenemos que
modificar el método update(). Lo haremos enseguida, pero antes (todavía en el método
create()) tenemos que borrar unas líneas de código que hacían que la bola se moviese al
principio del juego.

let velocity = 100 * Phaser.Math.Between(1.3, 2);


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

Esas líneas las debes borrar del proyecto!

Además en el create() tenemos que agregar una línea que modifica el comportamiento de la
plataforma, para que no se salga de los bordes.

this.platform.setCollideWorldBounds(true);

Esta configuración lo cierto es que debería haberse introducido ya al proyecto hace mucho,
pero se me había pasado.

Método update()

Recuerda que en update() colocamos todos los comportamientos a ejecutar cuando se usaban
los cursores, moviendo la plataforma a cada lado. Ahora tendremos que comprobar si está la
bola pegada, en cuyo caso bola y plataforma deben moverse juntas.

Además, vamos a configurar la pulsación del botón de "arriba" del cursor, para que cuando se
pulse la bola se despegue y comience el juego.

Todo esto lo tenemos en este método.

update() {
if (this.cursors.left.isDown) {
this.platform.setVelocityX(-500);
if(this.ball.getData('glue')) {
this.ball.setVelocityX(-500);
}
}
else if (this.cursors.right.isDown) {
this.platform.setVelocityX(500);
if (this.ball.getData('glue')) {
this.ball.setVelocityX(500);
}
}
else {
this.platform.setVelocityX(0);
if (this.ball.getData('glue')) {
this.ball.setVelocityX(0);
}
}

if (this.ball.y > 500) {

http://desarrolloweb.com/manuales/phaser3 Página 30 de 88
console.log('fin');
this.gameoverImage.visible = true;
this.scene.pause();
}

if (this.cursors.up.isDown) {
if (this.ball.getData('glue')) {
this.ball.setVelocity(-75, -300);
this.ball.setData('glue', false);
}
}
}

Básicamente, para cada pulsación se comprueba si estaba la bola pegada. Si lo estaba hacemos
las acciones correspondientes.

Cuando se pulsa la flecha de arriba del cursor se le asigna una velocidad a la bola y con ello
comienza a moverse. Además, en ese momento se modifica el dato "glue", para dejarlo a false y
así eliminar el "pegamento" que la unía a la plataforma.

Conclusión

Con estas modificaciones el proyecto ha quedado como puedes ver en el siguiente enlace de
Github.

El juego ya tiene bastante sentido y cierta jugabilidad. Podemos comenzar a sentirnos


orgullosos de lo que hemos conseguido sin demasiado esfuerzo, ¿qué os parece?

Pero nos queda una parte fundamental, que es colocar los ladrillos que se deben romper con
impactos de la bola. Esto nos dará pie a aprender nuevas cosas como es la creación de grupos
de elementos en Phaser.

Este artículo es obra de Miguel Angel Alvarez


Fue publicado / actualizado en 30/09/2020
Disponible online en https://desarrolloweb.com/articulos/posicion-velocidad-gravedad-
phaser

Grupos de elementos en 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.

http://desarrolloweb.com/manuales/phaser3 Página 31 de 88
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().

http://desarrolloweb.com/manuales/phaser3 Página 32 de 88
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:

key: hemos colocado un array con todos los identificadores de las imágenes de los
bloques previamente cargadas.
frameQuantity: este es el número de elementos para cada uno de las llaves del grupo. Es
decir, colcaremos 10 bloques azules, 10 naranjas, 10 verdes y 10 negros.
gridAlign: este método permite posicionar los elementos en una rejilla, muy úti para
nuestra primera pantalla. Esta rejilla tiene los siguientes valores:
width: La anchura en columnas de la rejilla
height: La altura en filas de la rejilla
cellWidth: Este es el tamaño de la celda de rejilla de anchura, en píxeles. No tiene que
ver con el tamaño de lo que metas dentro. Si lo que metes es menor, como es nuestro
caso, simplemente te sobrará algo de espacio.
cellHeight: Los píxeles de altura de la celda de la rejilla
x: La posición del primer elemento de la rejilla, en la horizontal
y: La posición del primer elemento de la rejilla, en la vertical.

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');

http://desarrolloweb.com/manuales/phaser3 Página 33 de 88
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

http://desarrolloweb.com/manuales/phaser3 Página 34 de 88
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.

http://desarrolloweb.com/manuales/phaser3 Página 35 de 88
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.

Para ver este vídeo es necesario visitar el artículo original en:


https://desarrolloweb.com/articulos/gupos-elementos-phaser

Este artículo es obra de Miguel Angel Alvarez


Fue publicado / actualizado en 21/10/2020
Disponible online en https://desarrolloweb.com/articulos/gupos-elementos-phaser

http://desarrolloweb.com/manuales/phaser3 Página 36 de 88
Mejorar los juegos trabajando con escenas

Ahora vamos a abordar el trabajo con las escenas del juego, manteniendo varias escenas para
dividir las partes bien diferenciadas del juego.

Gestión de escenas en Juegos Javascript con Phaser 3


En este artículo aprenderás a trabajar con varias escenas en un mismo juego Javascript con
Phaser, realizando acciones como intercambiar las escenas, pararlas o reiniciarlas.

En un juego habitualmente tenemos varias escenas que se pueden desarrollar de manera


independiente. Phaser ayuda mucho a la hora de crear escenas y permite realizar el cambio de
una escena a otra de una manera sencilla.

En este artículo del Manual de Phaser aprenderemos a manejar escenas, creando nuevas
escenas en nuestro juego y cambiando de unas a otras.

Qué es una escena

Una escena es como una pantalla o una fase del juego independiente, que mantiene su propio
flujo de ejecución también de manera independiente de otras escenas del juego.

En un juego podemos representar tantas escenas como queramos. Por ejemplo podemos tener
una primera escena para la pantalla inicial, con la carátula del juego y el botón de comenzar.
También podemos tener una escena con el desarrollo principal del juego y otra con las
opciones, o la pantalla de game over.

En fin, cada vez que veamos que una parte del juego es muy distinta a otra que estábamos
desarrollando, es una buena idea programar una escena independiente. Esto facilita el
mantenimiento del juego, porque tenemos diversas partes en archivos independientes, y no se
nos mezcla todo en una gran escena. Aunque también agrega algo de dificultad por el hecho de

http://desarrolloweb.com/manuales/phaser3 Página 37 de 88
tener que cambiar de una escena a otra, pero esta parte Phaser la hace bastante sencilla, por lo
que enseguida lo tendremos todo claro.

Phaser puede mantener varias escenas, pasar de una a otra, detenerlas, pausarlas, correr varias
escenas al mismo tiempo, etc. Todas las acciones con las escenas están dentro del propio API
de una escena, dentro de una propiedad llamada "scene". De hecho, ya habíamos usado este
API para pausar una escena cuando mostrábamos la pantalla de Game over. ¿Recuerdas?

this.scene.pause()

Cómo implementar diversas escenas en un juego

Cuando realizamos el objeto de configuración para crear un nuevo juego teníamos un array en
una propiedad llamada "scene" donde por el momento sólo habíamos colocado una escena, el
propio juego.

Sin embargo ahora vamos a agregar nuevas escenas y ese array se entregará con nuevos
elementos.

const config = {
type: Phaser.AUTO,
width: 800,
height: 500,
scene: [Game, Gameover, Congratulations],
physics: {
default: 'arcade',
arcade: {
debug: false
}
}
}

Cada escena la hemos construido en una clase distinta, así mantenemos cada cosa en su sitio y
nos ayuda a manejarnos mejor por las pantallas del programa.

Fíjate que ahora, aparte del juego (clase Game), vamos a tener dos escenas nuevas, una con la
clase Gameover y otra con la clase Congratulations. No hace falta ser muy imaginativo para
suponer qué van a contener esas escenas ¿no?

Estas escenas las vamos a implementar en archivos distintos. Como el juego ya tiene 3 escenas
es una buena idea crear una carpeta donde coloquemos todos los archivos de éstas.

Así pues, la estructura de carpetas y archivos del juego ahora pasa a ser así.

http://desarrolloweb.com/manuales/phaser3 Página 38 de 88
Por supuesto, dado que cada escena está en un archivo aparte, las vamos a tener que importar
antes de poder agregarlas al array de escenas del juego. Por tanto, el código del archivo index.js
quedará así:

import { Game } from './scenes/game.js';


import { Congratulations } from './scenes/congratulations.js';
import { Gameover } from './scenes/gameover.js';

const config = {
type: Phaser.AUTO,
width: 800,
height: 500,
scene: [Game, Gameover, Congratulations],
physics: {
default: 'arcade',
arcade: {
debug: false
}
}
}

var game = new Phaser.Game(config);

Ten en cuenta que la escena que hemos colocado como primera en el array será la escena que
comenzará el juego.

Cada escena la implementaremos de manera muy similar a lo que hemos aprendido para la
escena principal, con los típicos métodos preload(), create() y todos los que necesites.

Componentes comunes

La escena de "congratulations" es muy parecida a la escena de "gameover". De hecho es casi la


misma, solo que va a cambiar la imagen que se muestra en una y otra. Lo cierto es que las
podríamos haber implementado usando una única escena y no habría estado nada mal, pero
así podemos tener más escenas en el juego y encontrar soluciones para solucinar los casos en
los que tenemos dos o más escenas que usan los mismos elementos.

http://desarrolloweb.com/manuales/phaser3 Página 39 de 88
En nuestro caso, las nuevas escenas de nuestro juego van a tener un elemento en común, que
es un botón que sirve para reiniciar el juego. Como sabes, no es ideal hacer copy paste del
código del botón, así que vamos a ver cómo crear un componente botón que podríamos usar en
ambas escenas.

Este componente lo he creado en un archivo independiente, para que sea un módulo Javascript
que puedo usar desde cualquier escena que lo necesite. De momento solo tenemos un
componente, pero podrían venir más, por lo que crearemos una carpeta para ellos. Por tanto,
la estructura del proyecto cambia una vez más:

La idea de este componente es que encapsule toda la complejidad de hacer un botón, tanto la
parte visual como su funcionalidad al hacer clic sobre él. Así que, por facilidad, hemos decidido
implementarlo en una clase.

Cuando se instancia el botón necesitamos conocer la escena donde se va a mostrar, porque


luego la vamos a necesitar en varios métodos diferentes. Comenzamos entonces viendo cómo
sería el constructor.

export class RestartButton {


constructor(scene) {
this.relatedScene = scene;
}

// otros métodos de la clase


}

Es importante ver cómo dentro del constructor recibo la escena en la que estoy y la guardo en
una propiedad del componente llamada "relatedScene".

Ahora nuestro botón tendrá los métodos necesarios para hacer las operativas: Precarga de la
imagen del botón, método preload() Crear la imagen en la pantalla, método create()

http://desarrolloweb.com/manuales/phaser3 Página 40 de 88
Implementar el reinicio del juego cuando se pulsa.

Sprites del botón

Otra gracia de este botón es que lo hemos implementado mediante sprites!. Todavía no
habíamos visto los sprites y son muy útiles dentro de Phaser. Este es un ejemplo muy
elemental, que no muestra toda la potencia de los sprites ni de lejos, pero nos sirve para
introducirnos en ellos.

A falta de introducciones mayores, los sprites son distintos fotogramas de una imagen, que si
los pasamos en secuencia producen animaciones, como el movimiento de un personaje. En
nuestro botón tenemos algo tan simple como un par de estados, uno el normal y otro que se
activará al pasar el ratón por encima.

Los sprites se cargan en el método de preload, que tendrá esta forma:

preload() {
this.relatedScene.load.spritesheet('button', 'images/restart.png', { frameWidth: 190, frameHeight: 49 });
}

Como puedes ver, usamos this.relatedScene para precargar algo en la escena, ya que la
propiedad "relatedScene" es donde había guardado la escena relacionada con esta instancia del
componente.

Luego usamos el método load.spritesheet() que recibe varios parámetros:

El identificado que vamos a darle a este sprite.


La imagen donde se encuentran las distintas imágenes, que simplemente tendrá una
secuencia de las distintas alternativas de vistas del botón.
Las dimensiones del sprite (cada imagen suelta)

Ahora veamos cómo se añaden los sprites a una escena, algo que se haría en el método
create().

create() {
this.startButton = this.relatedScene.add.sprite(400, 230, 'button').setInteractive();
}

Un sprite se añade de manera similar a una imagen y, de entrada, mostrará la primera imagen
disponible del sprite. Luego las podemos cambiar.

El método setInteractive() simplemente sirve para que podamos hacer el botón interactivo, con
lo que responderá a diversos eventos del usuario.

Esos eventos los podemos crear justo a continuación, así que el método create nos quedaría
realmente así.

http://desarrolloweb.com/manuales/phaser3 Página 41 de 88
create() {
this.startButton = this.relatedScene.add.sprite(400, 230, 'button').setInteractive();

this.startButton.on('pointerover', () => {
this.startButton.setFrame(1);
});
this.startButton.on('pointerout', () => {
this.startButton.setFrame(0);
});
this.startButton.on('pointerdown', () => {
this.relatedScene.scene.start('game');
});
}

Fíjate que gracias a setInteractive() el botón podrá responde a eventos como 'pointerover',
'pointerout' o 'pointerdown' y que para asociar los manejadores a cada evento usamos el
método on sobre el propio sprite que hemos hecho interactivo.

El manejador asociado a 'pointerover' y 'pointerout' simplemente intercambian el sprite visible


en el botón.

Por su parte, el manejador 'pointerdown' es el que se encargará de cambiar la escena, para


volver a reiniciar el juego.

Este cambio de escena se tiene que hacer por medio del método start() pasando por parámetro
el nombre de la escena a la que queremos cambiar. Fíjate que start() pertenece a un objeto
"scene" que está dentro de "relatedScene".

El método start() produce que la escena a la que nos dirigimos se reinicie, que es justamente lo
que necesitamos. Sin embargo, si fuera el caso también es posible volver a la escena anterior
recuperando el estado en el que la hubiéramos dejado, en cuyo caso usaríamos el método
switch().

Nota: Para que switch() pueda recuperar verdaderamente el estado donde se había quedado
la escena es importante que esa escena a la que volvemos no se haya detenido. Si salimos de
la escena a la que intentamos volver con un método que finalice la escena actual, como
start(), por mucho que intentemos volver con switch() la escena se habrá tenido que
reiniciar de nuevo. Es decir, si hacemos un start() para irnos a otra escena, la escena en la
que estamos se detendrá, con lo que si volvemos a ella no la veremos en el estado que
estaba antes.

Código de las escenas nuevas

Ahora el código de las escenas "gameover" y "congratulations" ha quedado muy simple, ya que
la mayor parte del trabajo lo hemos delegado en el botón que acabamos de separar a un
componente.

Nota: Siempre es bueno separar código a otros componentes, incluso aunque esos
componentes solamente los vayamos a usar en una escena determinada. Así nuestros

http://desarrolloweb.com/manuales/phaser3 Página 42 de 88
archivos se mantendrán pequeños y manejables. En nuestro juego no hemos realizado esta
práctica porque estábamos comenzando, pero perfectamente podríamos haber creado un
componente para los ladrillos, otro para la plataforma, la bola, etc. Así habíamos separado
el código en varias partes y el juego principal no sería tan grande. Esta separación de código
será fundamental en el momento que las cosas comiencen a complicarse, porque manejar
escenas con cientos de líneas de código es una auténtica locura.

La escena de game over tendrá este código.

import { RestartButton } from "../components/restart-button.js";

export class Gameover extends Phaser.Scene {


constructor() {
super({ key: 'gameover' });
this.restartButton = new RestartButton(this);
}

preload() {
this.load.image('gameover', 'images/gameover.png');
this.restartButton.preload();
}

create() {
this.add.image(410, 250, 'background');
this.restartButton.create();
this.gameoverImage = this.add.image(400, 90, 'gameover');
}
}

Es importante fijarse que se le ha asignado un nombre a la escena en el constructor. Además


que en el constructor hemos instanciado el botón.

El preload y el create llaman al preload y el create del botón, realizando el trabajo necesario
para que ese botón se muestre y se le asocie la funcionalidad.

El código de la clase de congratulations quedará así.

import { RestartButton } from "../components/restart-button.js";

export class Congratulations extends Phaser.Scene {


constructor() {
super({ key: 'congratulations' });
this.restartButton = new RestartButton(this);
}

preload() {
this.load.image('congratulations', 'images/congratulations.png');
this.restartButton.preload();
}

create() {
this.add.image(410, 250, 'background');
this.restartButton.create();
this.congratsImage = this.add.image(400, 90, 'congratulations');
}
}

Es prácticamente lo mismo, solamente cambia el identificador de la escena, que siempre se

http://desarrolloweb.com/manuales/phaser3 Página 43 de 88
tiene que dar único en el constructor y la imagen que da las felicitaciones.

Cómo pasar de una escena a otra en Phaser

Ahora vamos a centrarnos en el flujo de pasar de una escena a otra. No tiene mucho misterio y
algo ya hemos visto en el propio botón anterior.

En la clase Game hemos creado un método que sirve para finalizar el juego. Puede ser
finalizado porque has perdido o porque has completado la pantalla rompiendo todos los
ladrillos.

endGame(completed = false) {
if(! completed) {
this.scene.start('gameover');
} else {
this.scene.start('congratulations');
}
}

En ambos casos lo que hacemos es iniciar una nueva escena. Fíjate que la escena se inicia con
el método start() indicando el indentificador de la escena que quieres iniciar.

Como estamos iniciando las nuevas escenas con el método start() se produce también la
parada de la escena actual, por lo tanto, si volvemos a la escena Game más adelante,
simplemente se verá reiniciada.

Ahora podemos llamar a endGame() cada vez que deseamos finalizar el juego.

Por ejemplo, cuando habíamos encontrado que la bola se perdía por los límites inferiores,
llamamos a endGame() sin pasarle parámetros:

if (this.ball.y > 500 && this.ball.active) {


console.log('fin', this.ball.y, this.ball, '--');
this.endGame();
}

Y cuando el juego se acaba porque hemos roto todos los ladrillos, llamamos a endGame()
pasándole true como parámetro.

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

Conclusión

Con esto hemos terminado el juego y hemos obtenido un resultado bastante atractivo, con
relativamente poco esfuerzo y un código bastante sencillo de escribir, todo gracias a las

http://desarrolloweb.com/manuales/phaser3 Página 44 de 88
posibilidades de Phaser 3.

Para darle un toque final nos deberíamos entretenernos en darle algún sonido, algo que
haremos en el próximo artículo.

Hasta el momento tienes todo el código y las imágenes del juego en este enlace.
https://github.com/deswebcom/ball-game-phaser/tree/scenes

Este artículo es obra de Miguel Angel Alvarez


Fue publicado / actualizado en 20/11/2020
Disponible online en https://desarrolloweb.com/articulos/escenas-juegos-phaser

http://desarrolloweb.com/manuales/phaser3 Página 45 de 88
Profunduzando en las posibilidades del
framework Phaser

En este bloque vamos a profundizar en nuestro conocimiento del framework, para la


realización de juegos más impactantes, con mejor experiencia de usuario. Trabajaremos con
sonido, animación y practicaremos creando una estructura de componentes que facilite el
diseño del código y su mantenimiento. Cambiaremos los requisitos y mejoraremos el juego con
diversas funcionalidades, aportando complejidad a la práctica.

Cómo cargar y reproducir sonidos en juegos con Phaser 3


En este artículo introduciremos el manejo de audio en juegos realizados con Phaser 3. Veremos
cómo precargar un sonido, cómo agregar un audio a la escena y reproducirlo en el momento
que queramos.

A lo largo de los artículos anteriores del Manual de Phaser hemos aprendido muchas de sus
características principales, a la vez que íbamos desarrollando un juego de romper ladrillos
(breakout / Arkanoid). El juego va quedando bastante bien, pero lo cierto es que, sin unos
efectos de audio, resulta un poco soso.

Phaser 3 pone muy sencillo el uso de audio, aportando métodos simples para la carga de
sonidos y luego su reproducción. Por tanto, la práctica del presente artículo será bastante
rápida.

El trabajo con audio es muy similar al que realizamos anteriormente con las imágenes, con su
precarga, incorporación a la escena y todo eso. Solamente que los sonidos además habrá que
ponerlos a reproducir en el momento que sea necesario. Lo iremos viendo todo de manera
práctica.

Incorporación de los archivos de audio

Lo ideal es usar archivos de audio comprimido, tipo ogg o mp3, para evitar que la

http://desarrolloweb.com/manuales/phaser3 Página 46 de 88
incorporación de sonido acabe con un juego muy pesado. Phaser 3 soporta en realidad los
formatos que soporte el navegador.

Crearemos una carpeta nueva para ir incorporando todos los archivos de audio que queramos
agregar al juego. En nuestro caso la hemos llamado "sounds". Allí hemos colocado de entrada
varios archivos en formato ogg.

Precarga del audio

Ahora vamos a realizar la precarga de los archivos de audio, para que estén en la memoria del
ordenador y disponibles para usar. Esto se hace de manera muy similar a las imágenes,
indicando el identificador único que tendrá este recurso.

this.load.audio('platformimpactsample', 'sounds/platform-impact.ogg');
this.load.audio('brickimpactsample', 'sounds/brick-impact.ogg');
this.load.audio('gameoversample', 'sounds/gameover.ogg');
this.load.audio('winsample', 'sounds/you_win.ogg');
this.load.audio('startgamesample', 'sounds/start-game.ogg');

Hemos precargado audio para unas cuantas situaciones. Creo que los identificadores son
bastante descriptivos como para que los puedas entender.

Recuerda que, una vez precargado el audio está disponible a lo largo de todo el juego.

Añadir el audio a una escena

El siguiente paso es añadir el audio a la escena donde lo pretendamos usar. Se hace también de
manera similar, al menos conceptualmente, a como hacíamos con las imágenes. Solo que los
audios una vez añadidos no suenan directamente, sino que se tendrán que reproducir en el
momento adecuado.

this.platformImpactSample = this.sound.add('platformimpactsample');
this.brickImpactSample = this.sound.add('brickimpactsample');
this.gameOverSample = this.sound.add('gameoversample');
this.winSample = this.sound.add('winsample');
this.startGameSample = this.sound.add('startgamesample');

Es importante almacenar en una propiedad de la escena el audio agregado, porque ese objeto
es el que vamos a tener que usar para reproducir el sonido.

Reproducir un sonido

El último paso es invocar el método play() del sonido agregado a la escena, con eso se
reproducirá el sonido. Tan sencillo como eso!.

Solamente toca buscar los momentos donde asociar cada sonido. Por ejemplo, teníamos un
método para gestionar las colisiones de la bola contra la plataforma.

http://desarrolloweb.com/manuales/phaser3 Página 47 de 88
platformImpact(ball, platform) {
this.platformImpactSample.play();
// resto del código de este método…
}

Teníamos un método para gestionar ei impacto de la bola contra un ladrillo.

brickImpact(ball, brick) {
this.brickImpactSample.play();
// resto del código de este método...
}

El sonido de Game over lo tocamos en el momento en el que la bola se pierde por la parte de
abajo de la pantalla.

if (this.ball.y > 500 && this.ball.active) {


this.endGame();
this.gameOverSample.play();
}

El sonido de victoria lo reproducimos cuando se detecta que ya no hay más ladrillos en el


juego.

if (this.bricks.countActive() === 0) {
this.endGame(true);
this.winSample.play();
}

Por último, el sonido de inicio del juego lo tocamos cuando la bola se despega de la plataforma.

if (this.cursors.up.isDown) {
if (this.ball.getData('glue')) {
this.startGameSample.play();
this.ball.setVelocity(-60, -300);
this.ball.setData('glue', false);
}
}

Eso es todo!! realmente esta parte de la práctica es la más sencilla de todas, pero sin embargo,
el cambio en nuestro juego hace una gran diferencia.

Ahora que tenemos sonidos estamos bastante satisfechos con el resultado. Creo que es un buen
momento para enseñar el juego del breakout a nuestros amigos!!

El código completo, tal como se ha quedado después de esta práctica de inserción de sonidos,
lo podemos ver aquí. https://github.com/deswebcom/ball-game-phaser/tree/sounds

http://desarrolloweb.com/manuales/phaser3 Página 48 de 88
Sin embargo, mostrando el juego a mis hijos me he dado cuenta que les resulta un poco difícil.
Enseguida pierden la bola!! He llegado a la conclusión de que sería ideal crear un sistema para
disfrutar de varias vidas antes de que aparezca el game over. Veremos esta mejora en el
próximo artículo.

Este artículo es obra de Miguel Angel Alvarez


Fue publicado / actualizado en 14/12/2020
Disponible online en https://desarrolloweb.com/articulos/sonido-juegos-phaser

Cómo implementar varias vidas en un juego Phaser 3


En este artículo veremos cómo implementar una mejora del breakout con Phaser 3, para darle
al jugador varias vidas y permitir que la partida sea un poco más sencilla y jugable.

Hemos desarrollado un juego con Javascript y el motor Phaser 3, lo que nos ha dado pie a
aprender muchas cosas sobre el framework, a lo largo de diversos artículos en el Manual de
Phaser.

En esta ocasión vamos a seguir mejorando el juego de nuestra práctica, aportando una
facilidad importante, como es la gestión de varias vidas. A decir verdad, esta mejora resulta
más de lógica de programación que de manejo del propio framework, por lo que tu pericia
como desarrollador será lo más importante. Aunque, como sabes, al resolver los problemas
siempre se aprenden cosas nuevas, y esta vez no va a ser distinto.

Crear un componente de gestión de vidas

La lógica de nuestra escena del juego ya es lo suficientemente compleja como para parchearla
con nuevas características, agregando unas decenas de líneas de código extra aquí y allá. Es
importante que desde el principio separemos el código por responsabilidades, haciendo
componentes (o llamémosle simplemente clases) que se encargan de implementar alguna de
las áreas del programa. De hecho, este consejo no lo hemos llevado siempre a la práctica a lo
largo del desarrollo realizado hasta el momento, por no complicarnos demasiado, pero ya es
hora que nos lo tomemos en serio.

http://desarrolloweb.com/manuales/phaser3 Página 49 de 88
Así pues, toda la lógica de la gestión de las diversas vidas en el juego la vamos a sacar fuera de
la escena, a un componente nuevo en el que nos encargaremos de diversas operativas:

Llevar la cuenta de las vidas restantes del jugador


Gestionar el display de vidas restantes
Permitir restar una vida al jugador y avisar ya no había vidas disponibles para seguir
restando.

Nuestro componente se llamará LiveCounter y lo crearemos en una clase independiente, que


pondremos en la carpeta "components".

export class LiveCounter {


// Código de la clase para la gestión de vidas del jugador
}

Cuando construyamos el componente le vamos a pasar la escena, porque la necesitaremos en


diversos lugares del código. Además pasaremos también en el constructor el número de vidas
con el que queremos iniciar el juego.

constructor(scene, initialLives) {
this.relatedScene = scene;
this.initialLives = initialLives;
}

Guardaremos la escena en una propiedad del componente, para poder usarla más adelante.
Guardamos también el número de vidas iniciales con el que queremos iniciar el juego.

Crear el display de vidas

Este display, que muestra las vidas restantes, será como el del juego clásico del Arkanoid,
donde podíamos ver las vidas representadas con una miniatura de las plataformas que maneja
el jugador. Así pues, cada vida que nos quede, además de la que estamos usando en un
momento dado, requerirá una imagen en la pantalla.

http://desarrolloweb.com/manuales/phaser3 Página 50 de 88
A medida que nos quiten vidas, esas imágenes irán desapareciendo progresivamente, por lo
tanto, me interesa tenerlas en un grupo, para poder acceder a ellas en su debido momento y
eliminarlas.

Vamos a implementar la creación de ese grupo en un método create() del componente que se
encarga de mostrar el display de las vidas.

Ese método create tiene que ir creando todas las imágenes de las vidas que tengamos que
mostrar en el marcador. Recuerda que, si tenemos 3 vidas al inicio (por ejemplo), mostraría 2
vidas el marcador, porque la tercera es la que se está jugando en ese instante.

Luego me di cuenta que hubiera sido mucho más sencillo colocar simplemente el número
de vídas en un dígito de texto, porque al final se lió un poco el código para mostrar las vidas
con distintas imágenes de la plataforma, más que nada por calcular la posición donde se
van a colocar las vidas.

Como podemos iniciar el juego con un número de vidas parametrizado, tendremos que hacer
un poco de programación para saber la posición del display (a más vidas más a la izquierda
debe comenzar a colocarse las imágenes).

Usaremos las funciones de crear grupos que nos ofrece Phaser para que las cosas sean más
sencillas. El método create() tendrá esta forma.

create() {
let displacement = 60;
let firstPosition = 800 - ((this.initialLives - 1) * displacement);
this.liveImages = this.relatedScene.physics.add.staticGroup({
setScale: { x: 0.5, y: 0.5 },
key: 'platform',
frameQuantity: this.initialLives-1,
gridAlign: {
width: this.initialLives - 1,
height: 1,
cellWidth: displacement,
cellHeight: 30,
x: firstPosition,
y: 30
}
});
}

Básicamente, creo una variable displacement para indicar la cantidad de píxeles que hay entre
cada imagen de cada vida. Luego veo la posición donde se colocaría la primera imagen, que
tengo que calcular en función del número de vidas a mostrar en el display, por el
desplazamiento entre ellas. Luego realizo el grupo, que mediante su configuración permite
colocar las imágenes en los lugares correctos.

La gestión de grupos ya la vimos en un artículo anterior, cuando colocamos los ladrillos del
juego. En este caso además estamos haciendo un "setScale" para que las imágenes del grupo se
muestren con la mitad de su tamaño normal.

http://desarrolloweb.com/manuales/phaser3 Página 51 de 88
Método para restar una vida del jugador

Ahora veamos el método que hemos creado para el objeto contador de vidas, que nos permite
restarle una vida al jugador.

liveLost() {
if (this.liveImages.countActive() == 0) {
this.relatedScene.endGame();
return false;
}
let currentLiveLost = this.liveImages.getFirstAlive();
currentLiveLost.disableBody(true, true);
return true;
}

Este método hace uso del API de la clase Group de Phaser, que nos permite saber el número de
elementos que hay activos y nos permite acceder a ellos.

En un primer momento comprobamos si nos quedan vidas para restar todavía. Si no quedaban
vidas, avisamos a la escena que usa este componente, para que de por finalizado el juego.

Si aún quedaban vidas en el contador, entonces accedemos a la primera disponible que esté
viva, con getFirstAlive(). Luego hacemos que ese elemento del grupo desaparezca del juego.

Este método devuelve true si el juego está en funcionamiento y false en caso que no pudiera
quitar vida alguna, en cuyo caso el juego estaba acabado.

Hasta aquí el código de la clase LiveCounter. Ahora se trata de usarla dentro del juego.

Cómo usar el contador de vidas del juego

Veamos paso por paso cómo se debe modificar el juego para habilitar las vidas del jugador. La
mayor complicación será llevar el juego a un estado inicial cada vez que se pierda una vida y
aún tengamos otras vidas disponibles para seguir jugando.

El primer paso será crear una instancia del contador de vidas. Lo hacemos en el método init().

init() {
this.score = 0;
this.liveCounter = new LiveCounter(this, 3);
}

Esto permite que, cada vez que la escena del juego se resetea, se cree un nuevo contador de
vidas.

Método loader()

El contador de vidas usa la misma imagen de la plataforma para mostrar cada vida, por lo que
no hay que recargar nada. Sin embargo, hemos creado un nuevo sonido para cuando el usuario

http://desarrolloweb.com/manuales/phaser3 Página 52 de 88
pierde la vida.

this.load.audio('livelostsample', 'sounds/live-lost.ogg');

Recuerda que en el pasado capítulo del manual explicamos la gestión de sonidos en Phaser.

Método create()

En el método create() ahora tenemos que invocar el método create() del contador de vidas.
Para mostrar el display de las vidas.

this.liveCounter.create();

Además también añadimos el sonido de perder una vida a la escena.

this.liveLostSample = this.sound.add('livelostsample');

Cómo perder una vida

Cuando se pierde una vida, tenemos que pedirle al contador de vidas que la descuente. El
contador de vidas nos devuelve un boleano para saber si el juego no había acabado, que
usamos en un condicional para llevar el juego a un estado inicial, en el que la bola está pegada
a la plataforma.

if (this.ball.y > 500 && this.ball.active) {


let gameNotFinished = this.liveCounter.liveLost();
if (!gameNotFinished) {
this.setInitialPlatformState();
}
}

Cómo llevar el juego al estado inicial

Ahora la dificultad está en llevar el juego al estado inicial, donde la bola se encuentra pegada a
la plataforma.

setInitialPlatformState() {
this.liveLostSample.play();
this.platform.x = 400;
this.platform.y = 460;
this.ball.setVelocity(0,0);

http://desarrolloweb.com/manuales/phaser3 Página 53 de 88
this.ball.x = 385;
this.ball.y = 430;
this.ball.setData('glue', true);
}

Básicamente cambiamos las propiedades necesarias en la bola y la plataforma, para conseguir


ese estado inicial. Además reproducimos el sonido de una vida perdida.

También hay un pequeño cambio en el método que implementa el final del juego, para
conseguir que ahora se encargará también de reproducir el sonido de game over.

endGame(completed = false) {
if(! completed) {
this.gameOverSample.play();
this.scene.start('gameover');
} else {
this.scene.start('congratulations');
}
}

Conclusión

Esos son los detalles que hemos tenido que cambiar para adaptar el juego para que permita
disfrutar de varias vidas. Como has visto, se trata más de un problema de lógica del juego, que
otra cosa, que se resuelve con un poco de programación.

Lo más interesante aquí es ver cómo hemos separado la mayor parte del código a una clase
aparte, de modo que no ha sido necesario agregar excesiva complejidad a la escena. De hecho
hemos tocado mínimamente el código del juego principal y sin embargo, la mejora ha sido
importante. Este trabajo realizado para separar el código en diversas clases es esencial para
que el desarrollo pueda crecer sin llegar a volverse un caos.

Puedes ver el código de este juego, tal como lo hemos dejado en este punto, en este enlace.
https://github.com/deswebcom/ball-game-phaser/tree/lives

Estaba prácticamente decidido a dejar este proyecto por aquí, pero mi hijo me motivó para
crear distintos niveles, para que puedas pasar por pantallas diferentes, con distintas
colocaciones de los ladrillos. Esta mejora la explicaremos en el próximo artículo.

Este artículo es obra de Miguel Angel Alvarez


Fue publicado / actualizado en 13/04/2021
Disponible online en https://desarrolloweb.com/articulos/implementar-vidas-juego-
phaser

Cómo gestionar distintos niveles en un juego Phaser


Cómo gestionar distintas fases con niveles independientes en el Juego Breakout HTML5 con

http://desarrolloweb.com/manuales/phaser3 Página 54 de 88
Phaser 3. Además crearemos distintos niveles con nuevas distribuciones y tipos de ladrillos.

Qué bonito sería el juego si tuviera distintos niveles, ¿no?. Ya que hemos trabajado lo
suficiente para crear todo un sistema de romper ladrillos, con muy poquito más podemos
hacer que el juego pueda tener diversas pantallas y que puedas ir pasando de una a otra
cuando has terminado de romper todos los ladrillos. ¿Nos ponemos manos a la obra?

En esta ocasión estamos también ante un requisito que está más cercano a la programación
que a otra cosa. No aprenderemos mucho nuevo del framework Phaser 3, pero sí nos servirá
para ir ganando pericia como programadores de juegos. Todos los artículos para entender
cómo hemos llegado al estado actual en el juego los tienes en el Manual de Phaser.

Organización de las clases para generación de niveles

El reto en el que nos encontramos no es trivial. Si comenzamos a programar sin saber muy
bien cómo vamos a conseguir que la aplicación muestre distintos niveles es muy probable que
no lleguemos a la mejor solución, una que sea sencilla de entender, pero que sobre todo nos
permita agregar tantos niveles como queramos, sin que el juego en sí se vea afectado. Es decir,
que podamos escalar el juego hasta el infinito sin que aumente su complejidad, ya que agregar
nuevas pantallas sería tan simple como añadir más casillas a un array.

Para ello vamos a pensar en una estructura de clases que nos permita llegar a nuestro objetivo
de no compicarnos demasiado a medida que las pantallas aumentan.

Así pues, en nuestro juego vamos a incorporar dos elementos principales:

Un generador de niveles: que será el encargado de generar cada fase y realizar la


operación de pasar al nivel siguiente. El juego conversará con el generador de niveles,
solicitando que se cambie de nivel cuando haga falta.
Un conjunto de fases del juego: en realidad aquí tendremos "n" clases, una por cada fase
que se desee crear. En el caso que queramos implementar un nuevo nivel, simplemente
tendremos que crear una nueva clase. El generador de niveles conocerás clases que
implementan cada uno de los niveles.

http://desarrolloweb.com/manuales/phaser3 Página 55 de 88
Piensa en cada fase como un mapa de ladrillos distintos. La fase será la que cree el conjunto
de ladrillos que se van rompiendo en cada nivel. Lo que he llamado "generador de niveles"
quizás hubiera sido mejor llamarlo intercambiador de niveles, puesto que realmente lo que
realiza es la acción de controlar los distintos niveles del juego para pasar de uno a otro.

Además, como todos los niveles del juego tienen algunas funcionalidades comunes, crearemos
una clase base para las fases del juego. Cada una de las fases del juego (niveles, pantallas)
extenderá esa clase base.

Clase base para las fases del juego

Vamos a comenzar viendo la clase base que implementa las clases del juego, Para poder
entenderla te pido que tengas en mente cómo estaba hecha la escena del juego hasta este
punto, porque hay algún código de la escena que nos hemos traído para aquí.

export class Phase {


constructor(scene) {
this.relatedScene = scene;
}

configureColisions() {
this.relatedScene.physics.add.collider(this.relatedScene.ball, this.bricks, this.relatedScene.brickImpact, null, this.relatedScene);
}

isPhaseFinished() {
return (this.bricks.countActive() === 0)
}
}

Como todo componente, es necesario recibir la escena donde lo vamos a usar, guardando una
referencia en el constructor.

Hemos agregado en esta clase la configuración de las colisiones, que antes teníamos en la
escena game.js. Ya que la fase es quien posee el grupo de los ladrillos, lo normal es que ella
misma se encargue de implementar las colisiones.

Además con ello conseguimos descargar de código a la escena principal del juego, lo que
resulta ideal para un mejor mantenimiento. De hecho, gracias a estas mejoras en la escena
Game ahora tendremos que borrar todo el trabajo de generación de los ladrillos y la colisión,
que estaba en el método create().

Luego mostraremos y se comentará el detalle sobre cómo queda la escena principal para
usar los distintos niveles. Además, al final del artículo encuentras el enlace para ver el
código del proyecto hasta este punto, por si tienes cualquier duda.

Las clases de cada una de las fases del juego

http://desarrolloweb.com/manuales/phaser3 Página 56 de 88
Extendiendo la clase "Phase" que acabamos de ver, podremos crear todas las pantallas o
niveles del juego. Tendrán un método create() en el que crearemos todos los ladrillos que se
vayan a mostrar en esta fase. Es tan sencillo como esto.

Mira este código de una de las fases del juego:

import { Phase } from './phase.js'

export class Phase4 extends Phase {

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

this.configureColisions();

}
}

Importamos la clase Phase


Extendemos la clase Phase para crear este nivel particular
Realizamos el método create en el que generamos el conjunto de ladrillos.
Invocamos al método configureColisions() que está implementado en la clase Phase.

No siempre podremos colocar los ladrillos mediante una configuración del método
staticGroup(). Hay veces que la colocación no responde a un patrón fácilmente programable,
por lo que los ladrillos también los podemos colocar uno a uno.

Para ilustrar este punto te dejo otro ejemplo de código de otra fase.

import { Phase } from './phase.js'

export class Phase2 extends Phase {

create() {
this.bricks = this.relatedScene.physics.add.staticGroup();

this.bricks.create(400, 270, 'orangebrick');


this.bricks.create(360, 225, 'orangebrick');
this.bricks.create(440, 225, 'orangebrick');
this.bricks.create(480, 180, 'orangebrick');
this.bricks.create(400, 180, 'orangebrick');
this.bricks.create(320, 180, 'orangebrick');
this.bricks.create(280, 135, 'orangebrick');
this.bricks.create(360, 135, 'orangebrick');
this.bricks.create(440, 135, 'orangebrick');
this.bricks.create(520, 135, 'orangebrick');
this.bricks.create(330, 90, 'orangebrick');
this.bricks.create(470, 90, 'orangebrick');

http://desarrolloweb.com/manuales/phaser3 Página 57 de 88
this.configureColisions();
}
}

En realidad el conjunto de acciones es exactamente el mismo, lo que cambia es cómo


generamos los ladrillos en el grupo.

Generador de niveles

Ahora vamos a ver el generador de niveles. Este generador tiene que ser capaz de conocer el
orden con el que se van a ir pasando las fases del juego y debe poder pasar de una a otra
cuando se le solicite.

import { Phase1 } from './phase1.js'


import { Phase2 } from './phase2.js'
import { Phase3 } from './phase3.js'
import { Phase4 } from './phase4.js'
import { Phase5 } from './phase5.js'
import { Phase6 } from './phase6.js'

export class PhaseConstructor {


constructor(scene) {
this.relatedScene = scene;
this.phases = [
Phase6,
Phase5,
Phase4,
Phase3,
Phase2,
Phase1,
];
}

create() {
let CurrenPhaseClass = this.phases.pop();
this.currentPhase = new CurrenPhaseClass(this.relatedScene);
return this.currentPhase.create();
}

nextLevel() {
if(this.phases.length == 0) {
this.relatedScene.endGame(true);
} else {
return this.create();
}
}

isPhaseFinished() {
return this.currentPhase.isPhaseFinished();
}
}

Importo todos los niveles que queremos implementar en el juego.


En el constructor recibo la escena donde se va a usar este componente
En el constructor genero un array con todos los niveles ordenados. Igual te extraña que
estén ordenados al revés, pero es que los voy a ir sacando con el método pop() de los
arrays, como si fuera una pila
El método create(), que se invocará al arrancar la escena del juego (desde el método

http://desarrolloweb.com/manuales/phaser3 Página 58 de 88
create del juego), se encarga de coger la última fase del array, instanciarla y solicitarle
que se cree el grupo de ladrillos
El método nextLevel() se encarga de verificar que existen todavía fases en el juego. Si no
es así, entonces llama al método endGame de la escena principal. Si quedaban fases,
entonces hace que se cree la siguiente, mediante el método create()
Tenemos un método extra que permite saber si la fase se ha terminado (porque todos
los ladrillos se hayan roto ya). Para ello pregunta a la fase actual si está terminada, que
es quien conoce al grupo de ladrillos que se está usando en ese instante.

Con esto, somos capaces de crear el sistema de fases. Lo importante es que, si queremos incluir
una nueva fase, no tenemos que tocar nada. Simplemente crear una nueva clase para el nivel
que se va a introducir, meterla en el array y listo!

Cómo usar el generador de fases desde la escena del juego

Esta es la parte más sencilla, ya que simplemente es invocar los métodos que hemos dejado
preparados en el generador de pantallas del juego. De hecho, en la escena Game lo más que
tenemos que hacer es borrar código que ahora está localizado en el generador de pantallas.
Fantástico! porque así dejamos el juego más leve y manejable.

Voy a centrarme en las novedades en el juego que resultan remarcables, porque además de
usar el generador de fases, hemos introducido alguna mejora extra, como nuevos bloques de
otros colores y sonidos para nuevos eventos del juego.

En el método init() instancio el constructor de niveles del juego:

init() {
this.phaseConstructor = new PhaseConstructor(this);
this.score = 0;
this.liveCounter = new LiveCounter(this, 3);
}

En el método create() de la clase Game, todo lo que era la construcción de los ladrillos y las
colisiones se resume a invocar el método del constructor de niveles:

this.phaseConstructor.create();

El método brickImpact() tiene un par de novedades. Primero, la responsabilidad de saber si el


nivel está terminado o no ahora la tiene el constructor de fases, invocando el método
isPhaseFinished(). Además, en el caso que la fase se haya terminado, tenemos que pedirte al
constructor de fases que pase al siguiente nivel, con el método nextLevel().

brickImpact(ball, brick) {
this.brickImpactSample.play();
brick.disableBody(true, true);
this.increasePoints(10);
if (this.phaseConstructor.isPhaseFinished()) {
this.phaseChangeSample.play();

http://desarrolloweb.com/manuales/phaser3 Página 59 de 88
this.phaseConstructor.nextLevel();
this.setInitialPlatformState();
}
}

Ya está! no hay nada más que haya cambiado. Una vez más, mucha de la complejidad nos la
hemos llevado a las nuevas clases que se han generado. De hecho, con todos estos cambios que
a priori podrían suponer una complejidad mucho mayor para nuestro juego, en realidad el
impacto ha sido justamente inverso! ahora el juego es hasta más simple que antes!!!

Crear unos bricks indestructibles

Hay otra mejora que hemos introducido en esta fase y que me he saltado para hablar de ella al
final.

Hacer niveles requiere imaginación y se me estaban acabando las ideas. Por eso se me ocurrió
que sería divertido darle alguna novedad en los niveles del juego, insertando algo que sería
fácil de introducir, como bloques indestructibles.

La idea para implementarlos es básicamente generar esos bloques en un grupo aparte. Es


decir, no forman parte del grupo de ladrillos que hay que romper para acabar el nivel. Es
normal, porque el comportamiento de estos bloques es distinto y tampoco es necesario
romperlos (ni se puede) para pasar al siguiente nivel.

Vamos a ver una clase que implementa una fase donde hay bloques indestructibles.

import { Phase } from './phase.js'

export class Phase1 extends Phase {


create() {
this.bricks = this.relatedScene.physics.add.staticGroup({
key: ['bluebrick', 'orangebrick', 'greenbrick', 'blackbrick', 'yellowbrick', 'blackbrick', 'yellowbrick', 'bluebrick', 'orangebrick', 'greenbrick'],
frameQuantity: 1,
gridAlign: {
width: 5,
height: 4,
cellWidth: 150,
cellHeight: 100,
x: 135,
y: 150
}
});

this.fixedBricks = this.relatedScene.physics.add.staticGroup();
this.fixedBricks.create(316, 165, 'greybrick');
this.fixedBricks.create(466, 165, 'greybrick');

this.configureColisions();
this.configureColisionsFixed();
}
}

Simplemente hemos creado un grupo extra, con los bloques nuevos, que serán grises, como
imitando a metal.

http://desarrolloweb.com/manuales/phaser3 Página 60 de 88
Luego se configuran las colisiones de una manera diferente. Ese método configureColisions()
está en la clase Phase, la que extendemos. Que en verdad tenía este código que ahora te
muestro completo:

export class Phase {


constructor(scene) {
this.relatedScene = scene;
}

configureColisions() {
this.relatedScene.physics.add.collider(this.relatedScene.ball, this.bricks, this.relatedScene.brickImpact, null, this.relatedScene);
}

configureColisionsFixed() {
this.relatedScene.physics.add.collider(this.relatedScene.ball, this.fixedBricks, this.relatedScene.fixedBrickImpact, null, this.relatedScene);
}

deleteFixedBricks() {
if(this.fixedBricks) {
this.fixedBricks.getChildren().forEach(item => {
item.disableBody(true, true);
})
}
}

isPhaseFinished() {
return (this.bricks.countActive() === 0)
}
}

El método deleteFixedBricks() se necesita porque, al pasar de una fase a la otra, se deben


borrar los bloques fijos (irrompibles), porque si no, permanecerían al entrar en los próximos
niveles.

Por tanto, el codigo para pasar de un nivel a otro, que tenemos en la clase PhaseConstructor,
también tiene una pequeña diferencia.

nextLevel() {
this.currentPhase.deleteFixedBricks();
if(this.phases.length == 0) {
this.relatedScene.endGame(true);
} else {
return this.create();
}
}

Había omitido justamente la parte en la que pedimos al nivel actual borrar los bloques fijos,
antes de pasar al siguiente nivel.

Conclusión

Creo que con esto he terminado de explicar todas las novedades introducidas en esta mejora
del juego, que permite disponer de diferentes niveles o pantallas con distintas distribuciones
de bloques.

http://desarrolloweb.com/manuales/phaser3 Página 61 de 88
La verdad es que el juego está teniendo un aspecto que incluso podría superar las expectativas
de más de uno ¿no?

El código completo, tal como lo hemos dejado en esta etapa del desarrollo, lo puedes consultar
en este repositorio de GitHub. https://github.com/deswebcom/ball-game-phaser/tree/phases

Ya solamente nos queda uno de los detalles más importantes del juego original del Arkanoid:
la posibilidad de ganar poderes especiales cuando se rompen ciertos ladrillos, que mejore la
jugabilidad, y la diversidad de las partidas. Esto lo veremos seguidamente.

Este artículo es obra de Miguel Angel Alvarez


Fue publicado / actualizado en 03/05/2021
Disponible online en https://desarrolloweb.com/articulos/gestionar-niveles-phaser

Animaciones con sprites en Phaser


Qué son sprites, cómo usarlos para que algunos elementos del juego desarrollado con Phaser 3
se muestren con animaciones sencillas gracias a la progresión de varias imágenes.

Otra de las claves fundamentales para crear juegos atractivos son las animaciones. En Phaser
podemos conseguir animaciones de diversas maneras y una de las más sencillas y típicas la
conseguimos a través de los sprites.

En los juegos habitualmente tenemos personajes que al moverse van cambiando su aspecto,
para que parezca que están corriendo por ejemplo. Hasta ahora en nuestro juego de
demostración, que hemos ido construyendo en el Manual de Phaser3, no habíamos tenido la
necesidad de usar sprites, pero ahora vamos a incorporar nuestro primer elemento animado.

La animación la realizaremos para los poderes especiales que salen de vez en cuando al romper
algunos ladrillos. Esos elementos aparecerán en pantalla y se moverán de diversas maneras.

La imagen con los distintos fotogramas

Para comenzar una animación basada en sprites tenemos que componer la secuencia de
imágenes que forman la animación. Esa imagen tendrá todos los fotogramas de la animación,
uno al lado del otro, pero en el mismo archivo gráfico.
http://desarrolloweb.com/manuales/phaser3 Página 62 de 88
Para nuestro juego vamos a realizar una animación de un diamante, que hace un movimiento
similar a como si girase sobre sí mismo. Los fotogramas los podemos ver aquí.

En este artículo te proporcionamos una imagen válida para conseguir la animación con
sprites. Puedes hacer tus propias imágenes fácilmente, pero si estás aprendiendo y no te
apetece parar mucho con esta parte de crear las imágenes de los fotogramas de la
animación, también puedes conseguir en Internet muchas imágenes con sprites que te
servirán para hacer todo tipo de animaciones, para tus personajes, enemigos, etc.

Es una secuencia de imágenes muy "casera" y no demasiado bonita, ya que que mi fuerte no es
el diseño gráfico y la he hecho yo mismo a mano, pero lo importante es que veamos la
progresión de la animación en el sprite que vamos a usar. Además nos servirá perfectamente
para este ejercicio y para el estilo de juego que estamos realizando.

Cómo precargar un sprite

Ya en la parte de la programación de la animación, tenemos que comenzar por hacer una


precarga del archivo gráfico con los sprices, igual que hacemos con las imágenes del juego,
desde el método preload().

this.load.spritesheet('bluediamond',
'images/blue_diamond-sprites.png',
{ frameWidth: 48, frameHeight: 48 }
);

Al cargar este sprite usamos un método nuevo que no habíamos visto todavía: spritesheet(). En
él, indicamos el identificador y el archivo donde tenemos la secuencia de imágenes. Lo que
difiere de la carga de imágenes normales es que tenemos que indicar la anchura y la altura de
los fotogramas. Como puedes comprobar, todos los fotogramas en una misma animación
tendrán las mismas dimensiones, que son 48 píxeles de ancho y de alto.

Todos los fotogramas de una misma animación tendrán idéntico tamaño, pero no
necesariamente tienen que ser siempre cuadrados. Obviamente, cada sprite del juego
tendrá las dimensiones que hagan falta, unos actores podrán ser mayores que otros, más
alargados o más cuadrados, etc.

Cómo asociar un sprite a la escena

Asociar un sprite a la escena es tan sencillo como lo era asociar una imagen común en el juego,

http://desarrolloweb.com/manuales/phaser3 Página 63 de 88
solo que usamos add.sprite() en lugar de add.image().

this.add.sprite(40, 40, 'bluediamond');

Este código se insertará desde el método create() de la escena, siendo "this" la propia escena.

Cómo crear la animación

Ahora viene el punto interesante y novedoso para incorporar las animaciones, que es decirle a
Phaser cómo debe realizar la animación del elemento. Para ello existe un método especial en la
escena llamado anims.create().

this.anims.create({
key: 'bluediamondanimation',
frames: this.anims.generateFrameNumbers('bluediamond', { start: 0, end: 7 }),
frameRate: 10,
repeat: -1,
yoyo: true,
});

Existen una serie de configuraciones posibles para la animación. Lo que siempre tendremos
será el identificador que le queremos asignar. En este caso se ha puesto
'bluediamondanimation'.

Además, siempre tendremos que indicar qué frames, de entre todos los fotogramas de la
imagen, son usados para la animación que estamos creando. Ten en cuenta que en una imágen
podrían haber fotogramas para diversas animaciones de un personaje, por ejemplo cuando
camina hacia la parte de la derecha, la izquierda, cuando salta, etc.

El framerate es la cantidad de invocaciones en el método update que transcurren entre cada


fotograma de la animación. Repeat sirve para indicar las veces que esta animación se repite,
aunque en este caso con el valor -1 estamos indicando que se repita de manera constante. El
atributo "yoyo" hace que la animación sea de ida y vuelta, es decir, comienza en el primer
frame, va hasta el último y, en vez de comenzar de nuevo con el primero, realiza la secuencia
de fotogramas en sentido contrario, del último al primero.

Existen muchas configuraciones adicionales que podemos consultar en la documentación.

Cómo asociar la animación al elemento

Una vez "declarada" la animación, podemos asociarla al sprite con una invocación anims.play()
del sprite que hemos añadido a la escena.

this.miSprite = this.add.sprite(40, 40, 'bluediamond');


this.miSprite.anims.play('bluediamondanimation');

http://desarrolloweb.com/manuales/phaser3 Página 64 de 88
Así el elemento se mostrará en en juego con una animación, que hemos configurado de manera
constante.

Cómo usar el sistema de físicas

Por supuesto, si nuestra animación se aplica a un elemento que necesitamos que procese las
colisiones con otros actores del juego, necesitamos crearlo con el sistema de físicas que ya
conocemos.

this.miSprite = this.physics.add.sprite(40, 40, 'bluediamond');


this.miSprite.anims.play('bluediamondanimation');
this.physics.add.collider(this.ball, this.miSprite);

Cómo añadir sprites animados a un grupo

También podría ocurrir que tengamos elementos, que queremos que formen parte de un
grupo. El trabajo es idéntico a los grupos de imágenes.

Creamos el grupo de diamantes.

this.diamonds = this.relatedScene.physics.add.group();

Añadimos elementos al grupo:

let diamond = this.diamonds.create(x, y, 'bluediamondanimation')

Y lo bueno de esto es que podemos añadir las colisiones que queramos gestionar, todas de una
vez para todos los elementos del grupo.

this.relatedScene.physics.add.collider(this.ball, this.diamonds, this.ballImpact, null, this);

Aplicar características individuales a los elementos del grupo

Cada uno de los elementos del grupo puede tener sus propias características específicas, que
podemos asignar en cualquier momento, por ejemplo cuando lo creamos.

let diamond = this.diamonds.create(x, y, sprite)


diamond.setScale(0.6);
diamond.anims.play(sprite + 'animation');
diamond.body.setAllowRotation();
diamond.body.setAngularVelocity(100);
diamond.body.setVelocity(Phaser.Math.Between(-100, 100), Phaser.Math.Between(-100, 100));
diamond.setBounce(1);
diamond.setCollideWorldBounds(true);

http://desarrolloweb.com/manuales/phaser3 Página 65 de 88
Al ejecutarse este código, los diamantes que creemos se basarán en un sprite contenido en la
variable "sprite". Todos se redimensionarán a un 60% del tamaño (siempre igual), pero la
animación cambiará, porque estamos usando la variable "sprite" para indicar su nombre.
Además todos los elementos estarán moviéndose en un ángulo, con una misma velocidad, pero
la dirección de su movimiento podrá cambiar porque setVelocity() usa valores aleatorios.

Componente Diamonds

Para acabar vamos a ver un componente completo que implementa los diamantes con sus
animaciones y comportamientos. Este componente, como sabes, nos permite dejar toda la
complejidad de los diamantes localizada en un módulo para facilitar su mantenimiento. Por
supuesto, lo vamos a usar en el juego "breakout" para crear diamantes que se moverán por la
pantalla. La idea es que, cuando la bola impacte con estos diamantes se asignen poderes
especiales al jugador.

Todos los diamantes pertenecerán a un grupo. Cuando se construya un objeto de la clase


Diamonds se creará el grupo. Luego se crearán diamantes bajo demanda, con configuraciones
parametrizadas. Además los propios diamantes tendrán configuradas en este mismo
componente las colisiones con la bola.

El código del componente, aplicando el conocimiento nuevo que has aprendido en este
artículo, lo tenemos por aquí.

export class Diamonds {


constructor(scene) {
this.relatedScene = scene;
this.diamonds = this.relatedScene.physics.add.group();
this.relatedScene.physics.add.collider(this.relatedScene.ball.get(), this.diamonds, this.ballImpact, null, this);
}

create(x, y, sprite, relatedPower) {


let diamond = this.diamonds.create(x, y, sprite)
diamond.relatedPower = relatedPower;
diamond.setScale(0.6);
diamond.anims.play(sprite + 'animation');
diamond.body.setAllowRotation();
diamond.body.setAngularVelocity(100);
diamond.body.setVelocity(Phaser.Math.Between(-100, 100), Phaser.Math.Between(-100, 100));
diamond.setBounce(1);
diamond.setCollideWorldBounds(true);
}

ballImpact(ball, diamond) {
diamond.destroy();
diamond.relatedPower.givePower();
let currentVelocity = ball.body.velocity;
this.relatedScene.removeGlueFromBall();
if(currentVelocity.y > 0) {
ball.body.setVelocityY(300);
} else {
ball.body.setVelocityY(-300);
}
}
}

http://desarrolloweb.com/manuales/phaser3 Página 66 de 88
Los diamantes estarán asociados a un poder, que se asignará al crear el diamante. Cada
diamante tendrá también un sprite y animación parametrizadas.

El impacto con la bola hará que el diamante desaparezca y dará el poder al jugador. Además,
para evitar que la bola se vea frenada como consecuencia del impacto, hacemos que, después
del impacto la bola tenga siempre una velocidad vertical de 300. Para implementar este
comportamiento quizás haya un condicional que te puede despistar:

if(currentVelocity.y > 0) {
ball.body.setVelocityY(300);
} else {
ball.body.setVelocityY(-300);
}

Eso sirve para saber si la bola estaba moviéndose hacia arriba o hacia abajo después del
impacto. Si era hacia arriba entonces asignamos una velocidad negativa y si era hacia abajo la
velocidad es positiva.

Las colisiones con los ladrillos se configurarán en la propia configuración de la fase del juego,
ya que la fase es quien sabe qué ladrillos están en cada momento.

Este componente todavía lo tenemos que modificar algo, para conseguir aplicar la parte que
nos falta, que es crear los poderes especiales que conseguiremos cuando capturemos los
diamantes. Pero esto ya lo veremos en el siguiente artículo.

Hasta aquí hemos aprendido a crear y configurar animaciones en base a sprites, lo que es un
gran avance. Lo cierto es que no es nada complejo gracias al motor de juevos Phaser.

Este artículo es obra de Miguel Angel Alvarez


Fue publicado / actualizado en 03/06/2021
Disponible online en https://desarrolloweb.com/articulos/animaciones-sprites-phaser

Componentes avanzados en el juego con Phaser 3


Componentes avanzados y su estructura en clases involucradas para la gestión de los poderes
especiales del jugador, en el juego del Breakout realizado por Phaser 3. Nuevas técnicas del
framework.

http://desarrolloweb.com/manuales/phaser3 Página 67 de 88
La verdad es que, para ser un juego de demostración, hasta el momento hemos podido avanzar
bastante en el desarrollo de nuestro Breakout, o clon del clásico Arkanoid. En este artículo
vamos a continuar hacia lo que sería la "traca final", como dirían los valencianos, realizando
unos componentes avanzados para darle todavía un toque más profesional al juego.

En esta ocasión entramos en un tema un poco más complejo, en relación a lo que hemos visto
anteriormente a lo largo del Manual de Phaser, que es la gestión de los poderes especiales. Por
ello no tengo intención de explicar punto a punto todas las novedades que hemos introducido,
sino simplemente la estructura de clases que hemos incorporado y, por supuesto, las cosas
nuevas que podemos aprender del framework Phaser 3.

Cada poder se encuentra representado en el juego con un diamante, que aparece cuando se
rompe alguno de los ladrillos del juego. Los diamantes tienen su propia animación y
comportamiento, que vimos en la clase anterior en la que aprendimos a manejar
animaciones con sprites.

Clases involucradas en la gestión de poderes

La gestión de los poderes por parte del usuario involucra un conjunto de clases nuevo.

Clase Power: Esta es una clase base para la creación de todos los poderes.
Clase de un poder particular: Cada poder del juego se representará por una clase
independiente, que tendrá las especializaciones concretas para este poder, extendiendo
la clase Power base.

De este modo, cada vez que queremos agregar un nuevo poder al juego, simplemente tenemos
que crear una nueva clase con el poder especial que hemos agregado. Nuestro juego estará
abierto a modificaciones de una manera sencilla!

Clase Power

Esta es una clase genérica. No tiene más que las cosas comunes que necesitamos en todos los
poderes.

http://desarrolloweb.com/manuales/phaser3 Página 68 de 88
export class Power {
constructor(scene, diamonds, powerSprite) {
this.relatedScene = scene;
this.powerSprite = powerSprite;
this.diamonds = diamonds;
}

create(x, y) {
this.diamonds.create(x, y, this.powerSprite, this);
}

givePower() {
console.log('Define the power');
}
}

Su constructor recibe la escena donde estamos trabajando, la clase que almacena todos los
diamantes de un nivel particular y el nombre del sprite para este diamante (porque cada poder
se representa con un diamante distinto que tiene una animación basada en un sprite distinto).

El método create de un poder hace que se cree el diamante. Sólo se ejecutará cuando se rompa
el ladrillo que tiene asignado un el poder. (No todos los ladrillos del juego liberan poderes,
como más adelante veremos).

Por último el método givePower() es el que asigna el poder en particular. Este sería un método
abstracto en Programación Orientada a Objetos, pero como Javascript no tiene todavía
métodos abstractos, le he dado un comportamiento de base (mostrar un mensaje en la consola)
que esperamos que se redefina en cada clase hija.

Un poder particular

Ahora veamos las clases de poderes particulares, que extienden la clase Power y sirven para
crear ya un poder real del juego.

Este poder asigna una vida extra al jugador. Usará diamantes azules.

import { Power } from './power.js';

export class LivePower extends Power {


constructor(scene, diamonds) {
super(scene, diamonds, 'bluediamond');
}

givePower() {
this.relatedScene.increaseLives();
}
}

Este otro poder hace que la plataforma sea más grande, facilitando el juego con una mayor
superficie de impacto. Para este poder usaremos diamantes rojos.

import { Power } from './power.js';

export class LargePlatformPower extends Power {

http://desarrolloweb.com/manuales/phaser3 Página 69 de 88
constructor(scene, diamonds) {
super(scene, diamonds, 'reddiamond');
}

givePower() {
this.relatedScene.setPlatformBig();
}
}

Este último poder hace que el jugador tenga "pegamento" en la plataforma, de modo que,
cuando la bola impacta contra la base, se queda adherida y podemos soltarla cuando nosotros
queramos, apuntando mejor a los ladrillos que nos falten por romper. Como puedes ver, usará
diamantes verdes.

import { Power } from './power.js';

export class GluePower extends Power {


constructor(scene, diamonds) {
super(scene, diamonds, 'greendiamond');
}

givePower() {
this.relatedScene.setGluePower();
}
}

Has visto qué sencillez para cada poder del juego. Realmente sólo define el diamante que
quiere liberar y sobrescribe el método givePower(), que tiene que invocarse en la escena para
poder ejecutar ese poder en el juego.

Cada vez que queramos crear un nuevo poder en el juego simplemente tendremos que crear
una nueva clase derivada y listo!

Cómo asignar poderes a los ladrillos

Ahora veamos cómo se generan los poderes, asociando éstos a ciertos ladrillos. De manera que,
cuando se rompan los ladrillos, se lancen los correspondientes diamantes que liberarán los
correspondientes poderes.

Esto implica cambiar las clases que implementan las fases, que ya explicamos en el artículo de
implementación de los niveles del juego.

La clase base para las fases ahora incorpora nuevos métodos, o modificaciones de los
existentes.

Primero necesitamos modificar el constructor, para agregar un array de poderes.

constructor(scene) {
this.relatedScene = scene;
this.powers = [];
}

http://desarrolloweb.com/manuales/phaser3 Página 70 de 88
Asignamos las colisiones de cualquier elemento con los ladrillos. Este método lo hemos hecho
genérico para asignar colisiones de cualquier cosa, aunque de momento solo lo usaremos para
tratar colisiones de los diamantes.

setBrickCollider(element) {
this.relatedScene.physics.add.collider(this.bricks, element);
if (this.fixedBricks) {
this.relatedScene.physics.add.collider(this.fixedBricks, element);
}
}

Luego creamos un método que nos diga el índice del ladrillo que se ha roto en este momento:

getBrickIndex(brick) {
let children = this.bricks.getChildren();
for(let i in children) {
if (children[i] == brick) {
return i;
}
}
}

Ahora, cuando gestionamos los ladrillos que se rompen, necesitamos saber si éstos tienen un
poder asociado. En cuyo caso se tendrá que crear el correspondiente diamante.

brickImpact(ball, brick) {
let brickIndex = this.getBrickIndex(brick);
if(this.powers[brickIndex]) {
this.powers[brickIndex].create(ball.x, ball.y)
}
this.relatedScene.brickImpact(ball, brick);
}

Esto lo hemos hecho comprobando si en el array de poderes, en el índice del ladrillo que se
rompió, tenemos alguna clase de poder. En el caso que sí tengamos, invocamos al método del
poder que crea el diamante del color que le corresponde a ese poder.

Por último en esta clase teníamos un método que se encargaba de borrar los ladrillos
indestructibles, cuando se pasaba a la fase siguiente del juego. En este punto necesitamos
borrar también los diamantes que no se hayan colectado.

deleteFixedBricks() {
if(this.fixedBricks) {
this.fixedBricks.getChildren().forEach(item => {
item.disableBody(true, true);
});
}
if(this.diamonds) {
this.diamonds.diamonds.getChildren().forEach(item => {
item.disableBody(true, true);
});
}
}

http://desarrolloweb.com/manuales/phaser3 Página 71 de 88
Asociar componentes de poderes en una escena del juego

Tenemos lo básico para poder trabajar con nuestros nuevos componentes avanzados para crear
los poderes especiales. Ahora veamos cómo los asociamos en cualquiera de las clases de un
nivel concreto.

Esta fase es un buen ejemplo de código:

import { Phase } from './phase.js'


import { Diamonds } from "../diamonds.js";
import { LivePower } from '../powers/live-power.js';
import { LargePlatformPower } from '../powers/large-platform-power.js';
import { GluePower } from '../powers/glue-power.js';

export class Phase4 extends Phase {

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

this.configureColisions();

this.diamonds = new Diamonds(this.relatedScene);


this.setBrickCollider(this.diamonds.diamonds);

this.powers[3] = new LivePower(this.relatedScene, this.diamonds);


this.powers[35] = new LivePower(this.relatedScene, this.diamonds);
this.powers[1] = new LargePlatformPower(this.relatedScene, this.diamonds);
this.powers[24] = new LargePlatformPower(this.relatedScene, this.diamonds);
this.powers[16] = new GluePower(this.relatedScene, this.diamonds);
this.powers[29] = new GluePower(this.relatedScene, this.diamonds);

}
}

Importamos todas las clases de los poderes


Creamos un objeto de dimamantes (que dentro tiene el grupo de diamantes)
Asignamos las colisiones de los diamantes con los ladrillos con setBrickCollider()
Luego generamos los poderes. Fíjate que estos poderes se asignan en casillas arbitrarias
de un array. Cada poder de esta manera está asociado a un índice, que corresponde con
alguno de los ladrillos del juego.

Cómo realizar las transformaciones debidas a los poderes

Como vimos, cada poder tiene un método que implementa el comportamiento cuando se tiene
que dar el beneficio al jugador. Estas transformaciones se deben realizar en la clase Game, que
http://desarrolloweb.com/manuales/phaser3 Página 72 de 88
es la que conoce a todos los sistemas que tienen que sufrir las modificaciones de estado.

Las transformaciones ya son más relacionadas a la propia lógica de programación que a


características del framework.

Por ejemplo, este es el método de la clase Game que se invoca cuando se quiere asignar una
vida extra al jugador, que se otorga con los diamantes azules.

increaseLives() {
this.liveCounter.increase();
}

Como puedes ver, no hay mucha dificultad, simplemente la pasamos la bola al contador de
vidas, para que sea él el que se encargue de asignar la vida extra.

Ya dentro del contador de vidas, se realiza el código final, que asigna esa vida extra creando la
correspondiente imagen en el display de vidas.

increase() {
let targetPos = 765;
this.liveImages.getChildren().forEach( (item, index) => {
item.x = item.x - this.displacement;
})
let newLive = this.liveImages.create(targetPos, 33, 'platform');
newLive.setScale(0.3);
}

Esa estrategia de crear componentes que se ocupen de su trabajo simplifica las cosas, porque
cada uno encapsula la complejidad de la tarea. Es importante porque, a medida que se van
creando variantes en el juego, también se va complicando el código de la escena principal y si
no aplicamos un mínimo orden habrá un momento en el que nuestro código será simplemente
un caos.

Para componetizar y poder implementar los comportamientos del aumento del tamaño de la
plataforma y el pegamento de la bola, hemos tomado la decisión de separar la bola y la
plataforma de la escena principal.

Decidimos crear una clase para la bola y otra para la plataforma, que es como se ha quedado el
juego en su estado final, aunque la verdad es que esta división del código no es siempre del
todo práctica, ya que muchas veces los comportamientos de la bola de y la plataforma van
ligados el uno al otro (como por ejemplo mover la plataforma y la bola al mismo tiempo
cuando la bola está pegada a la plataforma). Por ello, para hacer muchas cosas con la
plataforma, tenemos que entregarle la bola, de modo que sea capaz de completar las acciones.
Por ello es probable que en el repositorio final de código del juego tengamos el concepto
"jugador" que engloba tanto a la bola como a la plataforma y ambas puedan colaborar de

http://desarrolloweb.com/manuales/phaser3 Página 73 de 88
manera más sencilla. Lo dejo como idea y quizás lo refactorice más adelante.

Nota: he de admitir que el diseño de clases lo he ido realizando sobre la marcha a medida
que iba realizando el juego. Quizás si ahora empezase desde cero, con las ideas más claras
de hasta dónde quería llegar, podría diseñarlo de otra manera y llegar a clases más bonitas
y bien hechas.

Este es el código de la bola tal como ha quedado en el estado final del juego:

export class Ball {

constructor(scene) {
this.relatedScene = scene;
this.isGlued = true;
}

create() {
this.ball = this.relatedScene.physics.add.image(385, 430, 'ball');
this.ball.setBounce(1);
this.ball.setCollideWorldBounds(true);
}

isLost() {
return (this.ball.y > 500 && this.ball.active) ? true : false;
}

get() {
return this.ball;
}

throw(velocity) {
this.ball.setVelocity(velocity, -300);
this.isGlued = false;
}

removeGlue() {
this.isGlued = false;
}
}

Como puedes ver, han salido métodos sencillos y que resultan muy claros con respecto a lo que
hacen.

No puedo decir que la clase de plataforma haya quedado tan sencilla, pero ahí va el código.

export const INITIAL_PLATFORM_SIZE = 0.6;


export const LARGE_PLATFORM_SIZE = 1;

export class Platform {


constructor(scene) {
this.relatedScene = scene;
this.size = INITIAL_PLATFORM_SIZE;
this.gluePower = false;
this.hasBallGlued = false;
}

create() {
this.platform = this.relatedScene.physics.add.image(400, 460, 'platform').setImmovable().setScale(this.size);

http://desarrolloweb.com/manuales/phaser3 Página 74 de 88
this.platform.setCollideWorldBounds(true);
}

hasGluePower() {
return this.gluePower;
}

updatePosition(ball, cursors) {
if (cursors.left.isDown) {
this.platform.setVelocityX(-500);
if (ball.isGlued || this.hasBallGlued) {
ball.get().setVelocityX(-500);
}
}
else if (cursors.right.isDown) {
this.platform.setVelocityX(500);
if (ball.isGlued || this.hasBallGlued) {
ball.get().setVelocityX(500);
}
}
else {
this.platform.setVelocityX(0);
if (ball.isGlued || this.hasBallGlued) {
ball.get().setVelocityX(0);
}
}
}

setInitialState(ball) {
this.platform.x = 400;
this.platform.y = 460;
ball.get().setVelocity(0, 0);
ball.get().x = 385;
if (this.size == LARGE_PLATFORM_SIZE) {
ball.get().y = 420;
} else {
ball.get().y = 430;
}
ball.isGlued = true;
}

setSize(size) {
this.size = size;
this.platform.setScale(size);
}
setBigSize() {
this.setSize(LARGE_PLATFORM_SIZE);
this.gluePower = false;
}
setInitialSize() {
this.setSize(INITIAL_PLATFORM_SIZE);
}

removeGlue() {
this.gluePower = false;
}

setGluePower() {
this.setInitialSize();
this.gluePower = true;
}

get() {
return this.platform;
}

isGluedBecausePower() {
return (this.hasGluePower() && this.hasBallGlued)
}
}

http://desarrolloweb.com/manuales/phaser3 Página 75 de 88
Gracias a esta separación hemos podido reducir el código de la escena principal del juego, la
clase Game. Además, hemos conseguido que el código sea más semántico, gracias a que invoca
a muchos métodos de la bola y la plataforma que indican claramente lo que hacen.

import { PhaseConstructor } from '../components/phases/phase-constructor.js';


import { LiveCounter } from '../components/live-counter.js';
import { Platform } from '../components/platform.js';
import { Ball } from '../components/ball.js';

const INITIAL_LIVES = 3;
const INITIAL_VELOCITY_X = -60;

export class Game extends Phaser.Scene {

constructor() {
super({ key: 'game' });
}

init() {
this.glueRecordVelocityX = INITIAL_VELOCITY_X;
this.phaseConstructor = new PhaseConstructor(this);
this.platform = new Platform(this);
this.ball = new Ball(this);
this.liveCounter = new LiveCounter(this, INITIAL_LIVES);
this.score = 0;
}

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

this.add.image(410, 250, 'background');

this.liveCounter.create();

this.platform.create();
this.ball.create();

this.physics.add.collider(this.ball.get(), this.platform.get(), this.platformImpact, null, this);

this.phaseConstructor.create();

this.scoreText = this.add.text(16, 16, 'PUNTOS: 0', { fontSize: '20px', fill: '#fff', fontFamily: 'verdana, arial, sans-serif' });

this.platformImpactSample = this.sound.add('platformimpactsample');
this.brickImpactSample = this.sound.add('brickimpactsample');
this.fixedBrickImpactSample = this.sound.add('fixedbrickimpactsample');
this.gameOverSample = this.sound.add('gameoversample');
this.winSample = this.sound.add('winsample');
this.startGameSample = this.sound.add('startgamesample');
this.liveLostSample = this.sound.add('livelostsample');
this.phaseChangeSample = this.sound.add('phasechange');

this.createAnimations();

this.cursors = this.input.keyboard.createCursorKeys();
}

update() {
this.platform.updatePosition(this.ball, this.cursors);

if (this.ball.isLost()) {
let gameNotFinished = this.liveCounter.liveLost();
if (!gameNotFinished) {
this.liveLostSample.play();
this.platform.setInitialState(this.ball);
this.platform.setInitialSize();
this.platform.removeGlue();

http://desarrolloweb.com/manuales/phaser3 Página 76 de 88
this.glueRecordVelocityX = INITIAL_VELOCITY_X;
}
}

if (this.cursors.up.isDown) {
if (this.ball.isGlued) {
this.startGameSample.play();
this.ball.throw(INITIAL_VELOCITY_X);
} else if(this.platform.isGluedBecausePower()) {
this.ball.throw(this.glueRecordVelocityX);
this.platform.hasBallGlued = false;
}
}
}

platformImpact(ball, platform) {
this.platformImpactSample.play();
this.increasePoints(1);
let relativeImpact = ball.x - platform.x;
if(this.platform.hasGluePower()) {
ball.setVelocityY(0);
ball.setVelocityX(0);
this.glueRecordVelocityX = this.calculateVelocity(relativeImpact);
this.platform.hasBallGlued = true;
} else {
ball.setVelocityX(this.calculateVelocity(relativeImpact));
}
}

calculateVelocity(relativeImpact) {
if(relativeImpact > 50) {
relativeImpact = 50;
}
if (relativeImpact > 0) {
return (8 * relativeImpact);
} else if (relativeImpact < 0) {
return (8 * relativeImpact);
} else {
return (Phaser.Math.Between(-10, 10))
}
}

brickImpact(ball, brick) {
this.brickImpactSample.play();
brick.disableBody(true, true);
this.increasePoints(10);
if (this.phaseConstructor.isPhaseFinished()) {
this.phaseChangeSample.play();
this.phaseConstructor.nextLevel();
this.platform.setInitialState(this.ball);
}
}

fixedBrickImpact(ball, brick) {
this.fixedBrickImpactSample.play();
}

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

endGame(completed = false) {
if(! completed) {
this.gameOverSample.play();
this.scene.start('gameover');
} else {
this.winSample.play();
this.scene.start('congratulations');
}
}

http://desarrolloweb.com/manuales/phaser3 Página 77 de 88
createAnimations() {
this.anims.create({
key: 'bluediamondanimation',
frames: this.anims.generateFrameNumbers('bluediamond', { start: 0, end: 7 }),
frameRate: 10,
repeat: -1,
yoyo: true,
});
this.anims.create({
key: 'reddiamondanimation',
frames: this.anims.generateFrameNumbers('reddiamond', { start: 0, end: 7 }),
frameRate: 10,
repeat: -1,
yoyo: true,
});
this.anims.create({
key: 'greendiamondanimation',
frames: this.anims.generateFrameNumbers('greendiamond', { start: 0, end: 7 }),
frameRate: 10,
repeat: -1,
yoyo: true,
});
}

increaseLives() {
this.liveCounter.increase();
}

setGluePower() {
this.platform.setGluePower();
}

setPlatformBig() {
this.platform.setBigSize();
}

removeGlueFromBall() {
this.ball.removeGlue();
}
}

Este cambio de la bola y plataforma en clases aparte también ha impactado en un par de


lugares, en la clase Phase y la clase Diamonds, que hacían uso de la bola, pero son cambios
mínimos, donde antes usabamos this.relatedScene.ball ahora tenemos que pedirle la bola así
this.relatedScene.ball.get(). También en la clase Diamonds, donde hacíamos
ball.setData('glue', false); ahora tenemos que invocar un método específico de la bola para
decirle que su estado actual es "sin pegamento" con this.relatedScene.removeGlueFromBall().

Como dije, no voy a explicar todo el detalle de los cambios que hemos realizado, porque es al
final un tema de programación. Solo quédate con la estructura de clases y los conceptos de
Phaser que hemos ido tratando. Para ver una referencia del código y los cambios completos
puedes entrar en el repositorio de GitHub y explorar el código completo, ahora en la rama
master, o ver los commits para saber qué se fue introduciendo en cada paso.

El enlace del repositorio GitHub lo tienes aquí: https://github.com/deswebcom/ball-game-


phaser

Conclusión

Ahora sí, vamos a dar este juego por terminado. Con estos componentes avanzados en Phaser

http://desarrolloweb.com/manuales/phaser3 Página 78 de 88
hemos realizado una versión del Arkanoid bastante atractiva, que espero que te haya resultado
sobre todo didáctica.

Estoy seguro que a lo largo de este proyecto has aprendido muchas cosas del framework
Phaser 3 y que las podrás aplicar a nuevos juegos que puedas crear tú, o quizás extender esta
práctica del Breakout para crear nuevos niveles y poderes para el jugador. Si nos mandas un
pull-request, será un placer integrar los niveles o poderes que hayas realizado.

Cualquier duda, nos la puedes comunicar como una FAQ.

En futuros artículos de Phaser ya pensaremos en implementar otro tipo de mejoras en el juego,


no tanto abordando nueva funcionalidad, sino prestaciones extra, como controles para que
puedas usarlo en móviles, convertir el juego en una progressive web app, etc. Así que si
profundizas en este manual podrás seguir aprendiendo cosas que aplicar a cualquier proyecto
con Phaser.

Este artículo es obra de Miguel Angel Alvarez


Fue publicado / actualizado en 18/06/2021
Disponible online en https://desarrolloweb.com/articulos/componentes-avanzados-
juego-phaser

http://desarrolloweb.com/manuales/phaser3 Página 79 de 88
Complementos y mejoras para juegos de
Phaser

Vamos a empezar una sección donde abordaremos diferentes complementos y mejoras que
podemos implementar a juegos Javascript creados con el framework Phaser.

Virtual Joystick para Phaser


Instala y configura un plugin para Phaser 3 con el que puedes implementar de manera sencilla
un Joystick virtual, para crear un control de usuario en móviles.

En este artículo vas a aprender a implementar configurar y usar un Joystick virtual, que
puedes incorporar en tus juegos creados con Phaser 3, de modo que los usuarios que acceden a
ellos a través de un móvil táctil tengan la posibilidad de controlar el juego.

Esta tarea resulta especialmente sencilla usando un plugin que se ofrece gratuitamente y que
puedes instalar en tu proyecto de una manera muy sencilla y usarlo en tus juegos.

El plugin con las explicaciones (un poco escuetas y en inglés) lo puedes ver en esta dirección:
plugin Phaser 3 para Joystick virtual.

Instalar el plugin de joystick virtual

Este paso consta de dos etapas, primero la precarga del plugin y luego el uso en la escena
donde lo necesites.

Método preload() para la precarga

La precarga la realizamos desde el método preload() de Phaser, en el que incorporamos el


plugin por medio de estas sentencias.

http://desarrolloweb.com/manuales/phaser3 Página 80 de 88
preload() {
let url = 'https://raw.githubusercontent.com/rexrainbow/phaser3-rex-notes/master/dist/rexvirtualjoystickplugin.min.js';
this.load.plugin('rexvirtualjoystickplugin', url, true);
}

Teóricamente el plugin lo puedes instalar con npm y en la página que enlacé antes con la
documentación lo mencionan, pero a mi no me funcionó correctamente. Me daba un error
Javascript. Igual es un problema temporal o algo que no hice bien.

El preload del plugin lo podemos realizar en la escena de precarga y luego usar el plugin en
cualquier otra escena donde lo necesitemos.

Método create() para incorporar el joystick a la escena

Mediante el método create() de la escena donde sea necesario usar el joystick podemos
incorporarlo al juego.

this.joyStick = this.plugins.get('rexvirtualjoystickplugin').add(this, {
x: 55,
y: 400,
radius: 100,
base: this.add.circle(0, 0, 50, 0x888888),
thumb: this.add.circle(0, 0, 25, 0xcccccc),
});

Con el anterior código consigues instanciar el joystick. Puedes configurar la posición y la forma
y tamaño del control.

Una vez lo tienes instalado, puedes crear el objeto "cursor", mediante el cual puedes saber en
todo momento qué direcciones se están aplicando en el joystick en todo momento.

this.joystickCursors = this.joyStick.createCursorKeys();

El cursor del joystick funciona de manera similar al que obtenemos con la forma que nos
ofrece el propio framework. Es decir, tiene la misma interfaz que consigues mediante el cursor
del teclado que realizas con:

this.cursors = this.input.keyboard.createCursorKeys();

Usar el joystick

Ahora simplemente queda usar el objeto "joystickCursors" para poder saber qué direcciones

http://desarrolloweb.com/manuales/phaser3 Página 81 de 88
del joystick están siendo aplicadas, lo que realizarás generalmente en el método update.

En un escenario en el que quieras responder a los movimientos del teclado y a los movimientos
del cursor, para que el juego se pueda manejar desde el teclado en ordenadores y con el cursor
virtual en móviles, harías esto:

update() {
if (this.cursors.up.isDown || this.joystickCursors.up.isDown ) {
// están pulsando la tecla hacia abajo del cursor del teclado
// ...o el joystick está siendo operado hacia abajo.
// alguna de las dos posibilidades anteriores es verdad.
}
}

Este joystick virtual lo implementé en el juego de práctica del Manual de Phaser 3, en esta
rama de Github.

Aquí puedes ver un screenshot del juego una vez implementado el joystick.

Si no sabes cómo acceder a esta rama del juego puede interesarte primero aprender sobre
las ramas en Git y luego leer esta faq que explica cómo descargar una rama de un
repositorio remoto.

Joystick analógico

Una de las ventajas más interesantes de este joystick virtual es que funciona como un control
analógico, que además de decirte si está siendo operado en una dirección o otra (si o no =

http://desarrolloweb.com/manuales/phaser3 Página 82 de 88
digital), es capaz de decirte qué cantidad de fuerza está haciendo (cuanto más desplazas el
joystick hacia afuera del centro, más fuerza se supone que estás haciendo).

Este valor te lo entrega mediante dos parámetros:

Force, que es la cantidad de fuerza


Angle, que es el ángulo hacia donde se está operando el joystick

Mediante ambos valores puedes saber el estado del joystick, para saber en qué direcciones se
está haciendo fuerza y cuánto para cada lado.

Este es un resumen del código que se puede ver en la página de uso del plugin, enlazada desde
la documentación.

this.joyStick = this.plugins.get('rexvirtualjoystickplugin').add(this, {
x: 100,
y: 100,
radius: 100,
base: this.add.circle(0, 0, 100, 0x888888),
thumb: this.add.circle(0, 0, 50, 0xcccccc),
forceMin: 50,
})
.on('update', this.dumpJoyStickState, this);
}

dumpJoyStickState() {
var cursorKeys = this.joyStick.createCursorKeys();
console.log('Force: ', this.joyStick.force);
console.log('Angle: ', this.joyStick.angle);
}

Fíjate que estamos mandando a la consola de Javascript los valores de fuerza y ángulo.

Otra cosa interesante es el evento "update" aplicado en el joystick, que se ejecuta cuando el
joystick se mueve.

Otro detalle que hemos incorporado en este ejemplo es el valor "forceMin" en la configuración,
que indica la fuerza mínima hacia un lado para que se de por entendido que se está moviendo
el joystick en esa dirección. Aunque ese detalle no implica que el joystick no ejecute su
manejador update() si la presión no es suficiente, el manejador se invocará con cualquier
movimiento mínimo que se haga. Lo que permite el "forceMin" es que se no considere que el
joystick está marcando una dirección u otra si le hacemos una fuerza muy leve.

En fin, es un buen componente para construir un joystick virtual con muy poco esfuerzo, que
podemos instalar en cuestión de minutos si deseamos darles a nuestros usuarios de móviles
una interfaz viable para controlar un juego.

Este artículo es obra de Miguel Angel Alvarez


Fue publicado / actualizado en 08/07/2021
Disponible online en https://desarrolloweb.com/articulos/virtual-joystick-phaser

http://desarrolloweb.com/manuales/phaser3 Página 83 de 88
Crear una PWA con un juego de Phaser
Cómo convertir tu juego HTML5 desarrollado con Phaser en una PWA (Progressive Web App),
con habilidad para instalarlo en el dispositivo, jugar offline y permitir el uso a pantalla
completa.

Las PWA permiten disfrutar de muchas de las ventajas de las aplicaciones móviles, pero sin
tener que obligar a tus usuarios a pasar por los stores de aplicaciones. Por tanto, si tienes un
juego realizado con Phaser y deseas que se pueda instalar, jugar offline y a pantalla completa,
en realidad no necesitas hacer todo el trabajo de publicar tu juego en un store, te sirve
simplemente con disponer de una web con algunos extras que la conviertan en una Progressive
Web App. En este artículo te vamos a vamos a explicar cuáles son esos añadidos y cómo puedes
conseguir implementarlos.

Antes de comenzar cabe aclarar que las PWA son de aplicaciones web que pueden trabajar con
muchas cosas de las reservadas para aplicaciones móviles, instalables mediante Google Play y
similares, pero usando únicamente tecnologías web estándar. En Android funcionan genial,
incluso en ordenadores de escritorio se pueden instalar las PWA y disfrutar de características
extra. En iOS también funcionan, aunque lo cierto es que con algunas limitaciones.

Infelizmente la estrategia de Apple pasa por frenar las tecnologías web, para que los
desarrolladores y empresas estén obligados a publicar sus aplicaciones en la store, de modo
que todo el mundo tenga que pasar por caja y alimentar aún más la nutrida caja de ingresos
de la empresa de la manzana. Es por ello que las PWA se quedan un poco cojas en iOS. Es
una pena y esperamos que en futuro cambie la tendencia, para no tener un lastre en el
mundo web, ahora que la anterior oveja negra (Internet Explorer) ha desaparecido.

No es mi objetivo en este momento introducir las PWA, por lo que te recomiendo acceder a la
categoría de las Progressive Web App para obtener más información.

Qué necesitas para convertir un juego de Phaser en una PWA

Básicamente necesitamos lo que se conoce como Service Worker y un manifiesto, que son dos
piezas fundamentales, que caracterizan las PWA. Además, el sitio web debe colocarse en un
servidor web que permita SSL, es decir, tenemos que servirlo mediante https.

http://desarrolloweb.com/manuales/phaser3 Página 84 de 88
Manifiesto (site.webmanifest)

El manifiesto es un archivo en formato JSON que contiene metadatos sobre la PWA que
estamos desarrollando. El manifiesto toma el nombre de "site.webmanifest" y lo debes de colocar
en la raíz del dominio donde coloques tu aplicación.

Entre esos metadatos podemos encontrar:

Nombre de la aplicación
Iconos de la app
Colores del tema
Orientación de pantalla
La URL de inicio
El modo de display

Puedes encontrar más información sobre el archivo manifest en esta página.

Vamos a ver el ejemplo de manifiesto que hemos usado en nuestro juego.

{
"name": "Breakout Game",
"short_name": "Breakout",
"icons": [
{
"src": "/android-chrome-192x192.png",
"sizes": "192x192",
"type": "image/png"
},
{
"src": "/android-chrome-512x512.png",
"sizes": "512x512",
"type": "image/png"
},
{
"purpose": "maskable",
"src": "/android-chrome-512x512.png",
"sizes": "512x512",
"type": "image/png"
}
],
"theme_color": "#5E406A",
"background_color": "#243A3E",
"display": "fullscreen",
"orientation": "landscape",
"start_url": "/index.html"
}

Como ves, todas las propiedades son auto-explicativas, por lo que no creo que tengas problema
en entenderlas

Por supuesto, los iconos deben estar en las rutas donde se ha indicado. Sobre éstos cabe decir
que los puedes poner en cualquier formato, pero generalmente será PNG de 24 bits para que

http://desarrolloweb.com/manuales/phaser3 Página 85 de 88
tenga transparencia por canal alpha y comportarse bien sobre cualquier tipo de fondo. Hay
diversos sitios en la web que a partir de una imagen pueden generarte todo un set de iconos en
diversas resoluciones.

Service worker

La pieza más importante de las PWA es el service Worker. Consiste en un archivo de código
Javascript que es capaz de hacer por debajo la mayoría de la magia, para conseguir mejoras
importantes, entre las que se encuentran:

Cacheo
Funcionamiento offline
Funcionamiento en background
Recepción de notificaciones

En este artículo puedes encontrar más información de service workers.

Por supuesto, realizar un Service Worker no es algo sencillo, pero afortunadamente hay
herramientas que permiten generar el código del service worker a base de una declaración de
configuración. El ejemplo más conocido es Workbox.

Workbox tiene un CLI que te permite crear el service worker de manera prácticamente
inmediata. En este artículo no pensaba explicar el paso por paso para producir el service
worker, por lo que te recomiendo leer en la propia documentación del CLI (quizás más
adelante hagamos un tutorial por lo que puedes ver en el buscador a ver si se ha publicado).

Lo que sí te explico es que básicamente nuestro service worker se va a encargar de cachear los
archivos que son necesarios para que el juego pueda funcionar offline. Esto son, todas las
imágenes, iconos, el manifiesto, los archivos de sonido, los svg, json y por supuesto el html.

El CLI necesita que indiques toda esta configuración en un archivo, que puedes nombrar
workbox-config.js y que quedaría de esta manera.

module.exports = {
"globDirectory": "./www",
"globPatterns": [
"**/*.{png,xml,js,ico,html,json,svg,webmanifest,mp3,ogg}"
],
"swDest": "www/sw.js"
};

El service worker que puedes generar con Workbox podría disponer de otros servicios.
Nosotros solamente estamos explotando la característica de funcionamiento offline, para lo
que es necesario realizar el cacheo de todos los archivos del juego. Es básicamente lo que
hemos configurado en el anterior archivo Javascript.

http://desarrolloweb.com/manuales/phaser3 Página 86 de 88
Una vez instalado Workbox en tu ordenador, necesitas generar el service worker con el
comando siguiente:

workbox generateSW workbox-config.js

Esto generará el archivo del service worker en la ruta indicada "www/sw.js".

######### Registrar el service worker

En la página web de nuestro juego que se carga para arrancarse, es decir, en el index.html,
necesitamos registrar el service worker generado. Para ello usamos un poco de Javascript del
lado del cliente.

El registro del service worker lo consigues con un código como este:

<script>
// Check that service workers are supported
if ('serviceWorker' in navigator) {
// Use the window load event to keep the page load performant
window.addEventListener('load', () => {
navigator.serviceWorker.register('sw.js');
});
}
</script>

Publicar el proyecto en un servidor con https

Esta parte ya depende un poco de qué servicio de alojamiento quieras usar para la web. Como
hemos dicho, para una PWA es requisito indispensable disponer de un hosting con SSL, que
permita acceder por https al servidor.

Aquí las opciones básicas serían contratar un alojamiento que incluya el certificado de
seguridad en una empresa de hosting, o bien usar alguno de los servicios de alojamiento
gratuito en servidor seguro que existen en el mercado.

Como el sitio web consta de una única página HTML y una serie de assets normales (imágenes,
javascript, audio...), el tipo de alojamiento necesario sería el más básico. Por supuesto, no
requiere ningún lenguaje del lado del servidor, así que las necesidades serían perfectamente
compatibles con servicios que tienen una capa gratuita como:

GitHub Pages
Netlify
Firebase Hosting
Etc.

En nuestro canal de Youtube tenemos algunos vídeos que explican cómo configurar estos

http://desarrolloweb.com/manuales/phaser3 Página 87 de 88
servicios.

Conclusión

Con eso es todo. Gracias a estas mejoras puedes convertir tu juego Phaser en una PWA
instalable en dispositivos, con posibilidad de funcionamiento offline.

Para encontrar el proyecto de la PWA del juego que hemos ido desarrollando en el Manual de
Phaser, puedes acceder a este repositorio de GitHub.

Además, quizás te interese la lectura del Manual de PWA, ya que explica muchas cosas de las
aplicaciones progresivas que seguramente te venga bien conocer para cubrir posibles dudas y
profundizar en los temas que hemos mencionado.

Este artículo es obra de Miguel Angel Alvarez


Fue publicado / actualizado en 15/10/2021
Disponible online en https://desarrolloweb.com/articulos/pwa-juego-phaser

http://desarrolloweb.com/manuales/phaser3 Página 88 de 88

También podría gustarte