~~~~我的生活,我的点点滴滴!!
这只是一个学习笔记,所以看到雷同莫惊讶!
在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__
3.x版本后,渲染机制变了,所以必须像上面这样写。
1、创建世界
//定义重力
b2Vec2 gravity(0,-9.8f);
//产生世界
m_world = new b2World(gravity);
//允许物体休眠
m_world->SetAllowSleeping(true);
m_world->SetContinuousPhysics(true);
//开启Debug Draw模式
setDebug(true);
一个正常的物理世界就此产生了。
2、生成地面及墙壁
-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);
生成天空、墙、地面完成。
3、产生精灵
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);
}
生成墙与生成精灵盒子展现了两种方式流程来创建物体,正常的流程:
-b2BodyDef 物体属性定义
-b2PolygonShape 定制器的形状定义
-b2FixtureDef 定制器的定义
-最后全绑定到物体body上
4、更新位置
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 );
-速度迭代建议8-10次,超过10次影响效率并且看不出来效果。
-位置迭代建议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绘制,简单看下效果图: