15 de mayo de 2013

Una de las herramientas más útiles que tengo a la hora de comunicar eventos que ocurren en una clase a otros sitios es el patrón de Observador, que en ActionScript 3 se manifiesta con el sólido sistema de eventos que forma parte de la librería básica de este lenguaje.
Lamentablemente, el sistema de eventos es prácticamente único dentro de AS3. Portear código basado en eventos a otras plataformas que no tengan esto incorporado no es sencillo. Así que una manera que he encontrado de trabajar más naturalmente ha sido los AS3 Signals, de Robert Penner, o su homólogo en Javascript, por Miller Medeiros. Basado en el sistema de Signals and Slots de la librería QT, se definen los signals como miembros públicos que se colocan dentro de un objeto. Otros objetos se suscriben a estas señales con una función de llamada de vuelta, que es llamada cuando la señal se emite. A estas funciones se les conoce como slots.
Esto es muy parecido a como funciona el sistema de eventos, pero a diferencia del de ActionScript 3, no requiere un sistema central que maneje los eventos. Cada señal mantiene registro de las funciones a las que debe llamar.
En C++ he conseguido que la implementación de signals con Boost, llamada signals2, es igualmente de útil y flexible que sus símiles en AS3 y JavaScript. Además de esto, la librería es thread-safe y se comporta bien con muchas plataformas, por lo que el código resultante es bastante porteable.
Boost es un compendio de librerías extremadamente grande, y que apenas estoy comenzando a explorar. El objetivo de este artículo es simplemente explorar lo que se puede hacer con signals2 para facilitar la comunicación entre clases. No está de más decir que necesitas comprender medianamente bien C++ para entender el tutorial.
Presentemos el caso
Supongamos que tenemos un juego en el que un personaje se mueve constantemente de un punto a otro en línea recta. El juego debe reaccionar cuando el personaje llega a su destino. Dicho esto, implementaremos este comportamiento en una clase separada de la clase encargada de dibujar la pantalla.

