~~~~我的生活,我的点点滴滴!!
这只是一个学习笔记,所以看到雷同莫惊讶!
在cocos2dx-3.0中使用Box2D,我们建完工程需要把libBox2D工程引用进来,然后头文件只需要带上#include "Box2D/Box2D.h",但是
为了看Debug效果,我们需要把cocos2dx-3.0样例cpp-tests下的GLES-Render.h与GLES-Render.cpp文件拷贝到我们工程中添加进来,
准备工作做完了,我们先一起来看代码:
#ifndef __HELLOWORLD_SCENE_H__ #define __HELLOWORLD_SCENE_H__ #include "cocos2d.h" #include "Box2D/Box2D.h" #include "GLES-Render.h" #define PTM_RATIO 32 USING_NS_CC; class HelloWorld : public cocos2d::Layer { public: // there's no 'id' in cpp, so we recommend returning the class instance pointer static cocos2d::Scene* createScene(); // Here's a difference. Method 'init' in cocos2d-x returns bool, instead of returning 'id' in cocos2d-iphone virtual bool init(); // a selector callback void menuCloseCallback(cocos2d::Ref* pSender); // implement the "static create()" method manually CREATE_FUNC(HelloWorld); void initPhysics(); b2World *m_world; Size screenSize; Size visualSize; Sprite* role; GLESDebugDraw* mDebugDraw; void setDebug(bool isDebug); virtual void draw(Renderer *renderer, const kmMat4 &transform, bool transformUpdated) override; void update(float dt) override; protected: kmMat4 _modelViewMV; void onDraw(); CustomCommand _customCommand; private: void drawBox(); }; #endif // __HELLOWORLD_SCENE_H__
//定义重力 b2Vec2 gravity(0,-9.8f); //产生世界 m_world = new b2World(gravity); //允许物体休眠 m_world->SetAllowSleeping(true); m_world->SetContinuousPhysics(true); //开启Debug Draw模式 setDebug(true);
-Box2D生成的物体全是实心的,这里我们想生成一个四方形空心体,代表了墙和地面。
-Box2D采取现实世界的米-千克-秒(MKS)的单位制,能良好的处理0.1米-10米范围物体的模拟,我们定义32像素为1米作为转换,
所以的都要除以32(PTM_RATIO)。
-我们以左下角为锚点。
screenSize = Director::getInstance()->getVisibleSize(); b2BodyDef groundDef; b2Body *groundBody = m_world->CreateBody(&groundDef); b2PolygonShape groundBox; //开始设置四边了 //地面 groundBox.SetAsBox(screenSize.width / PTM_RATIO, 0.5, b2Vec2(1,1), 0); groundBody->CreateFixture(&groundBox, 0); //天空 groundBox.SetAsBox(screenSize.width / PTM_RATIO, 0.5, b2Vec2(1, screenSize.height / PTM_RATIO - 1), 0); groundBody->CreateFixture(&groundBox, 0); //左墙 groundBox.SetAsBox(0.5, screenSize.height / PTM_RATIO, b2Vec2(1,1), 0); groundBody->CreateFixture(&groundBox, 0); //右墙 groundBox.SetAsBox(0.5, screenSize.height / PTM_RATIO, b2Vec2(screenSize.width / PTM_RATIO - 1, 1), 0); groundBody->CreateFixture(&groundBox, 0);
b2BodyDef groundDef; b2PolygonShape groundBox; for(int i = 1 ;i <= 2 ; ++ i) { auto sprite = Sprite::create("ball.png"); sprite->setPosition(screenSize.width * 0.5, screenSize.height * 0.5); //动态物体 groundDef.type = b2_dynamicBody; //用来存放精灵对象,就是这个完成精灵与刚体的绑定。 groundDef.userData = sprite; groundDef.position.Set(screenSize.width * 0.5 / PTM_RATIO * (i), screenSize.height * 0.5 / PTM_RATIO); b2Body *testBody = m_world->CreateBody(&groundDef); groundBox.SetAsBox(1,1); b2FixtureDef fixtureDef; fixtureDef.shape = &groundBox; //物体的密度 fixtureDef.density = 1.0f; //物体的摩擦 fixtureDef.friction = 0.3f; testBody->CreateFixture(&fixtureDef); this->addChild(sprite); }
-b2PolygonShape 定制器的形状定义
-b2FixtureDef 定制器的定义
-最后全绑定到物体body上
Box2D是在每一个时间步长Step()函数中来刷新的,这和cocos2dx的update()类似,看下Step()定义:
/// Take a time step. This performs collision detection, integration,and constraint solution. /// @param timeStep the amount of time to simulate, this should not vary. ---步长调用时间 /// @param velocityIterations for the velocity constraint solver. ---速度迭代的次数 /// @param positionIterations for the position constraint solver. ---位置的迭代次数 void Step( float32 timeStep,int32 velocityIterations,int32 positionIterations );
-位置迭代建议1-8次。
看下update()函数里面的实现:
void HelloWorld::update(float dt) { int velocityIterations = 8; int positionIterations = 1; m_world->Step(dt, velocityIterations, positionIterations); //更新精灵与刚体的位置同步 for(b2Body *b = m_world->GetBodyList(); b; b=b->GetNext()) { if( b && b->GetUserData() ) { auto sprite = (Sprite*)b->GetUserData(); sprite->setPosition(Point(b->GetPosition().x * PTM_RATIO, b->GetPosition().y * PTM_RATIO)); } } }
基本上完成上面操作后,精灵与刚体就绑定在一起了,但是!!运行后你完全看不出他们是否绑定成功,因为我们看不到效果图,仔细观察你会发现,物体下落到一定位置后停下来,
那个地方是我们设置的地面的高度,所以我们需要把他的轮廓显示出来,还需要添加下面代码:
void HelloWorld::draw(Renderer *renderer, const kmMat4 &transform, bool transformUpdated) { // // IMPORTANT: // This is only for debug purposes // It is recommend to disable it // Layer::draw(renderer, transform, transformUpdated); kmGLPushMatrix(); kmGLGetMatrix(KM_GL_MODELVIEW, &_modelViewMV); _customCommand.init(_globalZOrder); _customCommand.func = CC_CALLBACK_0(HelloWorld::onDraw, this); renderer->addCommand(&_customCommand); kmGLPopMatrix(); } void HelloWorld::onDraw() { kmMat4 oldMV; kmGLGetMatrix(KM_GL_MODELVIEW, &oldMV); kmGLLoadMatrix(&_modelViewMV); m_world->DrawDebugData(); kmGLLoadMatrix(&oldMV); }
这是3.x以后改变的渲染机制,draw为父类的虚函数,需要重载,CustomCommand为渲染命令,新的渲染机制是把所有的渲染命令全放一块,最后一块渲染。
这个问题纠结了好半天,由于我想直接在cocos2dx使用Box2D而不是使用cocos2dx为我们封装好的接口,但是写完Box2D的代码后,始终看不出到底有没有
写成功,前面几篇文章都是依赖于Box2D中例子testbed提供好的模块,我只需要加Box2D部分的代码,现在突然在cocos2dx中使用,完全不知道怎么融入
其中,看了cocos2dx新的demo后,发现必须使用draw来调用opengl绘制,简单看下效果图: