Curso de videojuegos con Phaser. Parte 1

¿Que es Phaser?

Phaser es un framework open source para Javascript con el que podemos crear videojuegos en HTML5 tanto para escritorio como móviles, y que hace uso de WebGL y la librería Pixi.js para renderizar de una forma rápida.

Debemos tener en cuenta que cuando desarrollamos con Phaser para un dispositivo móvil, el código no se compila a código nativo si no que mediante herramientas de terceros como Apache Cordova ejecutara nuestro código Javascript en un navegador interno.

Bien, ahora que ya sabemos que es Phaser vamos a comenzar con su instalación y las herramientas necesarias para crear nuestros videojuegos.

Preparando nuestro entorno

Para empezar necesitaremos un servidor web, y para ello podemos usar WAMP Server, XAMPP, http-server (nodejs)live-server (plugin vscode)

En este curso usare live-server (plugin visual studio code)ya que se integra directamente en el IDE, es ligero y cumple su función perfectamente.

El siguiente requisito es instalar el framework, y para ello usaremos el paquete de Phaser que se encuentra en npm.

A continuación ejecutaremos los siguientes comandos desde la consola para iniciar el proyecto e instalar el framework.

npm init -y
npm i phaser3-project-template
npm i phaser
npm i

En este punto ya tenemos los requisitos mínimos para comenzar a desarrollar en Phaser.

Empezando con Phaser

Cuando ejecutamos el comando npm i phaser3-project-template nos crea dentro de la carpeta node_module/phaser3-project-template el contenido necesario para empezar cualquier proyecto con Phaser desde cero.

Lo siguiente que haremos es copiar el contenido de esa carpeta en la raíz de nuestro proyecto, quedando de la siguiente forma.

Estructura básica

En este punto ejecutaremos el siguiente comando para iniciar nuestro videojuego.

npm run start

Proyecto arrancado

Como hemos podido observar, empezar un proyecto desde cero en Phaser es bastante simple.

Ahora veremos la estructura que nos ha creado Phaser, ya que necesitamos asentar las bases y tener mayor control sobre el proyecto.

Modificando el proyecto

De todos los archivos que se han creado, de momento solo necesitamos modificar los siguientes ficheros:

  • index.html
  • src/index.js

index.html

<!DOCTYPE html>
<html>
  <head>
    <meta charset="utf-8" />
  </head>
  <body>
    <!-- 
    Esto div no esta en el archivo original, lo he incluido para tener un mayor control.
    Si no añadimos una zona donde volcar el contenido de Phaser, este creara un canvas automático.
    -->
    <div id="containerGame"></div>
    <script src="build/project.bundle.js" charset="utf-8"></script>
  </body>
</html>

src/index.js

import "phaser";

var config = {
  type: Phaser.AUTO,
  parent: "containerGame",
  width: 800,
  height: 600,
  scene: {
    preload: preload,
    create: create,
  },
};

var game = new Phaser.Game(config);

function preload() {
  this.load.image("logo", "assets/logo.png");
}

function create() {
  var logo = this.add.image(400, 150, "logo");

  this.tweens.add({
    targets: logo,
    y: 450,
    duration: 2000,
    ease: "Power2",
    yoyo: true,
    loop: -1,
  });
}

A continuación abriremos el archivo src/index.js para modificar algunas de las opciones que podremos encontrar en la documentación oficial de Phaser 3.

Dentro del fichero src/index.js nos centraremos en la configuración, ya que es una parte importante y definirá la base para la creación de nuestro videojuego.

src/index.js

import "phaser";

var config = {
  // Modo renderización del Canvas.
  type: Phaser.AUTO,
  // El elemento del DOM que contendra el canvas, usando el atributo "id".
  parent: "containerGame",
  // Ancho del canvas.
  width: 800,
  // Alto del canvas.
  height: 600,
  // Título que se mostrara en la ventana del navegador
  title: "Creando videojuegos con Phaser 3",
  // URL del juego
  utl: "https://tecnops.es",
  // Versión del juego
  version: "0.0.1",
  /* 
    En la consola del navegador se muestra cierta información.
    Con esta configuración se puede modificar
  */
  banner: {
      text: "#000000",
      background: ["#ff0000","#ff5500", "#ffaa00"],
      hidePhaser: true,
  },
  // Phaser trabaja mediante escenas (scene). Podríamos decir que esta escena equivale a un nivel, pantalla o mundo donde se desarrolla el videojuego
  scene: {
    preload: preload,
    create: create,
  },
};

var game = new Phaser.Game(config);

function preload() {
  this.load.image("logo", "assets/logo.png");
}

function create() {
  var logo = this.add.image(400, 150, "logo");

  this.tweens.add({
    targets: logo,
    y: 450,
    duration: 2000,
    ease: "Power2",
    yoyo: true,
    loop: -1,
  });
}

Bien, con esta pequeña modificación ya entendemos las implicaciones que tiene esa primera configuración.

Ahora continuamos montando nuestra primera escena (nivel, pantalla, mundo, etc…).

Nuestra primera escena

Al comienzo de la entrada, definimos que era una escena de una forma superficial, ahora entraremos en detalle sobre esta parte que es bastante importante.

