Como utilizar un Sprite Sheet en Flutter con Flame 13 - Juegos 2D animados

Un sprite no es más que un objeto o personaje y son muy utilizados en cualquier motor de videojuegos como Unity; en el desarrollo de videojuegos, los sprites es donde están incluidos todos los movimientos (estados) de un personaje u objeto; por lo tanto, es común que para un juego 2D existan múltiples imágenes para cada objeto animable. 

nos interesa no solamente procesar una sola imagen, sino, varias de ellas, para ello, utilizaremos una imagen que conste de varias imágenes, como la generada anteriormente.

Un sprite sheet es una imagen que contiene varios sprites o imágenes más pequeñas utilizadas en el desarrollo de juegos en 2D. Los sprites shhets suelen representar objetos , personajes y elementos del juego, y pueden ser estáticos o animados. La hoja de sprites permite que el juego cargue varias imágenes necesarias para el juego de una sola vez, lo que puede mejorar la eficiencia en la carga y el rendimiento general del juego. Al tener todas las imágenes en una sola imagen, facilita la gestión y organización de los recursos gráficos en el desarrollo de juegos en 2D.

Caso práctico

Finalmente, a nivel de nuestro código, crearemos la siguiente clase:

lib/components/player_sprite_sheet_component.dart

import 'package:flame/sprite.dart';
import 'package:flutter/material.dart';

import 'package:flame/flame.dart';
import 'package:flame/components.dart';

import 'dart:ui';


class PlayerSpriteSheetComponent extends SpriteComponent {
  late double screenWidth, screenHeight, centerX, centerY;
  final double spriteSheetWidth = 680, spriteSheetHeight = 472;

  @override
  Future<void>? onLoad() async {
    //sprite = await Sprite.load('tiger.png');

    final spriteImage = await Flame.images.load('dino.png');
    final spriteSheet = SpriteSheet(image: spriteImage, srcSize: Vector2(spriteSheetWidth, spriteSheetHeight));

    sprite = spriteSheet.getSprite(2, 1);

    screenWidth = MediaQueryData.fromWindow(window).size.width;
    screenHeight = MediaQueryData.fromWindow(window).size.height;

    size = Vector2(spriteSheetWidth, spriteSheetHeight);

    centerX = (screenWidth / 2) - (spriteSheetWidth / 2);
    centerY = (screenHeight / 2) - (spriteSheetHeight / 2);

    position = Vector2(centerX, centerY);

    return super.onLoad();
  } 
}

Explicacion del codigo anterior

Como puedes apreciar en el código anterior, definimos el tamaño para cada sprite de nuestro sprite sheet; para la imagen que seleccionamos, sería de 680 píxeles x 472 píxeles:

Para ello, usamos un par de propiedades:

late double spriteSheetWidth = 680.0, spriteSheetHeight = 472.0;

Es importante notar que, debes de especificar el tamaño acorde al sprite que estés utilizando.

Cargamos el sprite sheet:

var spriteImages = await Flame.images.load('dino.png');

Y definimos el spriteSheet para que pueda ser manipulado mediante una propiedad; es en este paso que, utilizamos las dimensiones individuales de cada estado:

final spriteSheet = SpriteSheet(image: spriteImages, srcSize: Vector2(spriteSheetWidth, spriteSheetHeight));

Finalmente, ya es posible consultar de manera individual cada posición de uno de los pasos del sprite sheet:

sprite = spriteSheet.getSprite(1, 1);

Existen sprite sheet con tamanos distintos para cada frame o sprite, por lo tanto, debes de estar seguro que el sprite sheet que estas usando tienen los mismos tamanos para cada uno de los sprite, en este ejemplo es de 680 px 472 px.

Cargar un Sprite

Tambien es posible cargar sprite usando la función de load; se usa la función loadSprite de la clase Flame para cargar el sprite: sprite = await Flame.images.load('imagen.png');

Una vez cargado el sprite, podrías utilizar el componente SpriteComponent para renderizarlo en el juego como si fuera un componente; esto puede parecer poco pero, al usar los componentes de Flame, podemos usar carácteristicas como el sistema de colisiones, entrada de datos y en general, cualquier funcionalidad disponible en una clase PositionComponent.

Aquí te dejo un ejemplo de cómo cargar un sprite en Flame:

import 'package:flame/components/sprite_component.dart';
import 'package:flame/flame.dart';
class MyGame extends BaseGame {
 SpriteComponent _spriteComponent;
 MyGame() {
   _loadSprite();
 }
 void _loadSprite() async {
   var image = await Flame.images.load('ruta/a/la/imagen.png');
   _spriteComponent = SpriteComponent.fromImage(0, 0, image);
   add(_spriteComponent);
 }
}

Este código creará un nuevo juego (MyGame) y cargará un sprite utilizando la función load de Flame, y lo renderizará en el componente SpriteComponent.

Recuarda que este material es parte de mi curso y libro en Flame para la creación de juegos en 2D.

Transcripción del vídeo

La siguiente operación que vamos a hacer sería lógicamente cargar nuestro sprites para empezar a trabajar con ella así que para esto vamos a generar aquí un nuevo componente Yo te recomiendo que copies y pegues este porque va a ser prácticamente lo mismo entonces lo copiamos lo pegamos voy a renombrarlo Aquí voy a colocarlo estos nombres medio locos que le colocó ya que ahora es un spray shift por aquí va a colocar Player Sprite sheet componente punto dart, Este es el nombre grande lo cuenta que le voy a dar por aquí lo coloco coloco las referencias correctas y perfecto y aquí lo tenemos OK Bueno y aquí lo va a simplificar un poco por ejemplo quitarle lo del teclado no me interesa Así que le quitamos el mixing bajamos hacia el apartado del Kevin y lo borramos todo tampoco me interesa mucho así que lo podemos quitar al menos de momento y ya podemos limpiar Aquí también un poquito las importaciones Ok vamos a empezar aquí a realizar cambios lógicamente esto no lo vamos a cargar así porque vamos a cargar ahora un paso por vez de nuestros Ya lo vamos a ver pero lo podemos ir comentando aquí para que no haga ruido por lo demás aquí la posición todo esto el vector en algún punto cargar el spray componer o un componente de ese Recuerda que la que vamos a trabajar es la imagen que tenemos aquí con componente me refiero a estas imágenes vamos a cargar una promesa y luego la podemos ir variando y más adelante en el futuro la animamos pero poco a poco de momento es cargarlo Honestamente vamos a necesitar hacer lo siguiente Ok por aquí arriba Vamos a cargar nuestra imagen para eso tenemos que hacer ya que ahora es un Sprite la cosa cambia un poquito vamos a crear una variable llamada Sprite image este puede tener Cualquier nombre pero bueno en la que yo lo voy a dar y por aquí hacemos un flame punto y punto e indicamos aquí el nombre que es Dino punto png vamos a cargar esto el paquete lo tenemos aquí en flame; Así que para esto vamos a crear:

var spriteImages = await Flame.images.load('dino.png');

Colocarle var Sprite sheet va a ser igual a Sprite y por aquí le tenemos que referenciar varias cosas entre ellas le tenemos que referenciar la imagen tal cual puedes ver imagen va a ser el sprite Sheet que definimos por aquí Esto va aquí abajo disculpa es para image por aquí no da un error si lo analizamos aquí le estamos colocando Future ya que nuevamente cualquier operación viene siendo Future aquí nos faltó colocarle White y aquí lo toma sin ningún problema Ok por aquí viene siendo:

final spriteSheet = SpriteSheet(image: spriteImages, srcSize: Vector2(spriteSheetWidth, spriteSheetHeight));

