Curso de videojuegos con Phaser. Parte 2

Controlando nuestro videojuego

En la entrada anterior del curso de Phaser aprendimos como se instala y configura un proyecto desde cero, pero ahora necesitamos dar un paso mas y para ello en esta entrada aprenderemos a interactuar con nuestro videojuego usando el teclado y el ratón.

Para continuar el curso aunque no es obligatorio, podéis descargar el repositorio de Github.

Empezaremos esta segunda parte accediendo al teclado y para ello Phaser nos ofrece una serie de métodos y definiciones.

Empezando a usar el teclado

Si queremos asignar una tecla para realizar cualquier acción, existe el método addKey() y addKeys({}) que reciben como parámetro el código de la tecla (keycode) y un objeto de teclas (keycodes).

El listado de keycodes lo puedes obtener accediendo a la documentación oficial o al namespace que ofrece la propia librería.

Desde la consola del navegador podemos ejecutar la siguiente sentencia:

Los códigos anteriores son algunos de los 98 keycodes que ofrece Phaser.

A continuación haremos un ejemplo donde añadiremos la interacción del teclado en nuestro videojuego.

export default class Menu extends Phaser.Scene {
    constructor() {
        super('Menu');
        this.limit = false;
        this.direcction = 3;
    }
    init() {}
    preload() {
        // Definimos la ruta base
        this.load.path = "/assets/";

        // Cargamos las imagenes (forma abreviada)
        this.load.image(["ball", "spaceship"]);
    }
    create() {
        // Obtenemos el ancho y el alto del canvas
        const { game: { config: { height, width } } } = this;
        this.screenWidth = width;
        this.screenHeight = height;

        // Añadimos la imagen
        this.ball = this.add.image(width / 2, height, "ball");
        this.t = this.createSpaceShip("spaceship", 10, .15);

        // Modificamos las propiedades del elemento
        this.tweens.add({
            targets: this.ball,
            y: 400,
            duration: 600,
            ease: "Power2",
            yoyo: true,
            loop: -1,
        });

        const { LEFT, RIGHT, UP, DOWN } = Phaser.Input.Keyboard.KeyCodes;

        this.controls = this.input.keyboard.addKeys({
            LEFT,
            RIGHT,
            UP,
            DOWN,
        });

        this.controls.LEFT.on('down', () => {});
        this.controls.RIGHT.on('down', () => {});
        this.controls.UP.on('down', () => {});
        this.controls.DOWN.on('down', () => {});
    }
    update(time, delta) {
        // Vamos a realizar la detección de una forma sencilla, hasta que veamos la parte de físicas
        if (this.ball.x >= this.game.config.width || this.ball.x < 0) {
            this.limit = true;
            this.direcction = this.ball.x >= this.game.config.width ? -3 : 3;
        } else {
            this.limit = false;
        }
        this.ball.x += this.direcction;

        this.spaceship.x -= this.controls.LEFT.isDown ? this.spaceship.speed : 0;
        this.spaceship.x += this.controls.RIGHT.isDown ? this.spaceship.speed : 0;
        this.spaceship.y -= this.controls.UP.isDown ? this.spaceship.speed : 0;
        this.spaceship.y += this.controls.DOWN.isDown ? this.spaceship.speed : 0;

        let limitLEFT = this.spaceship.x < this.spaceship.widthScale / 2;
        let limitRIGHT = this.spaceship.x > this.game.config.width - this.spaceship.widthScale / 2;
        let limitUP = this.spaceship.y < this.spaceship.widthScale / 2;
        let limitDOWN = this.spaceship.y > this.game.config.height - this.spaceship.widthScale / 2;

        this.spaceship.x = limitLEFT ? this.spaceship.widthScale / 2 : this.spaceship.x;
        this.spaceship.x = limitRIGHT ? this.game.config.width - this.spaceship.widthScale / 2 : this.spaceship.x;
        this.spaceship.y = limitUP ? this.spaceship.widthScale / 2 : this.spaceship.y;
        this.spaceship.y = limitDOWN ? this.game.config.height - this.spaceship.widthScale / 2 : this.spaceship.y;
    }
    createSpaceShip(id, speed, scaleFactor) {
        this.spaceship = this.add.image(0, 0, id);
        this.spaceship.scaleFactor = scaleFactor;
        this.spaceship.x = this.screenWidth / 2;
        this.spaceship.y = this.screenHeight / 2;
        this.spaceship.setScale(this.spaceship.scaleFactor, this.spaceship.scaleFactor);
        this.spaceship.speed = speed;
        this.spaceship.widthScale = this.spaceship.width * this.spaceship.scaleFactor;
    }
};

