Bugs Boxes crearemos un simple juego con Box2d


2013-07-03 11:50:35

Bugs Boxes nuestro primer juego en Box2d

Con este tutorial podrás programar un simple juego de física utilizando la librería box2d (cocos2d). El juego consiste en apilar una serie de cajas que es arrojada por un bicho volador que se mueve de un lado al otro de la pantalla. Las cajas se apilarán formando una columna y ganarás cuando consigas llegar a la cima, pero no será nada sencillo ya que el bicho avanzará mas de prisa a medida que el tiempo pase.

Para comenzar este tutorial es necesario haber creado un proyecto como se explica en el tutorial anterior Box2d iOS ya que a partir de esa plantilla comenzaremos a desarrollar este juego.

Primera parte: Background y Sprite bicho

Con el proyecto ya creado y modificado como explica nuestro tutorial anterior Box2d iOS crearemos 2 nuevos sprite. El primero será poner una imagen de fondo y el segundo será un insecto volador que se moverá de un lateral al otro de la pantalla y que cuando pulsemos la pantalla caiga un bloque a partir de la posición donde se encuentra este insecto que llamaremos Bug.

Definimos en el archivo HelloWorldLayer.h dos nuevos Objetos tipo CCSprite (texto en rojo).


#import 'cocos2d.h'

#import 'Box2D.h'

#import 'GLES-Render.h'



// HelloWorldLayer

@interface HelloWorldLayer : CCLayer{

    b2World* world;

    GLESDebugDraw *m_debugDraw;

    

    CCSprite *background;

    CCSprite *bicho;

}



+(CCScene *) scene;

//añade un nuevo Sprite

-(void) addNewSpriteWithCoords:(CGPoint)p;



@end

Antes de continuar descarga las imágenes necesarias y cópialas en la carpeta Resources del proyecto. Estas son:

Ahora pasamos al archivo HelloWorldLayer.m y añadimos el código de color rojo al método init().


-(id) init{

    if((self=[super init])) {

        //habilitamos el toque de pantalla y el acelerometro

	self.isTouchEnabled = YES;

	self.isAccelerometerEnabled = YES;

		

        //obtenemos el tamaño de la pantalla del dispositivo

	CGSize screenSize = [CCDirector sharedDirector].winSize;

		

	//definimos el efecto de gravedad para el juego

	b2Vec2 gravity;

	gravity.Set(0.0f, -10.0f);

		

        //creamos el mundo donde insertaremos todos los objetos de nuestro juego

	world = new b2World(gravity, TRUE);

		

	//definimos los limites de la zona de juego. 

	b2BodyDef groundBodyDef;

	groundBodyDef.position.Set(0, 0);// bottom-left corner

        

	b2Body* groundBody = world->CreateBody(&groundBodyDef);

		

	b2PolygonShape groundBox;		

		

	// suelo o límite inferior

	groundBox.SetAsEdge(b2Vec2(0,0), b2Vec2(screenSize.width/PTM_RATIO,0));

	groundBody->CreateFixture(&groundBox,0);

		

	//limite superior

	groundBox.SetAsEdge(b2Vec2(0,screenSize.height/PTM_RATIO), b2Vec2(screenSize.width/PTM_RATIO,screenSize.height/PTM_RATIO));

	groundBody->CreateFixture(&groundBox,0);

		

	//pared izquierda o limite izquierdo

	groundBox.SetAsEdge(b2Vec2(0,screenSize.height/PTM_RATIO), b2Vec2(0,0));

	groundBody->CreateFixture(&groundBox,0);

		

	//pared derecha o limite derecho

		groundBox.SetAsEdge(b2Vec2(screenSize.width/PTM_RATIO,screenSize.height/PTM_RATIO), b2Vec2(screenSize.width/PTM_RATIO,0));

	groundBody->CreateFixture(&groundBox,0);



        //creamos el bicho que se moverá de un lado al otro de la pantalla

        bicho = [CCSprite spriteWithFile:@'bicho.png'];

        bicho.position = ccp([bicho texture].contentSize.width/2, screenSize.height-50);

        [self addChild:bicho z:1];

		

        [self schedule: @selector(tick:)];

    }

    return self;

}

Ahora en el método tick añadiremos el código necesario para que el sprite bicho se mueva de un lado al otro de la pantalla


