domingo, 27 de enero de 2013

Realidad Aumentada - Parte III - Cocos2D

Cocos2D es un framework para realizar juegos y aplicaciones gráficas. Cocos2D tiene algunas características claves como ser facil de usar, rápido, flexible y es open source.

Instalación de Cocos2D
Debemos descargar la versión estable cocos2d-iphone-1.0.1 (ya que el tutorial esta realizado para esta versión) desde  www.cocos2d-iphone.org/download, la desempacamos y en el nuevo directorio encontramos un archivo llamado cocos2D-ios.xcodeproj, abrimos ese proyecto y vemos que tiene como 70 targets, cada uno de ellos para testear una característica diferente.
Podemos seleccionar ParticleTest desde el menú que muestra los Scheme para comprobar que todo funciona correctamente.

Instalación de Templates
Cocos 2D viene con templates para XCode. Hay 3 proyectos principales

 cocos2D stand-alone template
 cocos2D + box2D template
 cocos2D + chipmunk template

Para ejecutar estos scripts abrimos la Terminal y vamos hasta el directorio donde desempaquetamos Cocos 2D. Para instalar los templates ejecutamos los siguientes comandos

cocos2d-iphone-1.0.1$ ./install-templates.sh -u
cocos2d-iphone template installer
Installing Xcode 4 cocos2d iOS template

Crear un proyecto nuevo
 Abrimos XCode, si instalamos correctamente los templates deberiamos ver algo como lo que se muestra en la siguiente figura.



Seleccionamos cocos2D y creamos un nuevo proyecto al cual llamaremos TestCocos2D.
Ejecutamos la aplicación y veremos algo como muestra la siguiente imagen:


Lo que vamos a hacer es convertir este Hello World en un Hello World pero de realidad aumentada.
Como sabemos las aplicaciones de realidad aumentada se realizan teniendo como base la vista de una cámara. En nuestro ejemplo tenemos una pantalla negra como fondo. Esto es lo primero que vamos a reemplazar.
Cocos2D utiliza OpenGL para hacer el render en 2D. Lo que tenemos que hacer es reemplazar lo que llamamos capa de dibujo para reemplazarla con el preview de la cámara. Esta capa implementa el protocolo EAGLDrawable que es el que se utiliza para el render y para mostrar en pantalla el objeto EAGLContext.
Lo que debemos saber por ahora es que no podemos mostrar la cámara con el formato de buffer de 16 bit si no que debemos utilizar el formato color de 32 bits.

Ajustando la vista por defecto

Abrimos AppDelegate.h y Agregamos un UIView a la interface de esta manera
UIView *cameraView;

En  AppDelegate.m lo que debemos hacer es intercambiar el buffer a 32 bits.
Busquemos la linea similar a esta
//EAGLView *glView = [EAGLView viewWithFrame:[window bounds]

//Create the EAGLView manually

//1. Create a RGB565 format. Alternative: RGBA8

//2. depth format of 0 bit. Use 16 or 24 bit for 3d effects, like CCPageTurnTransition

EAGLView *glView = [EAGLView viewWithFrame:[window bounds] pixelFormat:kEAGLColorFormatRGBA8 depthFormat:0];
 //kEAGLColorFormatRGBA8
No podemos usar el formato de 16-bit para la cámara  por eso debemos cambiar el formato kEAGLColorFormatRGB565 por este kEAGLColorFormatRGBA8. Lo próximo que haremos es agregar nuestra UIView a la pantalla en reemplazo del fondo negro. Busquemos este código para poder reemplazarlo:
 // make the View Controller a child of the main window
 [window addSubview: viewController.view];
 
Debajo de esta linea agregamos el siguiente código
 // Seteamos el color de fondo como clearColor para tener transparencia
 [CCDirector sharedDirector].openGLView.backgroundColor = [UIColor clearColor];
 
 // Nos aseguramos que sea el fondo no sea opaco por la misma razón 
 [CCDirector sharedDirector].openGLView.opaque = NO; 
 
 //Volvemos a asegurarnos la transparencia
 glClearColor(0.0, 0.0, 0.0, 0.0);
 
 // A nuestra nueva vista la estiramos del tamaño   
 cameraView = [[UIView alloc] initWithFrame:[[UIScreen mainScreen] bounds]];
 // Hacemos Transparente esta vista
 cameraView.opaque = NO;
 cameraView.backgroundColor=[UIColor clearColor];
 [window addSubview:cameraView];
 