*Nuestro juego hipotético interpola linealmente la posición de un personaje de un punto a otro*
En código podemos bocetear esto:
//Character.h
class Character {
public:
Character();
void update();
float position;
}
//Character.cpp
Character::Character()
: position(0.0f) { }
void Character::update() {
if (position < 1.0f) {
position += 0.1f;
} else {
position = 0.0f;
}
}
La clase principal del juego contiene el loop principal del juego, el cual ejecuta la función Character::update() para mover al personaje. El objetivo es que debemos ejecutar una función una vez que el personaje llegue al valor 1.0f de la posición. Notemos que la función no va a pertenecer a la clase Game (todavía). Un boceto de código sería algo como:
//Game.h
void playerLlegoAlFinal();
class Game {
public:
Game();
void update();
Character *player;
}
//Game.cpp
Game::Game() {
player = new Character();
}
void Game::update() {
player->update();
}
void playerLlegoAlFinal() {
//Función para llamar cuando el jugador llegue a la posición final
}
Vemos ahora que el problema es cuándo saber que el personaje llegó a la posición final. Podríamos comprobarlo con un if dentro del update, pero vemos que la posición es un float, ¿qué valor deberíamos comprobar para llamar a la función?, en la clase cuando Character llega al valor 1.0f reiniciar el valor. Podríamos también crear en Character un miembro que sea un apuntador a una función, de tal manera que Character la pueda llamar una vez que la posición sea 1.0f. Pero, ¿qué tal si pudiésemos llamar a una o más funciones cuando ocurra esto?
Entran las signals
Las signals son unas estructuras en Boost que agrego a una clase “A”, generalmente de manera pública. Las clases que necesiten que se llame a una función desde esa clase “A” agregan esa función al signal. Estas funciones pasan a llamarse slots. Cuando la clase “A” despacha la señal, esta estructura en consecuencia llama a todos los slots que se han agregado hasta el momento.
Notemos que esto se parece al sistema de eventos de ActionScript 3, pero la diferencia radica en que las funciones que agrego a las señales están fuertemente tipadas. Las funciones que se agregan deben tener la firma que se declara junto a la señal.
Boost trae dos implementaciones de signals and slots, signals y signals2. La primera necesita hacer un trabajo de compilación previo a su utilización, mientras que la segunda emplea únicamente los headers que se incluyen en nuestro proyecto, además de que la segunda implementación es thread-safe.
Agreguemos una señal a nuestra clase Character:
//Character.h
#include
class Character {
public:
Character();
void update();
float position;
boost::signals2::signalcharacterLlegoAlFinal;
}
//Character.cpp
Character::Character()
: position(0.0f) { }
void Character::update() {
if (position < 1.0f) {
position += 0.1f;
} else {
characterLlegoAlFinal();
position = 0.0f;
}
}
La señal se declara en el header de la clase, detallando también el tipo de funciones que acepta la señal. Para despachar la señal llamamos a la señal como una función cualquiera. En este ejemplo inicial llamaremos a funciones que no aceptan ningún parámetro. Veamos ahora cómo agregar una función desde Game.cpp.
//Game.cpp
Game::Game() {
player = new Character();
player->characterLlegoAlFinal.add(playerLlegoAlFinal);
}
Ahora cada vez que el personaje llegue a la posición 1.0f, se despachará la señal y en consecuencia se llamará a la función playerLLegoAlFinal(). Podría agregar otra función más tarde, y esta función se llamaría después de la primera.
Este es un caso suficientemente simple, casos en los que tengamos que llamar a funciones que no pertenecen a ninguna clase. Pero en la realidad solemos tener que pasar funciones miembro de una clase. Acá nos enfrentamos a otro problema. En lenguajes de alto nivel, las funciones son ciudadanos de primera clase, por lo que yo podría pasar directamente el nombre de la función, haciendo algo como “player.characterLLegoAlFinal.add(this.playerLLegoAlFinal)”. Pero esto es C++, donde las funciones en realidad son direcciones en memoria que debo asociar a la dirección de un objeto.
Boost nos ofrece “bind”, una herramienta muy poderosa que resuelve fácilmente este problema. A bind le pasamos la dirección del objeto, la dirección de la función, y nos devuelve un apuntador que asocia ambos datos. Cuando la señal se despacha, la función sabrá a que objeto se está refiriendo.
Veamos de nuevo el ejemplo anterior, pero la función playerLlegoAlFinal() será ahora un miembro de Game:
//Game.h
class Game {
public:
Game();
void update();
void playerLlegoAlFinal();
Character *player;
}
//Game.cpp
Game::Game() {
player = new Character();
player->characterLlegoAlFinal.add(boost::bind(&Game::playerLlegoAlFinal, this));
}
void Game::update() {
player->update();
}
void Game::playerLlegoAlFinal() {
//Función para llamar cuando el jugador llegue a la posición final
}
Ahora cuando la señal despache characterLlegoAlFinal, se llamará a la función playerLlegoAlFinal correspondiente a Game.
Agregando parámetros
¿Qué pasa si tenemos que pasar más datos con esa señal? Imaginemos ahora que el personaje cuenta las veces que ha llegado al final, y que eso es un parámetro que le pasaremos a los slots que llamemos. Veamos la nueva clase Character:
//Character.h
#include
class Character {
public:
Character();
void update();
float position;
int vecesLlegadas;
boost::signals2::signalcharacterLlegoAlFinal;
}
//Character.cpp
Character::Character()
: position(0.0f)
, vecesLlegadas(0) { }
void Character::update() {
if (position < 1.0f) {
position += 0.1f;
} else {
vecesLlegadas += 1;
characterLlegoAlFinal(vecesLlegadas);
position = 0.0f;
}
}
Ahora que hemos cambiado la firma de los slots de la señal, tenemos que cambiar la función en Game a void playerLLegoAlFinal(int veces). Pero ¿cómo hacemos con la función que le vamos a pasar a bind? A bind hay que especificarle que la función que le estamos pasando tiene un parámetro que no se conocerá hasta que la señal sea despachada. Veremos que bind tiene una característica que la hace extremadamente útil en otros casos distintos a emplear la función como slot. Veamos qué debemos hacer en nuestra clase Game:
//Game.h
class Game {
public:
Game();
void update();
void playerLlegoAlFinal(int veces);
Character *player;
}
//Game.cpp
Game::Game() {
player = new Character();
player->characterLlegoAlFinal.add(boost::bind(&Game::playerLlegoAlFinal, this, _1));
}
void Game::update() {
player->update();
}
void Game::playerLlegoAlFinal(int veces) {
//Función para llamar cuando el jugador llegue a la posición final
}
Los nombres _1, _2, _3, _4… hasta el _9 son marcadores de posición (placeholders), indican que la nueva función que estamos creando en este caso se le pasará el parámetro del número correspondiente en la posición del argumento de bind. Es decir, en este ejemplo estamos creando una función con bind que tiene asociados la dirección del objeto, la dirección de la función, y que acepta un solo argumento. Este argumento es de tipo el primer argumento de la función que estamos pasando, es decir, int veces.
Con esto vemos que con signals2 y bind podemos crear señales que comunican datos entre clases elegantemente. Pero antes de cerrar este tutorial exploremos un poco más bind, y veamos lo que es capaz de hacer.
Explorando bind
Vemos que la función bind es capaz de pasar parámetros placeholder, pero bind no nos limita a esto. Supongamos que tenemos una función de dos parámetros que ahora queremos agregar como slot a la señal de Character.
void Game::playerLLegoAlFinalYEstaFeliz(int veces, bool estaFeliz);
La señal sigue siendo la misma, characterLlegoAlFinal, que llama a slots que aceptan un solo parámetro. Haremos entonces que se llame a esta señal, asumiendo que el player llega siempre feliz. Agreguemos la función con bind:
//Game.cpp
Game::Game() {
player = new Character();
player->characterLlegoAlFinal.add(boost::bind(&Game::playerLlegoAlFinal, this, _1));
player->characterLlegoAlFinal.add(boost::bind(&Game::playerLlegoAlFinalYEstaFeliz, this, _1, true));
}
Para fines de la señal, este segundo slot acepta un solo parámetro. La función playerLLegoAlFinalYEstaFeliz será llamada con el número de veces indicada por la señal despachada, y el segundo parámetro siempre con true. Con bind podríamos incluso cambiar de posición los parámetros, y muchas otras cosas más. Boost ofrece un montón de funcionalidades que serían propias de lenguajes de alto nivel, con la eficiencia de C++. Quedan muchísimas más por conocer.
8 de mayo de 2013
cocos2d-tutorial-4Comenzando un proyecto

