En este capítulo aprenderemos como optimizar nuestro juego empaquetando texturas y a añadir sonidos.
Aquí viene un poco de teoría, ¿por qué necesitamos empaquetar las texturas? pues bien para minimizar el número de accesos a disco gracias a que cocos provee un clase llamada SpriteBatchNode que funciona de la siguiente manera: inicialmente se carga un descriptor en el SpriteFrameCahe, este descriptor es un .plist (una especie de xml pero para Mac) pues bien una vez cargado el descriptor en la cache, podremos crear sprites a partir de los nombres cargados en dicho descriptor obviamente solo con dicho descriptor no podemos renderizar imágenes.
Después de cargar este descriptor, creamos el SpriteBatchNode a partir de la spritesheet con las texturas que especifica el descriptor y a partir de aquí cualquier sprite que creemos con el SpriteFrameCache lo añadimos al SpriteBatchNode con addChild y automáticamente le asignará la textura correspondiente. Además de minimizar el número de accesos a disco, añade otra ventaja muy importante. Cuando creamos sprites con su método create, se genera un batch para rendirizar dicho sprite, de esta nueva forma solo se genera un batch para el SpriteBatchNode que permite renderizar todos sus nodos hijos, esto es una ventaja muy importante pues crear dicho batch es muy costoso.
¿Cómo puedo empaquetar mis texturas? hay varias herramientas en el mercado como TexturePacker pero son de pago, nosotros usaremos libgdx-texturepacker-gui que es una herramienta de empaquetado de otro framework llamado libgdx. Sin embargo el formato del descriptor no es compatible con cocos por eso he realizado un pequeño script en java para transformar el formato del empaquetador a .plist Aquí está el código del parser y de este otro link puedes bajar las texturas empaquetadas y el script para generar el .plist
El resultado final de empaquetar nuestras texturas será algo así:
Una única imagen con todos los sprites. Veamos un poco de código para ver cómo usar el SpriteBatchNode. Con ver el GameLayer y la clase Player será suficiente.
Tan solo hace falta fijarse en el init(), la lógica se mantiene igual que en los capítulos anteriores. Como vemos tal y como dijimos previamente, primero cargaremos el descriptor en la caché con SpriteFrameCache::getInstance()->addSpriteFramesWithFile("Hunter.plist") y luego creamos el batchNode SpriteBatchNode* gameBatchNode = SpriteBatchNode::create("Hunter.png") y para finalizar añadimos a la escena dicho nodo.
El resto de clases las añadimos como hijas de este nodo, este nodo solo puede tener como nodos hijo, nodos Sprite o clases derivadas de cocos2d::Sprite. Es por esto que nuestro TouchController pasará a heredar de Sprite en lugar de Node ya que para funcionar debe ser hijo de Player y ahora player será hijo del SpriteBachNode.
Veamos cómo crear Sprites a partir de la caché.
Aquí tenemos nuestra ya conocida clase Player, veamos en que ha cambiado. Si echamos un ojo al método createIdleAnimation() podremos apreciar que los frames de las animaciones ya no se crean igual, ahora usamos SpriteFrameCache::getInstance()->getSpriteFrameByName(name). La clase Sprite también permite crear sprites a partir de un spriteFrameName sin necesidad de usar la caché de la siguiente forma Sprite::createWithSpriteFrameName(name) es importante añadir cualquier nodo creado de esta forma a un nodo padre SpriteBatchNode que contenga la información necesaria para completar al nodo hijo creado a partir del descriptor. De no ser así obtendremos un error al ejecutar el juego.
Descarga las carpetas Classes y Resources del proyecto hasta el momento aquí
Ahora vamos a añadir un par de sonidos muy simples, algunos de ellos creados con as3sfxr y otros obtenidos de opengameart.org u otras fuentes.
Para comenzar crea una carpeta llamada music dentro de tu carpeta resources y añade estos sonidos
Lo normal en cocos seria usar SimpleAudioEngine, sin embargo en win32 no funciona el ajuste de volumen, en Windows phone, Android e IOS funciona correctamente, además el formato aceptado por todas las plataformas al usar SimpleAudioEngine es wav sin embargo vamos a crear una clase que nos permita ejecutar los sonidos también en win32 y poder probar fácilmente nuestro juego, la única pega es que en win32 el formato de audio será .mp3 y para el resto .wav.
Esta clase está diseñada para que trabaje de forma similar a SimpleAudioEngine, es un singleton y permite precarga de los sonidos. Esa precarga solo será efectiva en plataformas como Android e IOS en win32 no.
Para reproducir sonidos basta con llamar al singleton y luego a su metodo playEffect o Background segun convenga, además para facilitar nuestro código, nuestro audio engine asumirá que tenemos una copia de los ficheros de audio .wav también en .mp3 con lo que en el código llamaremos al fichero con extensión .wav y esta clase automáticamente usará uno u otro fichero si estamos en win32 o no. Esto implica tener audio duplicado con lo que a la hora de publicar en Android por ejemplo hay que recordar borrar los ficheros .mp3 que nunca serán usados.
Veamos como precargar el audio en nuestro appDelegate
Como vemos ahora junto con la configuración de pantalla, tenemos la precarga del audio. Fíjate también que llamamos al pauseAll() y resumeAll() para pausar y recomenzar la reproducción de audio cuando se sale y entra en el juego o se pausa el juego por ejemplo.
Como vemos ahora junto con la configuración de pantalla, tenemos la precarga del audio. Fíjate también que llamamos al pauseAll() y resumeAll() para parar y recomenzar la reproducción de audio cuando se sale y entra en el juego.
Descarga la carpeta Classes con el sonido integrado y la carpeta Resources del proyecto hasta el momento aquí