El tamaño el tamaño de que te pudieras preguntar en este caso lógicamente no es del documento es decir del documento como un todo Es decir de todo esto sino de cada uno de los sprite sheets que este creo que lo mostramos antes pero yo también por aquí tengo la tablita y ahora las dimensiones que aparecen por aquí Cuando leas a propiedades Recuerda que cada una de estas sheet tienen exactamente el mismo tamaño y esto es debido al bueno las animaciones al espacio que ocupan que sería más o menos por aquí tal cual podías ver de manera individual en fin las dimensiones son de 680 por 472 aquí tú tienes que colocar obviamente las que estés empleando para tu spritest en el caso que estés ampliando uno distinto aquí al del curso así que por aquí para mantener esto más sencillo vamos a crear aquí un par de propiedades más voy a colocarle final voy a llamarla como Sprite que va a ser igual a en este caso a la de 680 que es el de ancho coloco 680 coma vamos con el otro Sprite shift de th que va a ser igual a Y si buscamos va a ser igual aquí al de 472 472 colocamos el punto y coma y ahora por aquí lo referenciamos para esto aquí empleamos nuevamente un vector de dos dimensiones que es lo que nos indica por aquí el requiere un vector de dos dimensiones y le indicamos aquí nuestro Sprite with ya está un trabalenguas y Sprite colocamos el punto y coma y ya con esto ya cargamos nuestro Sprite Aquí voy a colocarle Bueno está bien ahí lo tenemos ahora bien qué hacemos:

final spriteSheet = SpriteSheet(image: spriteImages, srcSize: Vector2(spriteSheetWidth, spriteSheetHeight));

Con esto ya finalmente podemos cargar nuestro Sprite ya que al final Recuerda que Dependiendo el componente que quieras emplear siempre tienes algunas propiedades que tienes que definir algunas son obligatorias otras son más opcionales pero en este caso sería obligatoria la Sprite en la que tenemos aquí que todavía no la hemos definido Por cierto va a colocarla todo esto final Ahora sí Entonces por aquí voy a indicar que mi Sprite va a ser igual a algo que se emplean de que el spray sheet punto indicamos aquí la función llamada get Sprite aquí la tenemos la cual nos indica columna ya que si te fijas por aquí lo que nosotros tenemos es una matriz 001 02 y así ya vamos a hablar de esto pero es lo que nos está pidiendo vamos a colocar lo sencillito colocamos 00 colocamos punto y coma guardamos y vamos a recargar para ver qué es lo que tenemos Ah claro siempre se me olvida por aquí empleamos el nuevo componente aquí lo tenemos que sería Player Spice guardamos Ya lo tenemos bueno aparece aquí distorsionado Pero al menos lo tenemos ya vamos a ver eso pero aquí puedes colocar otra por ejemplo uno iba a colocar un poquito más para que cambie por ejemplo tres:

sprite = spriteSheet.getSprite(1, 1);