-(void) tick: (ccTime) dt{

    world->Step(dt, 10, 10);



    //Recorremos todos los sprites que contiene nuestro juego

    for (b2Body* b = world->GetBodyList(); b; b = b->GetNext()){

	if (b->GetUserData() != NULL) {

            //Actualizamos la posición y el ángulo de nuestro sprite en función del cuerpo o esqueleto

	    CCSprite *myActor = (CCSprite*)b->GetUserData();

	    myActor.position = CGPointMake( b->GetPosition().x * PTM_RATIO, b->GetPosition().y * PTM_RATIO);

	    myActor.rotation = -1 * CC_RADIANS_TO_DEGREES(b->GetAngle());

	}	

    }



    //obtenemos el tamaño de la pantalla del dispositivo

    CGSize screenSize = [CCDirector sharedDirector].winSize;

    //actualizamos la posición del bicho, avanzará en función a su orientación

    if(bicho.flipX)bicho.position = ccp(bicho.position.x-1,bicho.position.y);

    else bicho.position = ccp(bicho.position.x+1,bicho.position.y);

    

    //limitamos el movimiento del bicho para cuando llegue a un lado de la pantalla cambie de sentido

    if(bicho.position.x - ([bicho texture].contentSize.width/2) < 0)[bicho setFlipX:FALSE];

    else if(bicho.position.x + ([bicho texture].contentSize.width/2) > screenSize.width)[bicho setFlipX:TRUE];

}

El código es muy sencillo primero obtenemos el tamaño de pantalla y la almacenamos en screenSize. Para mover el bicho consultamos la orientación del sprite bicho, utilizamos la propiedad flipX. Finalmente consultamos la posición del bicho y la comparamos con la pantalla para que éste no se salga de la misma y cuando toque uno de los laterales cambiar el estado de flipX.

Para finalizar la primera parte del juego modificamos el método ccTouchesEnded y cambiamos el parámetro pasado a addNewSpriteWithCoords. En vez de pasar la posición donde hemos pulsamos, pasaremos la posición del sprite bicho.


- (void)ccTouchesEnded:(NSSet *)touches withEvent:(UIEvent *)event{

    //Add a new body/atlas sprite at the touched location

    for( UITouch *touch in touches ) {

	CGPoint location = [touch locationInView: [touch view]];

		

	location = [[CCDirector sharedDirector] convertToGL: location];

	//crea un nuevo sprite en las coordenadas de la pantalla

	[self addNewSpriteWithCoords: bicho.position];

    }

}

Si se ejecuta el programa verás algo similar a la siguiente imagen.


Segunda parte: Detectar colisiones

Para poder obtener que cuerpos colisionan tendremos que crear una nueva clase para poder obtner esta información. Vamos a ello:

Creamos una nueva clase y la llamaremos MyContactListener. Ahora diponemos en nuestro proyecto los archivos MyContactListener.h y MyContactListener.m y modificamos los mismos copiando el siguiente código:

MyContactListener.h


#import 'Box2D.h'

#import 

#import 



struct MyContact {

    b2Fixture *fixtureA;

    b2Fixture *fixtureB;

    bool operator==(const MyContact& other) const{

        return (fixtureA == other.fixtureA) && (fixtureB == other.fixtureB);

    }

};

    

class MyContactListener : public b2ContactListener {

    public:

        std::vector_contacts;

        

        MyContactListener();

        ~MyContactListener();

        

        virtual void BeginContact(b2Contact* contact);

        virtual void EndContact(b2Contact* contact);

        virtual void PreSolve(b2Contact* contact, const b2Manifold* oldManifold);

        virtual void PostSolve(b2Contact* contact, const b2ContactImpulse* impulse);

        

};

MyContactListener.m


#import 'MyContactListener.h'



MyContactListener::MyContactListener() : _contacts() {

}



MyContactListener::~MyContactListener() {

}



void MyContactListener::BeginContact(b2Contact* contact) {

    // We need to copy out the data because the b2Contact passed in

    // is reused.

    MyContact myContact = { contact->GetFixtureA(), contact->GetFixtureB() };

    _contacts.push_back(myContact);

}