*El resultado final de esta sección*
En esta parte del tutorial comenzaremos un proyecto de cocos2d-html5 a partir de la plantilla que la librería provee. Vamos a agregar una imagen, un texto y un botón a la escena y vamos a hacer que la imagen y el texto ejecuten unas acciones a partir de presionar el botón.
Copiemos el template
Para iniciar un proyecto con cocos2d-html5 basta con copiar el directorio template que se encuentra en el directorio que descomprimimos en la parte 2 del tutorial.

¿Qué haremos ahora?
A partir de ahora iremos haciendo cambios sobre este proyecto para agregar algunos de los nodos que conocimos en la parte anterior. De ahora en adelante conoceremos de primera mano los pasos que hay que dar para crear los diferentes tipos de nodos.
El directorio template contiene prácticamente todos los elementos que vimos en la parte anterior, en el directorio HelloWorldHTML5, sin el Sprite de ejemplo. De esta manera, sólo hace falta quitar unos pocos elementos para comenzar a poner los de nuestro juego.
Si accedemos a nuestro juego, con la dirección http://localhost/a/leGame veremos nuestros inicios en ejecución:

Elaborando la primera escena
Cambiando un sprite
Vamos nuevamente a myApp.js. Observemos el código que se encarga de crear la imagen y colocarla sobre la capa.
//myApp.js
// add "Helloworld" splash screen"
this.sprite = cc.Sprite.create("res/HelloWorld.png");
this.sprite.setAnchorPoint(cc.p(0.5, 0.5));
this.sprite.setPosition(cc.p(size.width / 2, size.height / 2));
Casi todos los nodos en cocos2d se crean con su propia función create(). En el caso de cc.Sprite, puedes no pasar ningún parámetro para crear un sprite vacío, o un parámetro con el nombre de un archivo, o el nombre de un archivo junto con un rectángulo que determina las coordenadas dentro de ese archivo. Después de hacer este tutorial, cuando te encuentres dudando de cuáles son los parámetros que debes pasar a un nodo, revisa la función create respectiva para consultar.
Son muy específicos los casos donde debes hacer new de un objeto. La razón de esto es porque las funciones create se encargan también de manejar los objetos en memoria, por lo que cocos2d se encarga de liberar el objeto cuando tengas que destruir la escena, evitando memory leaks, algo doblemente importante cuando trabajas con los bindings en Javascript. En cocos2d este concepto se conoce como objetos autorelease.
La segunda función establece el ancla del objeto, o su origen en su sistema de coordenadas locales. En este caso el ancla está puesto en la mitad del sprite. Recuerda que estamos empleando el sistema de coordenadas de OpenGL, aquí una guía visual de lo que representa el anchor point en varios puntos:

Observa que este sprite, que tiene de tamaño 42x40, tiene su centro en un punto que no corresponde a un único pixel.
La función cc.p es la misma función que cc.PointMake, y cc.Point, que retornan un objeto tipo punto. Estos nombres tienen más sentido cuando nos vamos a sus homólogos en C++.
La tercera función establece la posición del sprite. Recordemos que estamos en el sistema de coordenadas de OpenGL. El origen está en la esquina inferior izquierda, el eje x incrementa hacia la derecha, y el eje y incrementa hacia arriba. El objeto size que se encuentra en el código se obtiene de la función cc.Director.getInstance().getWinSize(), y es la que nos permite posicionar los objetos con respecto al tamaño de la ventana. En el caso de este código, el sprite se colocará exactamente en la mitad de la ventana.
Vamos entonces a descargar este sprite. Colócalo en el directorio ‘res’ del proyecto. Ahora cambia el nombre del archivo del sprite en el código
//myApp.js
// add "Helloworld" splash screen"
this.sprite = cc.Sprite.create(s_foobar);
this.sprite.setAnchorPoint(cc.p(0.5, 0.5));
this.sprite.setPosition(cc.p(size.width / 2, size.height / 2));
Hace falta tener que agregar también la definición de la variable s_foobar, y en consecuencia, la dirección de este archivo en resource.js. Así que vamos a este archivo, agreguemos una variable que contenga la dirección y agreguemos una entrada al arreglo g_ressources.
//resource.js
var s_foobar = "res/avatar_0.png";
var g_ressources = [
//...
{type:”image”, src:s_foobar},
//..
];
Por convención, iniciamos todos los nombres de los sprites con el prefijo s_, de tal manera que los podamos referenciar en los demás archivos sin tener que repetir el string con la dirección. Así, si tenemos que cambiar el nombre de un sprite, no tenemos que ir pasando sobre cada archivo cambiando el nombre, sino solamente cambiar la dirección en resource.js.
Es importante que incluyas todos los recursos que vayas a emplear para la carga, incluyendo las imágenes que son cargadas por otros archivos de texto, como los archivos .plist, o los archivos .fnt.
Ejecutemos nuevamente el juego y ahora obtendremos el siguiente resultado:

El Director y otros singletons
Habremos visto en el pedazo de código anterior de myApp.js esta llamada para obtener las dimensiones de la ventana:
var size = cc.Director.getInstance().getWinSize();
El objeto cc.Director es un objeto que cocos2d crea al inicializar toda la aplicación, y al igual que otros objetos, durante la ejecución de tu aplicación existe sólo una instancia a la que vas a acceder a través de la función getInstance(). Acostúmbrate a conseguir estas llamadas en varias partes del código.
Veremos también que con el director podremos cambiar de escenas y hacer otras cosas de interés. Pero esto lo dejaremos para otra parte del tutorial.
Controles rápidos con botones
Los botones encapsulan un comportamiento muy usual con los juegos: un elemento que presente un sprite cuando no hay un dedo tocándolo, presenta otro cuando lo toca, y una función que se va a llamar cuando lo presionas.
Tenemos varios tipos de botones, y todos parten del objeto MenuItem. Todos estos objetos deben ir en una capa especial de tipo Menu. El MenuItem de este código es un MenuItemImage, el cual se crea pasándole directamente las ubicaciones de las imágenes.
Otros tipos de MenuItem son:
-
MenuItemLabel: se crea pasándole un label (texto). Sobre los Labels hablaremos al final de esta sección.
-
MenuItemSprite: se crea pasándole dos sprites que hayas creado. Es útil para cuando las imágenes las extraes de un spritesheet.
-
MenuItemToggle: acepta dos MenuItems. Sirve para implementar botones “toggle”, que tienen dos estados, “encendido” y “apagado”, dependiendo de cuando los tocas.
Observemos entonces el código que crea el Menu y el botón actualmente:
// este código crea el MenuItemImage
var closeItem = cc.MenuItemImage.create(
"res/CloseNormal.png",
"res/CloseSelected.png",
function () {
history.go(-1);
},this);
closeItem.setAnchorPoint(cc.p(0.5, 0.5));
//este código crea la capa de menu y agrega el MenuItemImage
var menu = cc.Menu.create(closeItem, null);
menu.setPosition(cc.PointZero());
this.addChild(menu, 1);
closeItem.setPosition(cc.p(size.width - 20, 20));
Observemos que MenuItemImage acepta 4 parámetros, la imagen normal del botón, la imagen al ser presionada, la función que se va a llamar (que puede ser perfectamente una función anónima), y el “target” de la función (a qué se refiere el “this” dentro de la función). Esta función puede aceptar también 3 imágenes, la imagen normal, la seleccionada, y la que aparece cuando desactivas el botón con la función setEnabled(false). La función callback en este caso lo que hace es retroceder una página en el navegador. Si accedes al juego escribiendo el URL directamente no va a hacer nada.
Vamos a crear un nuevo botón descargando el estado normal y el estado seleccionado. Copiemos estas dos imágenes en el directorio res/. Vamos a escribir nuevo código debajo del bloque que acabamos de citar.
//myApp.js
closeItem = cc.MenuItemImage.create(
s_buttonNormal,
s_buttonSelected,
function () {
cc.log("Hola");
}, this);
closeItem.setAnchorPoint(cc.p(0.5, 0.5));
closeItem.setPosition(cc.p(size.width/2, 40));
menu.addChild(closeItem);
Veremos que la función que estamos pasando llama a la función cc.log(). Esto nos permite imprimir texto en la consola del navegador, bastante útil para depurar código cuando lo necesites. En el código hay nuevas variables, s_buttonNormal y s_buttonSelected, vamos a definirlas en resource.js:
//resource.js
var s_HelloWorld = "res/HelloWorld.png";
var s_CloseNormal = "res/CloseNormal.png";
var s_CloseSelected = "res/CloseSelected.png";
var s_foobar = “res/avatar_0.png”;
var s_buttonNormal = “res/button-normal.png”;
var s_buttonSelected = “res/button-selected.png”;
var g_ressources = [
{type:”image”, src:s_HelloWorld},
{type:”image”, src:s_CloseNormal},
{type:”image”, src:s_CloseSelected},
{type:”image”, src:s_foobar},
{type:”image”, src:s_buttonNormal},
{type:”image”, src:s_buttonSelected},
];
Volvamos a acceder a nuestro juego y veremos los cambios. En este ejemplo muestro la consola de Chrome con el texto “Hola” impreso.

