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.
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.
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:
Add constructor, destructor and tic method declaration under public domain. Add following code under private domain:
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.
Add definition for tick method.
Tick method will simulate your physics world. Now add definitions for constructor and destructor.
We have to de-allocate whatever we have allocated. Now add definition for draw method.
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.
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.
Here, we have initialized our debugger and set it for world debugging. We have also turn all the flags on for different debugging flavor. 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.
your view will look like this
That's about it enjoy coding
Download Now!
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 initialized our debugger and set it for world debugging. We have also turn all the flags on for different debugging flavor. 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
Thanks for sharing..
ReplyDeleteWhen i add it in my xcode..it gives me 10 errors..:(
DeleteThe above code is meant for windows development, So, If you wanted to use it for Xcode you need to convert the C++ syntax to objective-C.
DeleteThe 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.
ReplyDeleteThanks for sharing .
ReplyDeletethanks , very useful tutorial
ReplyDeleteI am very much pleased with the contents you have mentioned. I wanted to thank you for this great article. I enjoyed every little bit part of it and I will be waiting for the new updates.
ReplyDeletecocos2d-x game development
Thanks for the appreciation
DeleteThanks for sharing the information.
ReplyDeleteMobile Application Developer USA
I enjoyed reading your post.You make so many great points here that I read your article a couple of times.
ReplyDeleteLuxury Park 2
Luxury Park 2 Mamidipally
Luxury Park 2 Hyderabad
I found your post to be incredibly informative and well-researched. pilot prescription sunglasses
ReplyDeleteI found your post to be incredibly informative and well-researched. rimless eyeglasses
ReplyDelete