void MyContactListener::EndContact(b2Contact* contact) {

    MyContact myContact = { contact->GetFixtureA(), contact->GetFixtureB() };

    std::vector::iterator pos;

    pos = std::find(_contacts.begin(), _contacts.end(), myContact);

    if (pos != _contacts.end()) {

        _contacts.erase(pos);

    }

}



void MyContactListener::PreSolve(b2Contact* contact, const b2Manifold* oldManifold) {

}



void MyContactListener::PostSolve(b2Contact* contact, const b2ContactImpulse* impulse) {

}

Ahora que disponemos de la clase MyContactListener ya podremos escribir el código para saber que cuerpos colisionan. Importa la nueva clase creada y crea un objeto a partir de esta, además tendremos que crear un objeto del tipo b2Fixture para guardar la definición del suelo y así poder obtener la colisión entre un bloque y el suelo.


#import 'cocos2d.h'

#import 'Box2D.h'

#import 'GLES-Render.h'

#import 'MyContactListener.h'



// HelloWorldLayer

@interface HelloWorldLayer : CCLayer{

    b2World* world;

    GLESDebugDraw *m_debugDraw;

    

    CCSprite *background;

    CCSprite *bicho;

    MyContactListener *_contactListener;

    b2Fixture *_bottomFixture;

}



+(CCScene *) scene;

//añade un nuevo Sprite

-(void) addNewSpriteWithCoords:(CGPoint)p;



@end

Ahora pasamos al archivo HelloWorldLayer.m y el método init() asignamos a _bottomFixture la definición del suelo y más abajo podrás ver que se crear el objeto _contactListener para obtener el listado de cuerpos en colisión. Has de copiar el texto en color rojo como está en el siguiente código.


-(id) init{

    if((self=[super init])) {

        ...		

		

	// suelo o límite inferior

	groundBox.SetAsEdge(b2Vec2(0,0), b2Vec2(screenSize.width/PTM_RATIO,0));

	_bottomFixture = groundBody->CreateFixture(&groundBox,0);

		

	...



        // Create contact listener

        _contactListener = new MyContactListener();

        world->SetContactListener(_contactListener);



        [self schedule: @selector(tick:)];

    }

    return self;

}

Ahora solo nos queda consultar el estado de los cuerpos. Para ello vamos a añadir el siguiente código al final del método tick.


-(void) tick: (ccTime) dt{



    ...



    //recorremos todos los cuerpos dinámicos del juego que están en colision

    std::vectortoDestroy;

    std::vector::iterator pos;

    for(pos=_contactListener->_contacts.begin();pos != _contactListener->_contacts.end(); ++pos) {

        MyContact contact = *pos;

        

        //buscamos si algun cuerpo ha colisionado con la zona del suelo

        if(contact.fixtureA == _bottomFixture || contact.fixtureB == _bottomFixture){

            background.color = ccc3(255, 0, 0);

        }

    }

}

Si ejecutas el programa verás que cuando caiga un bloque y colisione con el suelo el fondo se coloreará en rojo.


Tercera parte: Cuerpo estático para la zona de caída

En esta tercera parte vamos a crear un sprite (CCSprite) y un cuerpo b2body pero de tipo estático ya que no le ha de afectar la física y éste será la zona donde deberán caer las cajas que es arrojada por el bicho volador.

En la clase HelloWorldLayer.m método init() vamos a crear un nuevo sprite (CCSprite) y un nuevo cuerpo b2body. La forma de crearlo es idéntica a la forma que hicimos con los bloques pero el tipo de cuerpo será estático. Añadimos al final el siguiente código.


-(id) init{

    if((self=[super init])) {

        ...



        CCSprite *rock = [CCSprite spriteWithFile:@'rock.png'];

        rock.position = ccp(screenSize.width/2, 0);

        [self addChild:rock z:1];



        //definimos el cuerpo o esqueleto del sprite

        b2BodyDef bodyDef;

        //cuerpo tipo estático

        bodyDef.type = b2_staticBody;

        

        bodyDef.position.Set(rock.position.x/PTM_RATIO, rock.position.y/PTM_RATIO);

        bodyDef.userData = rock;

        b2Body *body = world->CreateBody(&bodyDef);

        

        //definimos la forma del sprite con una forma geométrica

        b2PolygonShape dynamicBox;

        dynamicBox.SetAsBox(rock.contentSize.width/PTM_RATIO/2, rock.contentSize.height/PTM_RATIO/2);

        

        //Definimos las propiedades del cuerpo o esqueleto del sprite

        b2FixtureDef fixtureDef;

        fixtureDef.shape = &dynamicBox;

        fixtureDef.density = 1.0f;

        fixtureDef.friction = 0.3f;

        fixtureDef.restitution = 0.0f;

        body->CreateFixture(&fixtureDef);



        [self schedule: @selector(tick:)];

    }

    return self;

}