Los botones tienen mucha flexibilidad para implementar lo que necesites de interfaces. Los utilizarás frecuentemente.
Cambiando textos
En cocos2d se manejan dos tipos de texto, los textos hechos con fuentes TrueType (cc.LabelTTF), y los textos hechos con fuentes bitmap (cc.LabelBMFont).
Las fuentes TrueType tienen la flexibilidad de cambiarles el tamaño, utilizar su versión Bold o itálica, pero dibujar fuentes TrueType es costoso en recursos de procesamiento (son curvas matemáticas que se deben convertir a imagen), amén de las problemas que podrían surgir de los requisitos de la licencia que emplee la fuente en cuestión (recuerda que algunas no permiten distribuirse libremente con el proyecto).
Las fuentes bitmap son imágenes con todas las letras de la fuente y un archivo que indica las coordenadas de cada letra. Se pueden fabricar a mano, o a partir de una fuente TrueType con un software. Como son imágenes como cualquier sprite, dibujarlas se hace muchísimo más rápido que su contraparte en TTF.

*Este es un ejemplo de una imagen que contiene una fuente bitmap*
Para generar las fuentes en bitmap puedes emplear el programa BMFont de AngelCode.com (disponible gratuitamente). El uso de este programa se sale del alcance de esta sección, así que para usar una fuente bitmap de ejemplo descarga la imagen de ejemplo anterior y este archivo. Colócalos en el directorio res/ de tu proyecto, y agrega ambos archivos al registro de recursos en resource.js.
//resource.js
//...
var s_nombresText = "res/nombres.txt";
var s_nombresImage = "res/nombres_0.png";
//...
{type:"image", src:s_nombresImage},
{type:"fnt", src:s_nombresText},
//...
En myApp.js ya tenemos un ejemplo de cómo crear un label con fuentes TrueType:
//myApp.js
this.helloLabel = cc.LabelTTF.create("Hello World", "Arial", 38);
// position the label on the center of the screen
this.helloLabel.setPosition(cc.p(size.width / 2, size.height - 40));
// add the label as a child to this layer
this.addChild(this.helloLabel, 5);
En la función create pasamos tres parámetros, el texto inicial, el nombre de la fuente, y el tamaño inicial. Posteriormente le colocamos una posición y lo agregamos a la capa principal de la escena.
Si posteriormente queremos cambiar el texto del label, empleamos la función setString sobre el label. Agreguemos este código después del bloque que acabamos de ver:
this.helloLabel.setString("Hola mundo!");
Al recargar el juego veremos el cambio del label.
Para finalizar esta sección, agreguemos un label con bitmapfont, después del bloque que agregamos, escribamos este código:
this.helloBMLabel = cc.LabelBMFont.create("Chao", s_nombresText);
this.helloBMLabel.setAnchorPoint(cc.p(0, 0.5));
this.helloBMLabel.setPosition(cc.p(30, size.height/2));
this.addChild(this.helloBMLabel);
Al recargar el juego veremos estos cambios:

Las fuentes bitmap no son tan flexibles como las fuentes TrueType. Al escalar este tipo de fuentes se verán distorsionadas, por lo que hay que crear bitmaps para cada tamaño de la fuente. Además, se deben incluir todos los caracteres que vayamos a emplear, incluyendo el espacio, o de otra manera el juego no correrá bien al no poder ubicar el caracter que necesita.
Finalizando todo
Acabamos de ver algunos tipos básicos de nodos, textos, imágenes y botones, y cómo se agregan al escenario. En la próxima parte de este tutorial veremos con más detalle cómo puede el jugador controlar el juego, y más adelante cómo se trabaja el cambio entre escenas.
Avatar de la mujer cortesía de NatashaHaggard en OpenGameArt - http://opengameart.org/content/female-avatar
Contenido del botón cortesía de Buch en OpenGameArt - http://opengameart.org/content/horrific-button