Control de teclado en Phaser

Cómo controlar nuestro juego mediante teclado


Una de las tareas principales de Phaser, al ser un motor de videojuegos, es proporcionarnos una forma de controlar nuestro personaje. Aunque hay varias formas, si queremos utilizar el teclado, probablemente la más sencilla y práctica es utilizar createCursorKeys().

El método createCursorKeys()

En Phaser podemos utilizar el método this.input.keyboard.createCursorKeys(), el cuál nos creará un objeto con las propiedades up , down , left , right , space ESPACIO y shift SHIFT, donde cada una hace referencia a otro objeto.

Cada uno de esos otros objetos, contienen varias propiedades muy útiles:

Propiedad Descripción
isDown Devuelve true si la tecla está pulsada.
isUp Devuelve true si la tecla está soltada.
ctrlKey Devuelve true si se ha pulsado con la tecla CTRL.
altKey Devuelve true si se ha pulsado con la tecla ALT.
shiftKey Devuelve true si se ha pulsado con la tecla SHIFT.
metaKey Devuelve true si se ha pulsado con la tecla WIN o CMD.

Las propiedades de este objeto son actualizadas en cada instante, de modo que es muy sencillo detectarlas. Basta con añadir el código de detección en la función update(), después de haber llamado en create() al createCursorKeys() y guardarlo en una variable de clase.

Observa el siguiente código, donde controlamos un pequeño robot. En él, usamos la propiedad .isDown para saber si está pulsada la tecla correspondiente:

import * as Phaser from "https://cdn.jsdelivr.net/npm/[email protected]/+esm";

const width = 640;
const height = 480;

export class Screen extends Phaser.Scene {
  preload() {
    this.load.image("robot", `robot.png`);
  }

  create() {
    const [x, y] = [width / 2, height / 2];
    this.robot = this.add.image(x, y, "robot");
    this.cursors = this.input.keyboard.createCursorKeys();
  }

  update() {
    const { left, right, down, up } = this.cursors;

    if (left.isDown) this.moveRobot(-2, 0);
    else if (right.isDown) this.moveRobot(2, 0);
    else if (up.isDown) this.moveRobot(0, -2);
    else if (down.isDown) this.moveRobot(0, 2);
  }

  moveRobot(x, y) {
    this.robot.x += x;
    this.robot.y += y;
  }
}

const config = {
  type: Phaser.AUTO,
  width,
  height,
  autoCenter: Phaser.Scale.Center.CENTER_BOTH,
  scene: [ Screen ]
};

const game = new Phaser.Game(config);

Observa que hemos desestructurado las propiedades left, right, down y up. Esto nos permite acceder a los objetos de la forma más sencilla posible. En cada acceso, ejecutamos el método de clase moveRobot() que nos permite mover el personaje de posición.

Permitir múltiples direcciones

Si lo hacemos de esta forma, ten en cuenta que son condicionales donde sólo puede ocurrir una a la vez, por lo que el robot no podrá ir en diagonal (izquierda + abajo, por ejemplo). Si queremos permitir que se pueda mover en ambas direcciones a la vez, podriamos modificar y reemplazar el bloque de código del update() por el siguiente:

update() {
  const { left, right, down, up } = this.cursors;

  left.isDown && this.moveRobot(-2, 0);
  right.isDown && this.moveRobot(2, 0);
  up.isDown && this.moveRobot(0, -2);
  down.isDown && this.moveRobot(0, 2);
}

En este caso, estamos haciendo algo equivalente a condicionales if individuales, sin else, por lo que se permite mover el robot en varias direcciones.

Añadir teclas con addKeys()

Es muy probable que no nos baste con las teclas que vienen por defecto en el objeto, y queramos utilizar más teclas, como por ejemplo, la tecla Z y la tecla X. Para añadirlas, simplemente, tras la creación del objeto mediante createCursorKeys(), añadimos las teclas que necesitemos con el método addKey():