Si ejecutas el programa podrás ver que si las cajas caen sobre la roca se posan sobre ella pero si caen fuera de esta zona el fondo se pone rojo (game over).


Cuarta parte: Retoques finales

La parte importante del juego ya fue creada y explicada al detalle. Ahora realizaremos los retoques finales del juego con lo cual añadiremos las siguientes características:
  • Menú para comenzar la partida
  • Métodos para controlar el estado del juego
  • Score para mostrar los puntos obtenidos
  • Añadimos un Sprite al Sprite Bicho para mejorar su aspecto
  • Utilizamos el método IsAwake() del objeto b2body para saber cuando los cuerpos (bloques de hielo) no están en movimiento y así finalizar la partida.
  • Añadimos música de fondo y un simple sonido cuando el bicho arroja el bloque de hielo

En la Clase HelloWorldLayer.h añadimos los cambios (texto en rojo). Aquí importamos la librería SimpleAudioEngine.h ya que la utilizaremos para agregar música y sonido. Luego definimos unas constantes para controlar el estado del juego, para identificar el tipo de cuerpo, controlar la velocidad inicial, aceleración y máxima velocidad del desplazamiento x del bicho y la cantidad máxima de bloques de hielo que ha de colocar sobre la roca para ganar la partida.

Definimos los distintos elementos para agregar el menú del juego, el texto para mostrar si se ha ganado o perdido la partida y otros valores de tipo entero para controlar el estado del juego, la velocidad del bicho y contador de bloques de hielo. Finalmente definimos los métodos para controlar el estado del juego

Clase HelloWorldLayer.h


#import 'cocos2d.h'

#import 'Box2D.h'

#import 'GLES-Render.h'

#import 'MyContactListener.h'

#import 'SimpleAudioEngine.h'



static const int GAME_PRESTART = 0;

static const int GAME_START = 1;

static const int GAME_WIN = 2;

static const int GAME_GAMEOVER = 3;



static const int TYPE_BLOCK = 1;

static const int TYPE_ROCK = 2;



static const int SPEED_INI = 2;

static const float SPEED_INCR = 0.25;

static const int SPEED_MAX = 10;



static const int MAX_BLOCKS = 8;





// HelloWorldLayer

@interface HelloWorldLayer : CCLayer{

    b2World* world;

    GLESDebugDraw *m_debugDraw;

    

    CCSprite *background;

    CCSprite *bicho;

    

    MyContactListener *_contactListener;

    b2Fixture *_bottomFixture;

    

    

    CCMenu *menu;

    CCMenuItemLabel *menuStartGame;

    CCLabelBMFont *lblGameover;

    CCLabelBMFont *lblScore;

    int createdBlocks;

    int totalBocks;

    int statGame;

    float bicho_speed;

}



+(CCScene *) scene;

//añade un nuevo Sprite

-(void) addNewSpriteWithCoords:(CGPoint)p;

-(void)startGame;

-(void)gameover;

-(void)winGame;



@end

HelloWorldLayer.m


#import 'HelloWorldLayer.h'

#define PTM_RATIO 32





// HelloWorldLayer implementation

@implementation HelloWorldLayer



+(CCScene *) scene{

    // 'scene' is an autorelease object.

    CCScene *scene = [CCScene node];

	

    // 'layer' is an autorelease object.

    HelloWorldLayer *layer = [HelloWorldLayer node];

	

    // add layer as a child to scene

    [scene addChild: layer];

	

    // return the scene

    return scene;

}