Recargo todo y mira que ahora aquí aparece en otro estado OK Vamos a chequear aquí a ver si es que aparece distorsionado o soy yo pero me aparece aquí que aparece un poquito como aplastado ya parece aplastado Ah claro tiene toda la lógica del mundo ya que ahora las dimensiones cambian por eso es que parece que aplastado voy a quitar esto porque ya no hace falta y colocamos aquí las nuevas al igual que por aquí Ahora sí guardamos recargamos Ya lo tenemos Ahora sí se ve correctamente nuestro Sprite Ok cómo funciona este chiste Qué valores le tenemos que colocar por aquí para eso aquí te prepare una imagen la tengo por aquí en descargas la tengo creo que es esta aquí está la imagen Estas son las posiciones que nosotros pudiéramos emplear para este Sprite que nosotros definimos Fíjate que por aquí pudieran haber dos perfectamente ahí tú lo pudieras emplear para cargar otros recursos para otra animación también puedes indicarles un poco más pequeño etcétera puede hacer muchas cosas ahí pero para las pruebas que estamos haciendo esto quedó aquí muerto que sería obviamente ineficiente ya que nuevamente por aquí entrarían dos imágenes en fin volviendo al tema puedes colocar estos valores que depende Bueno lo puedes manejar como si fuera nuevamente una matriz 001 0210 es decir porque estamos en el eje de las Y 00 aquí ya crecemos en uno en el eje de las Y y aquí en dos y hasta aquí llegamos Porque además posiciones Luego si vamos hasta acá en esta fila y aquí estamos en la posición bueno en el eje de las x y sería lo mismo uno dos y tres hasta llegar acá ya lo tenemos Si quieres ver esto un poco más en la práctica puedes colocar aquí el borde que sería 3 0 guardamos ahí lo tenemos voy a colocar esta imagen creo que para acá para que se vea mejor ahí lo tienes y bueno no es esta recargo ya que tengo en el y debería de verse a ver que le colocó aquí 30 aunque esta imagen es diferente Ahora que estoy viendo me quedó diferente ahora que es otro estado que yo coloque aquí Bueno si estoy más me quedo ligeramente diferente ya que estará de otro recurso pensé que me había quedado igual pero ahora que estoy viendo aquí agarre fuera de caminar pero en esencia es lo mismo de igual manera inclusive pudiéramos probar esta en este caso yo hice el sprise para la parte de caminar ya lo puedes hacer también y probarlo aquí en el curso bueno en el proyecto en este caso la de 30 nos da cero no nos da nada porque si nos fijamos tenemos aquí un 0 1 y 2 llegamos hasta dos entonces aquí también se aplica lo mismo dejamos aquí un espacio muerto Pero en fin volviendo a esto aquí sería cero sería uno sería dos entonces Y esta sería en y sería 2 1 así que este es el más que podemos colocar colocamos dos 1 guardamos Ya lo tenemos Qué pasa si colocamos 22 Bueno lo mismo que sucedía ahorita ya no existe se salió del Rango así que por aquí no vamos a ver nada recargamos todo Ya lo tenemos pero bueno como te digo este viene siendo el funcionamiento aunque sea una imagen distinta pues entender porque es el mismo principio de una matriz Sencillamente es una matriz una raíz en cualquier lenguaje de programación es lo que tenemos aquí y de esta manera es que podemos acceder a las imágenes de manera independiente aquí la clave lo más importante aparte de que cambia un poquito la forma de cargar el recurso que ahora viene siendo de esta manera y no así es aquí definir el Sprite con las dimensiones correspondientes para nuestra imagen la imagen de manera individual que era la que te mostraba por aquí no es la imagen como tal del Sprite si una imagen de cada uno de los estados que tenemos por ejemplo este podemos obtener el tamaño para el resto que sería de 680 por 4,72 que es el que tenemos aquí ya que si colocadas otras dimensiones Ya por aquí la cosa se va a distorsionar por ejemplo aquí colocó 512 voy a guardar recargamos mira que ahora solamente va a aparecer un fragmento y lo mismo si hay que colocamos por ejemplo uno y uno no se va a visualizar de la manera correcta ahí como puedes ver inclusive en algunos puntos vas a ver que se cortan o aparece otra imagen pero bueno ya creo que con esto quedó claro su funcionamiento por lo demás de mucho más que decir claro están lo siguiente que tenemos que hacer sería animar todo esto pero eso lo veremos luego

- Andrés Cruz

In english

Andrés Cruz

Desarrollo con Laravel, Django, Flask, CodeIgniter, HTML5, CSS3, MySQL, JavaScript, Vue, Android, iOS, Flutter

Andrés Cruz En Udemy

Acepto recibir anuncios de interes sobre este Blog.

!Cursos desde!

10$

En Udemy

Quedan 2d 00:49!

Udemy

!Cursos desde!

4$

En Academia

Ver los cursos

!Libros desde!

1$

Ver los libros
¡Hazte afiliado en Gumroad!