Una escena (scene) como habíamos definido puede ser una pantalla, por ejemplo ajustes del juego, selección de personajes, un nivel de nuestro videojuego, etc…

Las escenas tienen un ciclo de vida que nos permite hacer ciertas tareas en ciertos momentos del tiempo en nuestro videojuego.

A continuación explicaremos los métodos que componen el ciclo de vida de una escena (scene).

  • init(): Donde se inicializan los datos y la carga de los mismos.
  • preload(): Hacemos la precarga de los recursos de nuestro videojuego.
  • create(): Creamos los elementos que se han precargado.
  • update(): Este método podemos decir que es el mas importante, ya que se repite constantemente y es donde se produce toda la acción de nuestro videojuego.

Ya tenemos nuestro código base y sabemos cual es el ciclo de vida de una escena (scene) en Phaser, ahora nos toca empezar a organizar nuestro código.

Organizando el código

Para organizar nuestro código crearemos una clase que extienda de Phaser y allí añadiremos el código necesario para nuestro videojuego.

src/index.js

import "phaser";
import Menu from "./menu";
var config = {
  // Modo renderización del Canvas.
  type: Phaser.AUTO,
  // El elemento del DOM que contendra el canvas, usando el atributo "id".
  parent: "containerGame",
  // Ancho del canvas.
  width: 800,
  // Alto del canvas.
  height: 600,
  // Título que se mostrara en la ventana del navegador
  title: "Creando videojuegos con Phaser 3",
  // URL del juego
  utl: "https://tecnops.es",
  // Versión del juego
  version: "0.0.1",
  /* 
    En la consola del navegador se muestra cierta información.
    Con esta configuración se puede modificar
  */
  banner: {
    text: "#000000",
    background: ["#ff0000", "#ff5500", "#ffaa00"],
    hidePhaser: true,
  },
  // Phaser trabaja mediante escenas (scene). Podríamos decir que esta escena equivale a un nivel, pantalla o mundo donde se desarrolla el videojuego
  scene: [Menu],
};

var game = new Phaser.Game(config);

src/menu.js

export default class Menu extends Phaser.Scene {
  constructor() {
    super('Menu');
  }
  init() {
    console.log("INIT");
  }
  preload() {
    console.log("PRELOAD");
    this.load.image("logo", "assets/logo.png");
  }
  create() {
    console.log('CREATE');
    var logo = this.add.image(400, 150, "logo");
    this.tweens.add({
      targets: logo,
      y: 450,
      duration: 2000,
      ease: "Power2",
      yoyo: true,
      loop: -1,
    });
  }
  update(time, delta) {
      console.log(time, delta);
  }
};

Como hemos podido comprobar el código ahora esta mucho mas organizado y sencillo, pudiendo ampliar y escalar nuestro videojuego de una forma controlada.

El siguiente paso sera la carga y manipulación de una imagen (sprite)

Cargando un sprite

A continuación podemos guardar la siguiente imagen u otra cualquiera para añadirla a nuestra escena.

El código final quedaría así.

export default class Menu extends Phaser.Scene {
  constructor() {
    super('Menu');
  }
  init() {
    console.log("INIT");
  }
  preload() {
    console.log("PRELOAD");
    // Definimos la ruta base
    this.load.path = "/assets/"

    // Cargamos las imagenes (forma abreviada)
    this.load.image(["ball"]);
  }
  create() {
    console.log('CREATE', this);

    // Obtenemos el ancho y el alto del canvas
    const { game: { config: { height, width } } } = this;
    
    // Añadimos la imagen
    var ball = this.add.image(width / 2, height, "ball");
    
    // Modificamos las propiedades del elemento
    this.tweens.add({
      targets: ball,
      y: 400,
      duration: 800,
      ease: "Power2",
      yoyo: true,
      loop: -1,
    });
  }
  update(time, delta) {
    // console.log(time, delta);
  }
};

En el código anterior podemos ver como estamos haciendo uso del ciclo de vida de Phaser, y para comprender mejor el flujo vamos explicar cada punto:

  1. En el fichero src/index.js estamos añadiendo la escena (src/menu.js), y en ese momento se invoca el método constructor() dentro del cual invocaremos a la palabra reservada  super(‘Menu’) indicando como parámetro el nombre de la escena.
  2. El siguiente código que se ejecutara es el método init(), aunque en este ejemplo no lo usamos para nada.
  3. El método preload() es el siguiente en ejecutarse, y en este momento sera donde realizamos la carga de nuestros recursos.
  4. Ahora continua la ejecución en el método create(), y es aquí donde añadimos y/o creamos nuestros recursos.
  5. Para finalizar, y aunque en este ejemplo no lo estamos usando, tocaría el turno al método update(). Este método se repite constantemente y es donde ocurre toda la ejecución de nuestro videojuego.

A continuación un pequeño vídeo de nuestro código en acción.

Para finalizar esta primera parte del curso de Phaser añadiré el enlace a la documentación y de esta forma podrás realizar algunas pruebas.

También puedes acceder al repositorio en Github donde encontraras todo el código y algunas mejoras sobre el código publicado en esta parte del curso.