Si ejecutamos el proyecto todavía no vamos a ver alguna diferencia.
Vamos a agregar el siguiente código debajo de lo que agregamos antes para crear el UIImagePicker y mostrar la cámara.
 UIImagePickerController *imagePicker;
    @try {
        imagePicker = [[[UIImagePickerController alloc] init] autorelease]; 
        imagePicker.sourceType = UIImagePickerControllerSourceTypeCamera; 
        imagePicker.showsCameraControls = NO;
        imagePicker.toolbarHidden = YES;
        imagePicker.navigationBarHidden = YES; 
        imagePicker.wantsFullScreenLayout = YES;
    }
    @catch (NSException * e) {
        [imagePicker release]; imagePicker = nil;
    }
    @finally {
        if(imagePicker) {
            [cameraView addSubview:[imagePicker view]]; [cameraView release];
    }
    }
    
    [window_ bringSubviewToFront:viewController.view];
Hay una cosa más por hacer, abrimos HelloWorldLayer.m y cambiamos el texto y el tamaño del label.
// create and initialize a Label
CCLabelTTF *label = [CCLabelTTF labelWithString:@"Hello Augmented World" fontName:@"Marker Felt" fontSize:48];
Ejecutamos el proyecto y vemos que la cámara no ocupa toda la pantalla. En el modo landscape existe una diferencia entre el tamaño de la pantalla y lo que muestra la cámara. La pantalla del iphone tiene un aspect ratio de 3:4 y la cámara de 4:3, por lo tanto debemos escalar la imagen de la cámara para se ajuste.
Para solucionar esto debemos escalar nuestro UIImagePicker así que volvemos a AppDelegate.m y buscamos el siguiente código y agregamos la última linea.
 
@try {

    imagePicker = [[[UIImagePickerController alloc] init] autorelease];      imagePicker.sourceType = UIImagePickerControllerSourceTypeCamera; imagePicker.showsCameraControls = NO;
    imagePicker.toolbarHidden = YES;
    imagePicker.navigationBarHidden = YES; 
    imagePicker.wantsFullScreenLayout = YES; 
    imagePicker.cameraViewTransform =CGAffineTransformScale(imagePicker.cameraViewTransform, 1.0, 1.3);
 }
Ejecutamos nuevamente el proyecto y vemos que ya la cámara ocupa todoa la pantalla.

Touches
Cocos2D consta una clase singleton llamada CCTouchDispatcher la cual se utiliza para que objetos de la misma envien notificaciones de eventos en la pantalla.
Tenemos 2 capas, una en el fondo que es la de la cámara y otra sobre esta para mostrar el contenido en la pantalla.
Para que este disponible los eventos de touch abrimos HelloWorldLayer.m y en el método init hacemos

 self.isTouchEnabled = YES;

 Luego tenemos que registrar a CCTouchDispatcher como la capa que acepta este tipo de eventos. Después del método init agregamos el siguiente mensaje:

 - (void)registerWithTouchDispatcher {
 [[CCTouchDispatcher sharedDispatcher] addTargetedDelegate:self priority:0 swallowsTouches:YES]; 
}

Seteamos el delegado como self y hacemos que ninguna otra capa responda ante algún evento salvo que decidamos que si puede manejar el evento.
Para aceptar el evento usamos el método ccTouchBegan. Justo después del método registerWithTouchDispatcher agregamos el siguiente código:
 
- (BOOL)ccTouchBegan:(UITouch *)touch withEvent:(UIEvent *)event {
    return YES;
} 

- (void)ccTouchEnded:(UITouch *)touch withEvent:(UIEvent *)event {
    CGPoint location = [self convertTouchToNodeSpace:touch];
    NSLog(@"Touch %@: ", NSStringFromCGPoint(location));
} 
El primer método devuelve YES al dispatcher para que sepamos que acepta el evento y que el dispatcher no debe buscar otra respuesta. El segundo evento reacciona al touch guardando el lugar donde se produce el touch en la pantalla. Usamos el método NSStringFromCGPoint para castear correctamente un struct de C a un string. Ejecutamos la aplicacion nuevamente en nuestro dispositivo, tocamos al azar la pantalla y deberiamos ver una imagen como la siguiente:


Código la tercera parte Realidad Aumentada Parte 3

4 comentarios:

Anónimo dijo...

Genial, me interesa mucho la realidad aumentada y la programación de juegos.
Mil gracias, seguid así.

Anónimo dijo...

Genial, me interesa mucho la realidad aumentada y la programación de juegos.
Mil gracias, seguid así.

Anónimo dijo...

Buen recurso. Te seguire con atención . Animo!

Sergio dijo...

Buen recurso, te seguire con atenición. Gracias!