Cocos2d-x: Enable Box2D Debugger

Today, I shall discuss about enabling box2d debugger in cocos2d-x. Box2d is 2d physics simulation engine for game development. Debugger in box2d is important in terms when developer is creating new fixture, debugger makes it easy for developer to adjust the fixtures of worlds objects.


You can download the complete source code for this tutorial.

Download Link

Now lets begin with some coding.

Create new cocos2dx win32 application project. Name your project debug_box2d.
The default HelloWorldScene files will be created.

In your “HelloWorldScene.h” file include the following directive.

#include "GLES-Render.h" 

 

Go to  “~tests\tests\Box2DTestBed\” path of your project root and copy “GLES-Render.h & GLES-Render.cpp” to you classes folder.

Now modify “HelloWorldScene.h” file as below:

~HelloWorld();
 HelloWorld();
 void HelloWorld::tick(cocos2d::ccTime dt);  

 

Add constructor, destructor and tic method declaration under public domain.
Add following code under private domain:

private:
      b2World *_world; b2Body *_body; cocos2d::CCSprite *_ball;
  
      // For debugging.  
      GLESDebugDraw* m_debugDraw;
  
      virtual void draw();  

 

What we have done here is that we have added world, body and ball sprite variable declarations, along with debugger variable declaration. We have also added draw method declaration, which we shall override for debugging purpose.

Now add following lines of code to your “HelloWorldScene.cpp” file.
First define PTM_RATIO variable, which we shall use to convert our point coordinate system to meters as box2d world coordinate system uses meter scale.

Add below code after your namespace.

// Point to Meter ratio for physics world. 
 #define PTM_RATIO 32  

 

Add definition for tick method.

void HelloWorld::tick(ccTime dt) 
 {  
   int positionIterations = 10;  
   int velocityIterations = 10;
  
   _world->Step(dt, velocityIterations, positionIterations);  
  
   for (b2Body *body = _world->GetBodyList(); body != NULL; body = body->GetNext())   
   {  
     if (body->GetUserData()) 
     {  
       CCSprite *sprite = (CCSprite *) body->GetUserData();  
       sprite->setPosition(ccp(body->GetPosition().x * PTM_RATIO,   
       body->GetPosition().y * PTM_RATIO));  
       sprite->setRotation(-1 * CC_RADIANS_TO_DEGREES(body->GetAngle()));  
     }  
   }  
 }  

 

Tick method will simulate your physics world.
Now add definitions for constructor and destructor.

HelloWorld::HelloWorld() : _body(NULL), _world(NULL), m_debugDraw(NULL)  
{ }
  
HelloWorld::~HelloWorld() 
{  
   delete m_debugDraw;
  
   if (_body)
   {  
     _world->DestroyBody(_body);  
     _body = NULL;  
   }
  
   if (_world) 
   {  
     delete _world;  
     _world = NULL;  
   }  
 }  

 

We have to de-allocate whatever we have allocated.
Now add definition for draw method.

void HelloWorld::draw(void)   
 {  
      CCLayer::draw();
  
      if( _world )  
      {  
           // Disbable different states.  
           glDisable(GL_TEXTURE_2D);  
           glDisableClientState(GL_COLOR_ARRAY);  
           glDisableClientState(GL_TEXTURE_COORD_ARRAY);
  
           // Draw physics world debuging.
           glEnableClientState(GL_VERTEX_ARRAY);
  
           _world->DrawDebugData();     
  
           glDisableClientState(GL_VERTEX_ARRAY);   // <-------- You need GL_VERTEX_ARRAY disabled          
  
           // Enable different states.          
           glEnableClientState(GL_TEXTURE_COORD_ARRAY);  
           glEnableClientState(GL_COLOR_ARRAY);
           glEnable(GL_TEXTURE_2D);  
   }  
 }  

 

Here, we have disabled and enabled some states before our drawing, then we disabled and enabled those states again after our drawing.