-(id) init{

    if( (self=[super init])) {

	//habilitamos el toque de pantalla y el acelerometro

	self.isTouchEnabled = YES;

	self.isAccelerometerEnabled = YES;

        

        [[SimpleAudioEngine sharedEngine] playBackgroundMusic:@'music.WAV' loop:YES];

        [[SimpleAudioEngine sharedEngine] setBackgroundMusicVolume:0.15];

        [[SimpleAudioEngine sharedEngine] setEffectsVolume:0.5];

		

        //obtenemos el tamaño de la pantalla del dispositivo

	CGSize screenSize = [CCDirector sharedDirector].winSize;

        

        background = [CCSprite spriteWithFile:@'background.png'];

        background.position = ccp(screenSize.width/2,screenSize.height/2);

        [self addChild:background z:0];

		

	//definimos el efecto de gravedad para el juego

	b2Vec2 gravity;

	gravity.Set(0.0f, -10.0f);

		

        //creamos el mundo donde insertaremos todos los objetos de nuestro juego

	bool doSleep = true;

	world = new b2World(gravity, doSleep);

		

	//definimos los limites de la zona de juego. 

	b2BodyDef groundBodyDef;

	groundBodyDef.position.Set(0, 0);// bottom-left corner

        

	b2Body* groundBody = world->CreateBody(&groundBodyDef);

		

	b2PolygonShape groundBox;		

		

	// suelo o límite inferior

	groundBox.SetAsEdge(b2Vec2(0,0), b2Vec2(screenSize.width/PTM_RATIO,0));

	_bottomFixture = groundBody->CreateFixture(&groundBox,0);

		

	//limite superior

	groundBox.SetAsEdge(b2Vec2(0,screenSize.height/PTM_RATIO), b2Vec2(screenSize.width/PTM_RATIO,screenSize.height/PTM_RATIO));

	groundBody->CreateFixture(&groundBox,0);

		

	//pared izquierda o limite izquierdo

	groundBox.SetAsEdge(b2Vec2(0,screenSize.height/PTM_RATIO), b2Vec2(0,0));

	groundBody->CreateFixture(&groundBox,0);

		

	//pared derecha o limite derecho

	groundBox.SetAsEdge(b2Vec2(screenSize.width/PTM_RATIO,screenSize.height/PTM_RATIO), b2Vec2(screenSize.width/PTM_RATIO,0));

	groundBody->CreateFixture(&groundBox,0);

        

        //creamos el bicho que se moverá de un lado al otro de la pantalla

        bicho = [CCSprite spriteWithFile:@'bicho.png'];

        bicho.position = ccp([bicho texture].contentSize.width/2, screenSize.height-50);

        [self addChild:bicho z:1];

        

        //añadimos un sprite al bicho

        CCSprite *box_child = [CCSprite spriteWithFile:@'blocks.png'];

        box_child.tag = 1;

        box_child.position = ccp([bicho texture].contentSize.width/2,-10);

        [bicho addChild:box_child];

        

        // Create contact listener

        _contactListener = new MyContactListener();

        world->SetContactListener(_contactListener);

        

        CCSprite *rock = [CCSprite spriteWithFile:@'rock.png'];

        rock.position = ccp(screenSize.width/2, 0);

        rock.tag = TYPE_ROCK;

        [self addChild:rock z:1];

        

        //definimos el cuerpo o esqueleto del sprite

        b2BodyDef bodyDef;

        bodyDef.type = b2_staticBody;

        

        bodyDef.position.Set(rock.position.x/PTM_RATIO, rock.position.y/PTM_RATIO);

        bodyDef.userData = rock;

        b2Body *body = world->CreateBody(&bodyDef);

        

        //definimos la forma del sprite con una forma geométrica

        b2PolygonShape dynamicBox;

        dynamicBox.SetAsBox(rock.contentSize.width/PTM_RATIO/2, rock.contentSize.height/PTM_RATIO/2);

        

        //Definimos las propiedades del cuerpo o esqueleto del sprite

        b2FixtureDef fixtureDef;

        fixtureDef.shape = &dynamicBox;

        fixtureDef.density = 1.0f;

        fixtureDef.friction = 0.3f;

        fixtureDef.restitution = 0.0f;

        body->CreateFixture(&fixtureDef);

        

        lblGameover = [CCLabelBMFont labelWithString:@'Game Over' fntFile:@'font_game.fnt'];

	lblGameover.position = ccp(screenSize.width/2,screenSize.height-100);

	[self addChild:lblGameover z:10];

        // Acción girar

        id rotateAction3 = [CCScaleBy actionWithDuration:2 scale:1.1];

        id backwards3 = [rotateAction3 reverse];

        id sequence3 = [CCSequence actions: rotateAction3, backwards3, nil];

        id repeat3 = [CCRepeatForever actionWithAction:sequence3];

        [lblGameover runAction:repeat3];

        lblGameover.visible = FALSE;

        

        

        lblScore = [CCLabelBMFont labelWithString:@'score: 0' fntFile:@'font_game.fnt'];

	lblScore.position = ccp(screenSize.width/2,screenSize.height-20);

        lblScore.scale = 0.5;

	[self addChild:lblScore z:10];

        

        CCLabelBMFont *label = [CCLabelBMFont labelWithString:@'PLAY' fntFile:@'font_game.fnt'];

        menuStartGame = [CCMenuItemLabel itemWithLabel:label target:self selector:@selector(startGame)];

        menuStartGame.position = ccp(screenSize.width/2,screenSize.height/2);

        

        menu = [CCMenu menuWithItems: menuStartGame, nil];

        menu.position = ccp(0,0);

	[self addChild: menu z:10];

        id rotateAction4 = [CCScaleBy actionWithDuration:0.30 scale:1.1];

        id backwards4 = [rotateAction4 reverse];

        id sequence4 = [CCSequence actions: rotateAction4, backwards4, nil];

        id repeat4 = [CCRepeatForever actionWithAction:sequence4];

        [menu runAction:repeat4];

        

        [self schedule: @selector(tick:)];

    }

    return self;

}