En el código anterior hemos visto como se hace uso del método addKey(), addKeys() y como podemos añadir los keycodes.

A continuación veremos como obtener los keycodes y para ello podemos hacerlo usando alguna de las siguientes formas:

  • Realizando una desestructuración
    • const { LEFT, RIGHT, UP, DOWN } = Phaser.Input.Keyboard.KeyCodes
  • Asignación común:
    • const LEFT = Phaser.Input.Keyboard.KeyCodes.LEFT;
      
  • Asignación de un objeto:
    • const keyboardCodes = Phaser.Input.Keyboard.KeyCodes;

El siguiente paso es añadir al método addKey() addKeys() los keycodes, y para ello lo podemos realizar de diferentes formas:

  • Podemos añadir una a una las teclas:
    • this.keyLEFT = this.input.keyboard.addKey(LEFT);
      this.keyRIGHT = this.input.keyboard.addKey(RIGHT);
  • También podemos añadir un objeto de keycodes:
    • this.input.keyboard.addKeys({ LEFT, RIGHT, });

Para finalizar solo nos queda escuchar la tecla para realizar o no la acción asociada:

  • this.controls.LEFT.on('down', () => { console.log('LEFT'); }); 
    this.controls.RIGHT.on('down', () => { console.log('RIGHT'); });

Con los métodos anteriores ya podemos controlar nuestro videojuego con el teclado, no obstante para conocer todas las características que ofrece Phaser siempre aconsejo acudir a la documentación oficial.

Controlando nuestro videojuego con el ratón

De la misma forma que Phaser nos ofrece un namespace para el teclado (Phaser.input.Keyboard), también tiene un namespace para los eventos (Phaser.input.Events).

Para ver el listado de todos los eventos ejecutaremos desde la consola del navegador el siguiente comando.

Phaser.Input.Events

Esta sentencia nos devuelve todos los eventos, pudiendo de esta forma escuchar el evento que necesitemos y ejecutar el código.

A continuación ampliaremos el código anterior para realizar un ejemplo:

...
// Eventos del ratón
const { POINTER_MOVE, GAME_OVER, GAME_OUT, GAMEOBJECT_DOWN, GAMEOBJECT_UP } = Phaser.Input.Events;

// Movemos el ratón por el canvas

this.input.on(POINTER_MOVE, evt => {
   if (evt.isDown) {
       this.ironman.x = evt.worldX;
       this.ironman.y = evt.worldY;
   }
});

// Controlamos cuando estamos dentro y fuera del canvas
this.input.on(GAME_OVER, evt => {
   console.log('DENTRO DEL CANVAS');
});
this.input.on(GAME_OUT, evt => {
   console.log('FUERA DEL CANVAS');
});

// Comprobar que gameObject hemos seleccionado
this.input.on(GAMEOBJECT_DOWN, (pointer, gameObject) => {
   gameObject.setScale(.50, .50);
});
this.input.on(GAMEOBJECT_UP, (pointer, gameObject) => {
   gameObject.setScale(.15, .15);
});
...

Como podemos observar, el control del ratón es mas sencillo que el teclado ya que solo necesitamos capturar el evento y añadir el código a ejecutar.

Para terminar esta segunda parte del curso, os añado el enlace a Github con todo el código actualizado y un pequeño ejemplo para ver los resultados.