Now make following changes into init method.

          // Create ball sprite   
           _ball = CCSprite::spriteWithFile("Ball.jpg", CCRectMake(0, 0, 52, 52));
           _ball->setPosition(CCPoint(winSize.width/2, winSize.height/2));
           this->addChild(_ball);          
  
           // Create a world   
           b2Vec2 gravity = b2Vec2(0.0f, -10.0f);   
           _world = new b2World(gravity);
  
           // Create edges around the entire screen   
           b2BodyDef groundBodyDef; 
           groundBodyDef.position.Set(0,0);   
           b2Body *groundBody = _world->CreateBody(&groundBodyDef);
  
           b2EdgeShape groundEdge; 
  
           b2FixtureDef boxShapeDef;   
           boxShapeDef.shape = &groundEdge;
  
           // Bottom.  
           groundEdge.Set(b2Vec2(0,0), b2Vec2(winSize.width/PTM_RATIO, 0));   
           groundBody->CreateFixture(&boxShapeDef);
  
           // Left.
           groundEdge.Set(b2Vec2(0,0), b2Vec2(0, winSize.height/PTM_RATIO));   
           groundBody->CreateFixture(&boxShapeDef);
  
           // Top.
           groundEdge.Set(b2Vec2(0, winSize.height/PTM_RATIO),           
                                 b2Vec2(winSize.width/PTM_RATIO, winSize.height/PTM_RATIO)); 
           groundBody->CreateFixture(&boxShapeDef); 
  
           // Right.
           groundEdge.Set(b2Vec2(winSize.width/PTM_RATIO, winSize.height/PTM_RATIO), b2Vec2(winSize.width/PTM_RATIO, 0)); 
           groundBody->CreateFixture(&boxShapeDef);  
  
           // Create ball body. 
           b2BodyDef ballBodyDef; 
           ballBodyDef.type = b2_dynamicBody; 
           ballBodyDef.position.Set(_ball->getPositionX()/PTM_RATIO, _ball->getPositionY()/PTM_RATIO); 
           ballBodyDef.userData = _ball; 
           _body = _world->CreateBody(&ballBodyDef);
  
           // Create ball shape.
           b2CircleShape circle; 
           circle.m_radius = 26.0/PTM_RATIO;  

           // Create ball Fixture.  
           b2FixtureDef ballShapeDef; 
           ballShapeDef.shape = &circle; 
           ballShapeDef.density = 1.0f; 
           ballShapeDef.friction = 0.2f; 
           ballShapeDef.restitution = 0.8f; 
           _body->CreateFixture(&ballShapeDef); 

           // Schedule tick method for physics simulation.
           this->schedule(schedule_selector(HelloWorld::tick), 1/60.0);  

 

What we have done here is create ball sprite position it to center of the screen, create physics world, create ball body into the world, create fixture for the ball body and finally, schedule our tick method for physics simulation.
Now add following lines of code in init method after above code.

           // Box2d Debugging.
           m_debugDraw = new GLESDebugDraw( PTM_RATIO );
           _world->SetDebugDraw(m_debugDraw);
  
           // Enable debugging flags.
           uint32 flags = 0;
           flags += b2Draw::e_shapeBit;
           flags += b2Draw::e_jointBit;
           flags += b2Draw::e_aabbBit;
           flags += b2Draw::e_pairBit;
           flags += b2Draw::e_centerOfMassBit;
           m_debugDraw->SetFlags(flags);  

 

Here, we have initialised our debugger and set it for world debugging. We have also turn all the flags on for different debugging flavour. You can experiment with them and see what happens.
That’s all, execute your project and you shall see following:

Sometimes it is important to have your body image view enabled as well during debugging. So for that comment following two lines in draw method

//glEnableClientState(GL_VERTEX_ARRAY);
//glDisableClientState(GL_VERTEX_ARRAY);

 

your view will look like this

That’s about it enjoy coding

9 thoughts on “Cocos2d-x: Enable Box2D Debugger

  1. The code listed above will only work for OpenGL ES 1.x but not for 2.x. This also means that it won't work with Cocos2d-x 2.x but only with 1.x since Cocos2d-x 2.x uses OpenGL ES 2.x. OpenGL ES 2.0 doesn't have a fixed-function pipeline anymore. There are no buitlin vertex attributes anymore (e.g. glVertex, glColor, etc.) and their corresponding array functions (e.g. glVertexPointer, glColorPointer, etc.) along with glEnableClientState/glDisableClientState have been removed too. In OpenGL ES 2.x we now have generic vertex attribute functions (i.e. glVertexAttrib, glVertexAttribPointer and glEnableVertexAttribArray/glDisableVertexAttribArray) together with a corresponding vertex shader to give these attributes their correct meaning.

Leave a Reply