Tutorial videojuegos: Pool de sprites

juego android fruit pool objetos

Cuando se crea un videojuego uno de los problemas con los que se encuentra un desarrollador de videojuegos es la caida de rendimiento cuando hay muchos sprites. Lo que hacemos todos al crear un videojuego con enemigos es ir creando sprites de enemigos a medida que los vamos necesitando. Cuando un enemigo muere, matamos el sprite para que se vaya al Garbage Collector. Esa forma de trabajar parece la más sencilla y la lógica, pero el rendimiento del juego cae exponencialmente.

¿Por qué noto lags durante el juego?

Si creas sprites en tiempo de juego... estás haciendo algo incorrecto. Los sprites se crean antes de comenzar el juego, si no, lo que nos ocurrirá es que aparecerán saltos ya que crear sprites es algo costoso y probablemente a nuestro motor de videojuegos no le de tiempo a renderizar la pantalla, mover sprites y además leer las texturas para crear los sprites nuevos.

Para solucionar esto viene en nuestra ayuda los pools de objetos. Primero describiremos un poco que es un pool y como se puede crear, luego pondremos un ejemplo de pool en Andengine.

¿Qué son los pools de objetos (en nuestro caso sprites)?

Realmente es un patrón de diseño. Básicamente son una estructura de datos donde metemos un conjunto de sprites antes de comenzar el juego y los vamos sacando según los necesitamos. Además cuando un sprite ya no es necesario lo devolvemos al pool para que pueda reutilizarse más adelante. De esta forma, si nuestro pool es lo suficientemente grande no necesitaremos crear sprites durante el tiempo del juego. El pool lo programaremos de forma que vaya inicializando los sprites con las variables iniciales según vamos reciclandolos. Es decir, nosotros solo nos procuparemos de pedirle un sprite al pool, éste se encargará de darnoslo reciclado (o nuevo) y con los datos inicializados. Si no hubieramos calculado bien el tamaño del pool inicial entonces el pool creará nuevos sprites que añadirá al pool, esto último no es deseable, pero si fuera necesario el pool deberá ser capaz de gestionarlo.

Caso real de un pool de sprites en Andengine usando el patrón de diseño pool

Andengine tiene una clase que nos ayudará en la creación y gestión de pools de sprites. En la clase de ejemplo que he puesto, si os fijáis heredamos de GenericPool para tener todas las características del pool disponible. Los objetos que guardamos en el pool son de tipo GraphicObject (una clase creada por mi) que tienen un metido initialize creado por mi que pondrá las coordenadas, tamaño,  y en general cualquier dato para inicializar el sprite.


import java.util.ArrayList;

import org.andengine.util.adt.pool.GenericPool;

public class ResourcePoolObject extends GenericPool<GraphicObject>{

	private ActivityGamePlaying activity;	
	private int typeObject; // El tipo de Objeto
	private int contadorObjetos=0;
	public ResourcePoolObject(ActivityGamePlaying activity,int typeObject){
		super();

		this.activity=activity;
		this.typeObject=typeObject;
		int pInitialSize = ConstantsGeneric.NUM_POOL_ITEMS;
		
		if(pInitialSize > 0) {
			this.batchAllocatePoolItems(pInitialSize);
		}
	}
	
	/**
	  * Called just before a Sprite is returned to the caller, this is where you write your initialize code
	  * i.e. set location, rotation, etc.
	 */
	 @Override
	 protected void onHandleObtainItem(final GraphicObject objeto) {		
                // Inicializamos nuestro objeto llamando al metodo initialize que habremos creado en nuestro objeto
		objeto.initialize();		
	}

	  /**
	 * Called when a Sprite is required but there isn't one in the pool
	 */
	 @Override
	 protected GraphicObject onAllocatePoolItem() {	
		// Creamos nuestro objeto (en mi caso es GraphicObject)
		final GraphicObject objeto = new GraphicObject(activity,typeObject);	
		objeto.doAttachs();
		activity.getScene().sortChildren();   
		return objeto;
	 }
		
	 /**
	  * Called when a Sprite is sent to the pool
	 */
	 @Override
	 protected void onHandleRecycleItem(final GraphicObject objeto) {		 
		 objeto.setIgnoreUpdate(true);
		 objeto.setVisible(false);		
	 }	 	 
}

Luego, durante el juego, si necesitamos un sprite del pool simplemente tendremos que pedirle al pool un objeto de esta forma:

// Suponemos que pool es un objeto tipo ResourcePoolObject (nuestro pool de sprites)
// En mi caso es un pool de sprites frutas, le pido una fruta de tipo "tipo_fruta"
GraphicObject fruta = pool.obtainPoolItem();

// Cuando queramos matar a un sprite hay que pasarle el sprite a reciclar al objeto ResourcePoolObject:
pool.recyclePoolItem(fruta);

Para añadir más versatilidad podríais tener un sprite con varias texturas cargadas y a la hora de inicializar ponerle la textura que quisíerais, de esta forma en un mismo pool podríamos tener enemigos de distinto tipo. O tambien podría hacerse un pool por cada tipo de enemigo.

Las posibilidades son muchas. Disfrutádlo

Juego que usa pool de sprites

Os presento el juego Fruit Tiles. Es un juego que hice hace algunos meses y que usa intensivamente pool de sprites para funcionar. Como veréis salen un montón de frutas en la pantalla que van desapareciendo. Pues ahí se usa el pool de sprites. Si lo descargáis y lo valoráis con 5 estrellas os lo agradeceré mucho! Así podré seguir haciendo tutoriales como este.

Votos totales: 29