create() {
  /* ... */
  this.cursors = this.input.keyboard.createCursorKeys();

  this.cursors.z = this.input.keyboard.addKey(Phaser.Input.Keyboard.KeyCodes.Z);
  this.cursors.x = this.input.keyboard.addKey(Phaser.Input.Keyboard.KeyCodes.X);
}

Como puedes ver, hemos añadido cada tecla al objeto cursors, en su propiedad correspondiente. Aquí tienes la lista de KeyCodes de Phaser, por si quieres buscar el nombre de otra tecla.

Si vamos a añadir varias teclas, quizás nos interese más utilizar el método addKeys(), y hacerlo en una sola línea, pasándole un objeto con las teclas adicionales por parámetro:

create() {
  /* ... */
  this.cursors = {
    ...this.input.keyboard.createCursorKeys(),
    ...this.input.keyboard.addKeys({
      'z': Phaser.Input.Keyboard.KeyCodes.Z,
      'x': Phaser.Input.Keyboard.KeyCodes.X
    })
  };
}

Una vez hecho esto, en el método update() tendríamos que seguir los pasos anteriores para detectar cuando se pulsa la nueva tecla, utilizando también el isDown:

update() {
  const { left, right, down, up, z } = this.cursors;

  if (left.isDown) this.moveRobot(-2, 0);
  else if (right.isDown) this.moveRobot(2, 0);
  else if (up.isDown) this.moveRobot(0, -2);
  else if (down.isDown) this.moveRobot(0, 2);

  if (z.isDown) console.log("Z Key pressed!!");
}

Detectar combos con createCombo()

En ciertos casos, probablemente nos interese detectar «combos de teclas», es decir, combinaciones muy concretas de teclas. Por ejemplo, una muy conocida es el Código KONAMI, que consiste en pulsar la siguiente combinación de teclas: BA, utilizada para desbloquear trucos o huevos de pascua (tienes uno en Manz.dev).

Para hacer esto en Phaser, sólo tenemos que escribir lo siguiente:

const KONAMI_CODE = [38, 38, 40, 40, 37, 39, 37, 39, 66, 65];

create() {
  const konamiCombo = this.input.keyboard.createCombo(KONAMI_CODE, { resetOnMatch: true });

  this.input.keyboard.on("keycombomatch", (combo, event) => {
    if (combo === konamiCombo) {
      this.konamiCombo();
    }
  });
}

En este caso, creamos el combo con la combinación de teclas (con los keyCodes que vimos anteriormente), y ponemos a escuchar un evento que estará pendiente cuando ocurra un combo. En cuanto ocurra, comprobamos si se trata del código konami, y si lo es, ejecutamos el método konamiCombo(), que habría que definir.

Detectar pulsaciones de teclas

Los mecanismos vistos anteriormente son los más útiles en juegos, ya que podemos detectar cuando una tecla ha sido pulsada de forma prolongada, desde el ciclo de vida update(). Sin embargo, en algunas ocasiones nos interesará simplemente detectar si se ha pulsado una tecla, sin más.

En esos casos, suele ser más práctico utilizar eventos, y escucharlos en el ciclo de vida create():

create() {
  // Escuchar cualquier tecla de teclado
  this.input.keyboard.on("keydown", ({ key }) => {
    console.log("Tecla presionada: ", key)
  });

  // Escuchar una tecla específica
  this.input.keyboard.on("keydown-P", (event) => {
    console.log("Tecla P presionada.");
  });
}

Observa que, simplemente, separando por guión, podemos escuchar eventos de teclas concretas. Por último, recuerda que también existe el evento keyup para detectar cuando se suelta una tecla.

¿Quién soy yo?

Soy Manz, vivo en Tenerife (España) y soy streamer partner en Twitch y profesor. Me apasiona el universo de la programación web, el diseño y desarrollo web y la tecnología en general. Aunque soy full-stack, mi pasión es el front-end, la terminal y crear cosas divertidas y locas.

Puedes encontrar más sobre mi en Manz.dev