-(void)startGame{

    //eliminamos todos los cuerpos y sprites asociados

    for (b2Body* b = world->GetBodyList(); b; b = b->GetNext()){

	if (b->GetUserData() != NULL) {

            CCSprite *myActor = (CCSprite*)b->GetUserData();

            if(myActor.tag==TYPE_BLOCK){

                [self removeChild:myActor cleanup:TRUE];

                world->DestroyBody(b);

            }

        }

    }

    //inicializamos los valores

    createdBlocks = 0;

    background.color = ccc3(255, 255, 255);

    lblGameover.visible = FALSE;

    menu.visible = FALSE;

    statGame = GAME_START;

    bicho_speed = SPEED_INI;

}



-(void)gameover{

    [lblGameover setString:@'Game Over'];

    background.color = ccc3(255, 0, 0);

    lblGameover.visible = TRUE;

    menu.visible = TRUE;

    statGame = GAME_GAMEOVER;

}



-(void)winGame{

    [lblGameover setString:@'Win Game'];

    lblGameover.visible = TRUE;

    menu.visible = TRUE;

    statGame = GAME_WIN;

}



-(void) addNewSpriteWithCoords:(CGPoint)p{

    //cramos un sprite igual que se hace en cocos2d

    CCSprite *sprite = [CCSprite spriteWithFile:@'blocks.png'];

    sprite.position = ccp( p.x, p.y);

    sprite.tag = TYPE_BLOCK;

    [self addChild:sprite z:1];

	

    //definimos el cuerpo o esqueleto del sprite

    b2BodyDef bodyDef;

    bodyDef.type = b2_dynamicBody;



    bodyDef.position.Set(p.x/PTM_RATIO, p.y/PTM_RATIO);

    bodyDef.userData = sprite;

    b2Body *body = world->CreateBody(&bodyDef);

	

    //definimos la forma del sprite con una forma geométrica

    b2PolygonShape dynamicBox;

    dynamicBox.SetAsBox(sprite.contentSize.width/PTM_RATIO/2, sprite.contentSize.height/PTM_RATIO/2);

	

    //Definimos las propiedades del cuerpo o esqueleto del sprite

    b2FixtureDef fixtureDef;

    fixtureDef.shape = &dynamicBox;	

    fixtureDef.density = 1.0f;

    fixtureDef.friction = 1.0f;

    fixtureDef.restitution = 0.0f;

    body->CreateFixture(&fixtureDef);

    createdBlocks++;

}







