Control de ratón en Phaser

Cómo controlar el mouse desde Phaser


En el artículo anterior vimos como controlar nuestro juego a través de la personalización de acciones mediante de teclado. En este artículo vamos a hacer lo mismo, pero a través de ratón. En algunos escenarios el ratón puede ser clave para el desarrollo de nuestro videojuego, por lo que estaría bien conocer como gestionarlo en Phaser.

Muchas de las interacciones de ratón en Phaser se realizan mediante eventos. La primera columna son eventos que escuchamos sobre un objeto concreto, mientras que los eventos de la segunda columna son más genéricos:

Evento de objeto Evento genérico Descripción
pointerdown gameobjectdown Se dispara cuando el usuario hace clic en un objeto.
pointerup gameobjectup Se dispara cuando el usuario deja de hacer clic en un objeto.
pointermove gameobjectmove Se dispara continuamente cada vez que el usuario mueve el puntero.
pointerover gameobjectover Se dispara cuando el usuario se mueve sobre un objeto (una vez).
pointerout gameobjectout Se dispara cuando el usuario está dentro del objeto y sale de él.
wheel gameobjectwheel Se dispara cuando el usuario hace scroll sobre un objeto.

Veamos donde puede ser útil utilizar estos eventos.

El método .setInteractive()

Interactuar con el ratón en Phaser es un proceso muy sencillo, pero por defecto no puedes interactuar con ningún elemento. Si quieres hacerlo, debes llamar al método .setInteractive() sobre el objeto en cuestión. De esta forma, permitirá al jugador interactuar con ese elemento:

create() {
  const robot = this.add.image(x, y, "robot");
  robot.setInteractive();
}

Además, el método .setInteractive(hitArea, callback, dropZone) tiene 3 parámetros opcionales que puedes indicar si quieres personalizar al máximo cuando va a ser interactivo el elemento:

Parámetro Descripción
hitArea Forma geométrica que define la zona interactiva personalizada. Por defecto, todo el objeto.
callback Función para determinar si el puntero está dentro del hitArea.
dropZone Establece si el objeto es una zona para arrastrar y soltar. Por defecto, false.

Recuerda que puedes encadenar métodos, de modo que puedes ejecutar el .setInteractive() al final del método anterior, como haremos en los siguientes ejemplos.

Escuchar clics en objetos

Una vez hemos establecido como interactivo el elemento con el que queremos interactuar, sólo tenemos que escuchar el evento en cuestión que nos interesa.

Veamos este código completo donde permitimos interactuar con la imagen del robot del juego, tintándolo de rojo cuando pulsamos sobre él (evento pointerdown) y reseteándolo cuando soltamos el botón del ratón (evento pointerup).

Observa especialmente el método de ciclo de vida create():

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];

    const robot = this.add.image(x, y, "robot").setInteractive();

    robot.on("pointerdown", (pointer) => robot.setTint(0xff0000));
    robot.on("pointerup", (pointer) => robot.clearTint());
  }
}

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

const game = new Phaser.Game(config);

Recuerda que el método .setTint() pinta de un color el objeto, mientras que .clearTint() lo resetea a su estado original.

Diferenciando el botón de ratón

Como habrás observado en la demo, el ejemplo anterior interactua ante cualquier pulsación de ratón, ya sea con el botón principal, secundario o incluso con el botón central.

Observa esta variación del ejemplo anterior, donde utilizamos el parámetro pointer para detectar cuál es el botón que se ha pulsado:

create() {
  /* ... */

  robot.on("pointerdown", (pointer) => {
    const isLeftButton = pointer.button === 0;
    const isRightButton = pointer.button === 2;
    const isMiddleButton = pointer.button === 1;

    isLeftButton && robot.setTint(0xff0000);
  });
}

De esta forma se puede personalizar la acción a realizar dependiendo del botón pulsado.

Detectar movimiento sobre objetos

Utilizando el evento pointermove podríamos detectar cuando el usuario está moviendo el ratón sobre un objeto del juego. Recuerda que el evento pointerover se lanzaría solamente una vez, mientras que pointermove se lanza continuamente por cada minúsculo movimiento del puntero:

create() {
  /* ... */

  robot.on("pointermove", (pointer) => {
    robot.setTint(Math.random() * 16_000_000);
  });
}

Sin embargo, recordemos que en la tabla que vimos antes, habían dos columnas, donde en la segunda teníamos el evento genérico. Este evento es muy útil para realizar una acción sin centrarnos en un objeto concreto. Por ejemplo, observa el siguiente ejemplo donde escuchamos el evento gameobjectmove sobre this.input:

create() {
  /* ... */

  this.input.on("gameobjectmove", (pointer, gameObject) => {
    gameObject.setTint(Math.random() * 16_000_000);
  });
}

En este caso, escuchamos de forma genérica y el evento se dispararía con cualquier tipo de objeto interactivo. El objeto en cuestión se nos devolvería en el parámetro gameObject. De esta forma, aunque el código es equivalente al anterior, este último es mucho más flexible y potente en ciertos casos.

Detectar scroll con la rueda del ratón

Utilizando el evento wheel podemos detectar cuando el jugador realiza un movimiento de la rueda del ratón sobre un objeto del juego:

create() {
  /* ... */

  robot.on("wheel", (pointer, deltaX, deltaY, deltaZ) => {
    robot.scale += deltaY * -0.001;
  });
}

Los parámetros deltaX, deltaY y deltaZ nos devuelve información de la cantidad de scroll realizado con la rueda del ratón. Dependiendo del modelo del ratón, es posible que los últimos valores no existan.

Detectar coordenadas del ratón

Es posible que lo que nos interese es saber las coordenadas donde el jugador ha hecho click, para luego realizar nuestros cálculos. En ese caso, debemos utilizar el parámetro pointer, ya que contiene información de dichas coordenadas, concretamente en las propiedades x e y:

create() {
    // Coordenadas globales
    this.input.on("pointerdown", (pointer) => console.log(pointer.x, pointer.y));

    // Coordenadas locales
    this.input.on("pointerdown", (pointer, gameObject) => {
      const x = pointer.x - (gameObject.x - (gameObject.width / 2));
      const y = pointer.y - (gameObject.y - (gameObject.height / 2));
      console.log(x, y);
    });
}

Observa que en el primer caso, simplemente mostramos los valores de pointer.x y de pointer.y. Esto nos devolverá las coordenadas exactas de donde se ha hecho clic en la pantalla del videojuego.

Por otro lado, el segundo ejemplo, está realizando un cálculo para obtener las coordenadas donde se ha pulsado en el propio objeto del juego.

¿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