Imágenes en Phaser

Trabajando con GameObjects: Images


Phaser está basado en el paradigma de la orientación a objetos, por lo que al crear una imagen, tenemos a nuestra disposición múltiples propiedades y métodos para manipular y modificar nuestra imagen. Echemos un vistazo a las cosas que podemos realizar con una imagen.

Cargar una imagen en Phaser

Lo primero y esencial, recordemos que es cargar la textura de una imagen y darle un nombre (en la fase de preload) y luego añadirla a nuestro juego. Esto se puede hacer con el siguiente código en nuestra escena:

import Phaser from "phaser";
const SPRITES_PATH = "assets/sprites";

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

  create() {
    const logo = this.add.image(0, 0, "logo");
  }
}

Recuerda que es imprescindible realizar el proceso de carga de texturas antes de añadirla, de lo contrario nos aparecerá un cuadrado verde, con una línea diagonal, que simboliza que no ha encontrado la textura o imagen indicada.

Tienes más información sobre las diferentes formas de crear objetos en Phaser en este artículo.

Posición de la imagen

Si nos fijamos, al utilizar el método this.add.image() hemos indicado la posición de la pantalla donde vamos a colocar nuestra imagen. En este ejemplo hemos indicado 0x0, lo que significa que se ubicará en el punto superior izquierdo de la pantalla.

En cualquier momento podemos modificar esta posición, simplemente indicando los nuevos valores con el método .setPosition():

create() {
  const logo = this.add.image(0, 0, "logo");
  logo.setPosition(100, 25);

  // O por separado (interesante si sólo queremos cambiar una):
  logo.setX(100);
  logo.setY(25);

  // Si queremos consultar la posición:
  console.log(logo.x); // 100
  console.log(logo.y); // 25
}

Un detalle importante a tener en cuenta es que las imágenes tienen por defecto establecido el punto de origen en el centro de la imagen. En Phaser, el punto de origen es un valor de 0 a 1, donde 0 es el inicio y 1 es el final. Por ejemplo, 0x0 sería la esquina superior-izquierda, 1x1 sería la esquina inferior-derecha y 1x0.5 sería todo a la derecha y al centro en el eje Y.

Para poder colocar más fácil nuestra imagen, podemos cambiar el punto de origen de esta forma:

create() {
  const logo = this.add.image(0, 0, "logo")
    .setOrigin(0, 0)
    .setPosition(0, 0);
}

Hemos establecido el punto de origen en el punto 0x0 (la esquina superior-izquierda) y la hemos colocado en la posición 0,0 de la escena. Además, en este caso, en lugar de repetir varias veces la variable logo para llamar a los métodos, he encadenado un método con otro, para simplificar el código.

Por aquí tienes otras propiedades y métodos interesantes para imágenes en Phaser. Ten en cuenta que la propiedad depth, en el caso de tener muchas imágenes, nos permite moverlas delante o detrás de las demás imágenes, similar a como lo hace z-index en CSS:

Propiedad Método asociado Descripción
x
y
setX()
setY()
setPosition()
Posición de la imagen en x,y.
originX
originY
setOrigin() Punto de origen de la imagen.
displayOriginX
displayOriginY
setDisplayOrigin() Punto de origen visual (aparente).
scrollFactorX
scrollFactorY
setScrollFactorX()
setScrollFactorY()
Factor de desplazamiento horizontal y vertical.
depth setDepth() Profundidad de elemento respecto a otros.

Rotación y tamaños

Obviamente, podemos modificar la rotación o los tamaños de una imagen. Para ello utilizaremos algunos de los siguientes métodos:

Propiedad Método asociado Descripción
angle / rotation setRotation() Ángulo de rotación (grados), rotation (radianes).
displayWidth
displayHeight
setDisplaySize() Anchura o altura visual en píxels.
scale
scaleX
scaleY
setScale()
setScaleX()
setScaleY()
Escala la proporción de la imagen (de 0 a 1).
width / height setSize() Anchura o altura (usado para colisiones o interacciones).
flipX
flipY
setFlipX() / toggleFlipX()
setFlipY() / toggleFlipY()
Invierte la imagen horizontal o verticalmente.

Veamos algunos ejemplos con estos métodos:

create() {
  const logo = this.add.image(0, 0, "logo");

  logo.setRotation(45);         // Rotamos 45 grados
  logo.setScale(0.5);           // Imagen de 100x100 se reduce a 50x50 (0.5 = 50%)
  logo.setDisplaySize(100, 50); // Imagen de 100x100 se reduce a 100x50 (se deforma)
  logo.flipX(true);             // Imagen se invierte horizontalmente
  logo.toggleFlipY(true);       // Imagen se invierte verticalmente
}