-(void) tick: (ccTime) dt{

    world->Step(dt, 10, 10);



    //Recorremos todos los sprites que contiene nuestro juego

    for (b2Body* b = world->GetBodyList(); b; b = b->GetNext()){

	if (b->GetUserData() != NULL) {

	    //Actualizamos la posición y el ángulo de nuestro sprite en función del cuerpo o esqueleto

	    CCSprite *myActor = (CCSprite*)b->GetUserData();

	    myActor.position = CGPointMake( b->GetPosition().x * PTM_RATIO, b->GetPosition().y * PTM_RATIO);

	    myActor.rotation = -1 * CC_RADIANS_TO_DEGREES(b->GetAngle());

	}	

    }

    

    //obtenemos el tamaño de la pantalla del dispositivo

    CGSize screenSize = [CCDirector sharedDirector].winSize;

    //actualizamos la posición del bicho, avanzará en función a su orientación

    if(bicho.flipX)bicho.position = ccp(bicho.position.x-bicho_speed,bicho.position.y);

    else bicho.position = ccp(bicho.position.x+bicho_speed,bicho.position.y);

    

    //limitamos el movimiento del bicho para cuando llegue a un lado de la pantalla cambie de sentido

    if(bicho.position.x - ([bicho texture].contentSize.width/2) < 0){

        [bicho setFlipX:FALSE];

        if(bicho_speed

    }else if(bicho.position.x + ([bicho texture].contentSize.width/2) > screenSize.width){

        [bicho setFlipX:TRUE];

        if(bicho_speed

    }

    

    if(statGame==GAME_START){

        totalBocks = 0;

        BOOL isGameOver = FALSE;

        //recorremos todos los cuerpos dinámicos del juego que están en colision

        std::vectortoDestroy;

        std::vector::iterator pos;

        for(pos=_contactListener->_contacts.begin();pos != _contactListener->_contacts.end(); ++pos) {

            MyContact contact = *pos;

            

            //buscamos si algun cuerpo ha colisionado con la zona del suelo

            if(contact.fixtureA == _bottomFixture || contact.fixtureB == _bottomFixture){

                isGameOver = TRUE;

            }

            

            //obtenemos los cuerpos que han entrado en colision

            b2Body *bodyA = contact.fixtureA->GetBody();

            b2Body *bodyB = contact.fixtureB->GetBody();

            

            //obtenmos los sprites contenidos dentro de los cuerpos

            if(bodyA->GetUserData() != NULL){

                CCSprite *spriteA = (CCSprite *) bodyA->GetUserData();

                if(spriteA.tag==TYPE_BLOCK || spriteA.tag==TYPE_ROCK){

                    if(!bodyA->IsAwake())totalBocks++;

                }

            }else if(bodyB->GetUserData() != NULL){

                CCSprite *spriteB = (CCSprite *) bodyB->GetUserData();

                if(spriteB.tag==TYPE_BLOCK || spriteB.tag==TYPE_ROCK){

                    if(!bodyB->IsAwake())totalBocks++;

                }

            }

        }

        if(isGameOver)[self gameover];

        else {

            [lblScore setString:[[NSString alloc] initWithFormat:@'score: %d',totalBocks*100]];

            if(totalBocks>=MAX_BLOCKS)[self winGame];

        }

    }

}



- (void)ccTouchesEnded:(NSSet *)touches withEvent:(UIEvent *)event{

    //Add a new body/atlas sprite at the touched location

    for( UITouch *touch in touches ) {

	CGPoint location = [touch locationInView: [touch view]];

		

	location = [[CCDirector sharedDirector] convertToGL: location];

        

        //obtenemos el sprite hijo que contiene el sprite bicho

        CCNode *bicho_child = [bicho getChildByTag:1];

        //convertimos la posición del sprite hijo a posición absoluta

        CGPoint pos = [bicho convertToWorldSpace:bicho_child.position];

        

	//crea un nuevo sprite en las coordenadas de la pantalla

        if(statGame==GAME_START && createdBlocks < MAX_BLOCKS){

            [self addNewSpriteWithCoords: pos];

            [[SimpleAudioEngine sharedEngine] playEffect:@'jump.wav'];

        }

    }

}



- (void)accelerometer:(UIAccelerometer*)accelerometer didAccelerate:(UIAcceleration*)acceleration{	

	

}



//liberamos los recursos utilizados

- (void) dealloc{

    delete world;

    world = NULL;

    delete m_debugDraw;

    [super dealloc];

}

@end

Acabado esto podrás ver el vídeo con el juego en ejecución. Recuerda que puedes ampliarlo y añadir más características

Descarga de proyecto base y final

Publicidad