Observa que también hay un método setSize(). Este método, a diferencia de setDisplaySize() se utiliza para cambiar el tamaño de la imagen respecto a tareas de interacción (colisiones, pulsar sobre ella, etc...), pero no respecto a como se ve visualmente.

Recortar imagen

Nos puede interesar en algún momento realizar un recorte (conocido como crop) para quedarnos con sólo un fragmento de la imagen. Esto es muy sencillo de realizar con el método setCrop(x, y, w, h). Simplemente, indicamos el punto x,y donde vamos a empezar a recortar, y el tamaño de ancho y de alto donde vamos a hacer recorte:

Propiedad Método asociado Descripción
isCropped setCrop() Comprueba si la imagen está recortada.

Por ejemplo, en el siguiente código añadimos una imagen que ocupa 880x199 píxels. Con setCrop() recortamos el fragmento desde 0,0 que ocupa 145x199 píxels:

create() {
  const logo = this.add.image(0, 0, "logo");

  logo.setCrop(0, 0, 145, 199);
  console.log(logo.isCropped);  // true
}

Finalmente, con la propiedad isCropped podemos saber si la imagen está recortada o no.

Tintar una imagen

Una operación rápida que podría interesarnos es la de tintar una imagen. Phaser tiene algunos métodos para realizar estas operaciones, vamos a analizarlas:

Propiedad Método asociado Descripción
tintFill setTintFill()
clearTintFill()
Relleno de tinta de la imagen.
tint setTint()
clearTint()
Tinta de la imagen.
isTinted Comprueba si la imagen tiene tinta.

En primer lugar, vamos a utilizar setTintFill(), que permite aplicar un color sólido, reemplazando la imagen, pero conservando su forma. Ten en cuenta que se le puede pasar uno o varios parámetros:

create() {
  const logo = this.add.image(0, 0, "logo");

  // Aplica el color rojo #ff0000 a toda la imagen
  logo.setTintFill(0xff0000);

  // Aplica colores diferentes a cada esquina de la imagen
  logo.setTintFill(0xff0000, 0x00ff00, 0x0000ff, 0xff00ff);
}

Los colores se aplican en formato hexadecimal (numérico), simplemente cambiando el # por un 0x. Por otro lado, también tenemos el metodo setTint(), que a diferencia de setTintFill() aplica la tinta como si fuera un modo de fusión, por lo que conserva parte de los colores y textura original.

Además de todo esto, tenemos también los métodos clearTint() y clearTintFill() para borrar el tintado de una imagen o el isTinted para comprobar si una imagen está tintada o no.

Visibilidad de una imagen

De forma similar, también se puede modificar la visibilidad de una imagen, utilizando sus canales alfa y modificando el grado de transparencia, o utilizando sus modos de fusión:

Propiedad Método asociado Descripción
alpha setAlpha() Transparencia del objeto.
blendMode setBlendMode() Modo de fusión en la imagen.
visible setVisible() Muestra u oculta la imagen.
active setActive() Activa o desactiva el objeto.
create() {
  const logo = this.add.image(0, 0, "logo");

  // Pone la opacidad del logo al 25%
  logo.setAlpha(0.25);

  // Aplica opacidades diferentes a cada esquina de la imagen
  logo.setAlpha(1, 0.5, 0.1, 0.05);
}

Por otro lado, Phaser tiene también un sistema de modos de fusiones para combinar imágenes con otras imágenes o colores. Es algo similar a lo que vimos con setTint(), pero también se puede aplicar a múltiples imágenes:

const image = this.add.image(0, 0, "image");
const logo = this.add.image(0, 0, "logo");

logo.setBlendMode(Phaser.BlendModes.MULTIPLY);

logo.setVisible(false);
logo.setActive(true);

Existen varios modos de fusión que podemos utilizar, donde los más comunes podrían ser ADD, MULTIPLY, NORMAL y SCREEN.

Por otro lado, tenemos dos estados muy importantes en los objetos: setVisible() y setActive(). Igual que las escenas, un objeto de Phaser también puede estar activo y visible (o no). Por un lado, que un objeto sea visible simplemente indica si la imagen debe renderizarse visualmente, pero si está activo puede seguir actualizándose su lógica. Por otro lado, que un objeto se encuentre activo significa que su lógica seguirá actualizándose, pero puede no ser visible si visible está a false:

Visible Activo Descripción
El objeto es visible y sigue actualizando su lógica.
El objeto es invisible, pero sigue actualizándose su lógica.
El objeto es visible, pero no actualiza su lógica.
El objeto no se renderizará visualmente ni actualizará su lógica.

¿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