本人系原创,欢迎转载,转载时请附上原创地址链接.
从今天起开始陆续更新box2d物理引擎深入研究博文,主要以cocos2d-x上box2d的实例为研究对象.因为其示例已经基本上涵盖了box2d的所有应用.
本文要讲的示例是Dump Shell.本文中的代码都经我自己修改所得。去掉了示例中容易给人带来理解不方便的地方。然后也梳理了一下这个示例的详细流程。Let's start!!!
在讲这个示例前先做一些准备工作,因为一些概念和结构体需要说明这样的话后面的流程看起来就不会那么的困难。
世界(b2World)和刚体(rigit body)这两个最重要的概念就不多了。详细请查阅box2d官方用户手册
概念:关节(joint).和人类身体中的关节作用是一样。用于连接两个刚体,刚体和刚体之间可以围绕关节(joing)做一些旋转等动作.
今天涉及到两种关节:鼠标关节(b2MouseJoint) 和 移动关节(b2PrismaticJoint);鼠标关节是最常用的关节,主要用于用户通过鼠标或者触摸屏来和操作刚体.比如拖动刚体之类.一个移动关节允许两个刚体沿着指定的轴进行相向运动。对于鼠标关节和移动关节的介绍请查阅box2d官方用户手册:manual.pdf下面开始讲解这个实例的流程。
第一步:刚体背景是一个layer,所以要拖动刚体围绕节点做运动需要layer接受鼠标点击事件.
点击(此处)折叠或打开
CCDirector::sharedDirector()->getTouchDispatcher()->addTargetedDelegate(this,0,true)
第二步:为我们工作做一些初始化工作.
头文件中:
点击(此处)折叠或打开
b2Body *m_groundBody;
//鼠标关节
b2MouseJoint *m_mouseJoint;
b2World *m_world;
//绘制刚体和关节的对象
GLESDebugDraw *m_DebugDraw ;
//世界中的刚体(多个刚体)
b2Body** bodies;
//移动关节
b2Joint** joints;
源文件中:
点击(此处)折叠或打开
void HelloWorld::ReadyForDrawBody()
{
//设置重力
b2Vec2 gravity;
gravity.Set(0.0f, -10.0f);
m_world = new b2World(gravity);
//设置是否sleep
m_world->SetAllowSleeping(true);
m_world->SetContinuousPhysics(true);
//创建GLESDebugDraw对象.在DEBUG调试环境下对刚体轮廓和关节等信息
//进行绘制。它是在b2World::Step中被调用.
m_DebugDraw = new GLESDebugDraw(32.0f);
m_world->SetDebugDraw(m_DebugDraw);
uint32 flags = 0;
//e_shapeBit:刚体的轮廓被绘制出来
flags += b2Draw::e_shapeBit;
m_DebugDraw->SetFlags(flags);
b2BodyDef bodyDef;
m_groundBody = m_world->CreateBody(&bodyDef);
//为即将创建的3个刚体对象 和 1个关节对象分配内存
bodies = (b2Body**)b2Alloc(3 * sizeof(b2Body*));
joints = (b2Joint**)b2Alloc(1 * sizeof(b2Joint*));
m_mouseJoint =NULL;
}
到这里世界对象应该有了,刚体对象的内存 和 关节对象的内存已经分配好了,并且做好了把刚体的轮廓绘制到屏幕上的各种准备.
第三步:创建刚体.
点击(此处)折叠或打开
void HelloWorld::CreateBodys()
{
/*******************************************************************************/
//第一个刚体
{
b2BodyDef bd;
bd.type = b2BodyType(2);
bd.position.Set(1.304347801208496e+01f, 2.500000000000000e+00f);
bodies[0] = m_world->CreateBody(&bd);
{
b2FixtureDef fd;
//摩擦力
fd.friction = 1.000000000000000e+00f;
//弹性
fd.restitution = 15.000000000000000e-01f;
//密度
fd.density = 1.000000000000000e+01f;
//是否是传感器.True:刚体碰撞后不模拟真实的物理运动 ,只做简单的碰撞检测.False:刚体碰撞后由world对象进行真实的物理模拟 .
fd.isSensor = bool(0);
//指自己所属的碰撞种类
fd.filter.categoryBits = uint16(2);
//指与其碰撞的种类
fd.filter.maskBits = uint16(10);
//当其为负值时,表示该组的对象不发生碰撞,为正值则表示该组的对象发生碰撞;另外,要使两个对象不发生碰撞,则: groupIndex 属性必须为负
fd.filter.groupIndex = int16(0);
b2PolygonShape shape;
//刚体的形状为PolygonShape,由下面四个顶点构成.
b2Vec2 vs[8];
vs[0].Set(-6.900000095367432e+00f, -3.000000119209290e-01f);
vs[1].Set(2.000000029802322e-01f, -3.000000119209290e-01f);
vs[2].Set(2.000000029802322e-01f, 2.000000029802322e-01f);
vs[3].Set(-6.900000095367432e+00f, 2.000000029802322e-01f);
shape.Set(vs, 4);
fd.shape = &shape;
////给第一个刚体赋予形状 摩擦力等物理特性.
bodies[0]->CreateFixture(&fd);
}
}
/*******************************************************************************/
/*********************************************************************************/
//第二个刚体.
{
b2BodyDef bd;
bd.type = b2BodyType(2);
bd.position.Set(8.478260636329651e-01f, 2.500000000000000e+00f);
bodies[1] = m_world->CreateBody(&bd);
{
b2FixtureDef fd;
fd.friction = 1.000000000000000e+00f;
fd.restitution = 5.000000000000000e-01f;
fd.density = 1.000000000000000e+01f;
fd.isSensor = bool(0);
fd.filter.categoryBits = uint16(4);
fd.filter.maskBits = uint16(10);
fd.filter.groupIndex = int16(0);
b2PolygonShape shape;
b2Vec2 vs[8];
vs[0].Set(-3.228000104427338e-01f, -2.957000136375427e-01f);
vs[1].Set(6.885900020599365e+00f, -3.641000092029572e-01f);
vs[2].Set(6.907599925994873e+00f, 3.271999955177307e-01f);
vs[3].Set(-3.228000104427338e-01f, 2.825999855995178e-01f);
shape.Set(vs, 4);
fd.shape = &shape;
//给第二个刚体赋予形状 摩擦力等物理特性.
bodies[1]->CreateFixture(&fd);
}
}
/*******************************************************************************/
/*******************************************************************************/
//第三个刚体
{
b2BodyDef bd;
bd.type = b2BodyType(0);
bd.position.Set(0.000000000000000e+00f, 0.000000000000000e+00f);
bodies[2] = m_world->CreateBody(&bd);
//第三个刚体的第一条边.
{
b2FixtureDef fd;
fd.friction = 1.000000000000000e+01f;
fd.restitution = 0.000000000000000e+00f;
fd.density = 0.000000000000000e+00f;
fd.isSensor = bool(0);
fd.filter.categoryBits = uint16(6);
fd.filter.maskBits = uint16(6);
fd.filter.groupIndex = int16(0);
b2EdgeShape shape;
shape.m_radius = 9.999999776482582e-03f;
shape.m_vertex0.Set(0.000000000000000e+00f, 0.000000000000000e+00f);
//m_vertex1和m_vertex2两个顶点形成一条边
shape.m_vertex1.Set(1.452173995971680e+01f, 1.669565200805664e+01f);
shape.m_vertex2.Set(1.452173995971680e+01f, 0.000000000000000e+00f);
shape.m_vertex3.Set(0.000000000000000e+00f, 0.000000000000000e+00f);
shape.m_hasVertex0 = bool(0);
shape.m_hasVertex3 = bool(0);
fd.shape = &shape;
//第三个刚体的右边的一条边
bodies[2]->CreateFixture(&fd);
}
//第三个刚体的第二条边.
{
b2FixtureDef fd;
fd.friction = 1.000000000000000e+01f;
fd.restitution = 0.000000000000000e+00f;
fd.density = 0.000000000000000e+00f;
fd.isSensor = bool(0);
fd.filter.categoryBits = uint16(8);
fd.filter.maskBits = uint16(6);
fd.filter.groupIndex = int16(0);
b2EdgeShape shape;
shape.m_radius = 9.999999776482582e-03f;
shape.m_vertex0.Set(0.000000000000000e+00f, 0.000000000000000e+00f);
//m_vertex1和m_vertex2两个顶点形成一条边
shape.m_vertex1.Set(0.000000000000000e+00f, 1.669565200805664e+01f);
shape.m_vertex2.Set(0.000000000000000e+00f, 0.000000000000000e+00f);
shape.m_vertex3.Set(0.000000000000000e+00f, 0.000000000000000e+00f);
shape.m_hasVertex0 = bool(0);
shape.m_hasVertex3 = bool(0);
fd.shape = &shape;
//第三个刚体的左边的一条边
bodies[2]->CreateFixture(&fd);
}
//第三个刚体的第三条边.
{
b2FixtureDef fd;
fd.friction = 1.000000000000000e+01f;
fd.restitution = 0.000000000000000e+00f;
fd.density = 0.000000000000000e+00f;
fd.isSensor = bool(0);
fd.filter.categoryBits = uint16(10);
fd.filter.maskBits = uint16(6);
fd.filter.groupIndex = int16(0);
b2EdgeShape shape;
shape.m_radius = 9.999999776482582e-03f;
shape.m_vertex0.Set(0.000000000000000e+00f, 0.000000000000000e+00f);
//m_vertex1和m_vertex2两个顶点形成一条边
shape.m_vertex1.Set(0.000000000000000e+00f, 1.669565200805664e+01f);
shape.m_vertex2.Set(1.452173995971680e+01f, 1.669565200805664e+01f);
shape.m_vertex3.Set(0.000000000000000e+00f, 0.000000000000000e+00f);
shape.m_hasVertex0 = bool(0);
shape.m_hasVertex3 = bool(0);
fd.shape = &shape;
//第三个刚体的上面的一条边
bodies[2]->CreateFixture(&fd);
}
//第三个刚体的第四条边.
{
b2FixtureDef fd;
fd.friction = 1.000000000000000e+01f;
fd.restitution = 0.000000000000000e+00f;
fd.density = 0.000000000000000e+00f;
fd.isSensor = bool(0);
fd.filter.categoryBits = uint16(135);
fd.filter.maskBits = uint16(6);
fd.filter.groupIndex = int16(0);
b2EdgeShape shape;
shape.m_radius = 9.999999776482582e-03f;
shape.m_vertex0.Set(0.000000000000000e+00f, 0.000000000000000e+00f);
//m_vertex1和m_vertex2两个顶点形成一条边
shape.m_vertex1.Set(0.000000000000000e+00f, 0.000000000000000e+00f);
shape.m_vertex2.Set(2.452173995971680e+01f, 0.000000000000000e+00f);
shape.m_vertex3.Set(0.000000000000000e+00f, 0.000000000000000e+00f);
shape.m_hasVertex0 = bool(0);
shape.m_hasVertex3 = bool(0);
fd.shape = &shape;
//第三个刚体的下面的一条边
bodies[2]->CreateFixture(&fd);
}
}
}
第三步中创建了三个刚体.一个大的两个小的。最大的刚体包围住了两个小的刚体.你可能很反感代码中1.304347801208496e+01f的表示方法,确实很反感,哥哥我也很反感。就将就理解吧,因为BOX2D中都是以米为单位。上面的代码虽然多实际上都是些固定的步骤,给出一个刚体描述定义,然后根据这个定义结构体创建刚体,然后根据给出的夹具对象赋予刚体物理特性(形状,摩擦力,弹力等)。上面代码中最重要的就是夹具中的筛选器定义,他直接决定了自己这个刚体将与那些刚体发生碰撞,和哪些刚体不发生碰撞。筛选器属性:b2FixtureDef::filter::categoryBits ,b2FixtureDef::filter::maskBits,b2FixtureDef::filter::groupIndex这三个属性非常重要,对其非常详细的介绍请看这篇博文:http://blog.sina.com.cn/s/blog_6a2061a20100n0or.html.
到目前为止,屏幕中就能看到三个刚体出现了.但是不能用鼠标拖动。
第四步:创建鼠标关节:在鼠标点到了刚体上创建鼠标关节,按下鼠标不放并拖动的时候改变鼠标关节位置,鼠标弹起来的时候删除鼠标关节.
点击(此处)折叠或打开
bool HelloWorld::ccTouchBegan(CCTouch* touch, CCEvent* event)
{
if(m_mouseJoint !=NULL )
return false;
//判断鼠标是否点在了刚体上。
CCPoint mousePt = touch->getLocation();
b2Body *pBody = m_world->GetBodyList();
for(; pBody != NULL; pBody = pBody->GetNext())
{
b2Fixture *pFixture = pBody->GetFixtureList();
for(;pFixture != NULL;pFixture = pFixture->GetNext())
{
b2Vec2 mouseVec;
mouseVec.Set(mousePt.x/PTM_RATIO, mousePt.y/PTM_RATIO);
if(pFixture->TestPoint(mouseVec))
{
//创建鼠标关节.
b2MouseJointDef md;
md.bodyA=m_groundBody;//一般为世界边界
md.bodyB=pBody;//需要拖动的物体
md.target=mouseVec;//指定拖动的坐标
md.collideConnected=true; //
md.maxForce=1000.0f*pBody->GetMass(); //给一个拖动的力
m_mouseJoint=(b2MouseJoint*)m_world->CreateJoint(&md);//创建
pBody->SetAwake(true);//
return true;
}
}
}
return true;
}
void HelloWorld::ccTouchMoved(CCTouch* touch, CCEvent* event)
{
if(m_mouseJoint == NULL )
return;
b2Vec2 vecMouse;
vecMouse.Set((touch->getLocation().x)/PTM_RATIO, (touch->getLocation().y)/PTM_RATIO);
//改变关节位置.
m_mouseJoint->SetTarget(vecMouse);
}
void HelloWorld::ccTouchEnded(CCTouch* touch, CCEvent* event)
{
//销毁关节.
if(m_mouseJoint != NULL)
{
m_world->DestroyJoint(m_mouseJoint);
m_mouseJoint =NULL;
}
}
到目前为止,刚体已经绘制在窗口上,并且有了鼠标关节但是鼠标拖动刚体的时候却看不到反应.这是因为拖动后的效果没有被绘制出来。并且拖动刚体的时候刚体的位置要发生变化,我们还要让世界对象知道刚体的当前位置
第五步:
绘制拖动时的效果.
点击(此处)折叠或打开
void HelloWorld::draw()
{
CCLayer::draw();
ccGLEnableVertexAttribs( kCCVertexAttribFlag_Position );
kmGLPushMatrix();
m_world->DrawDebugData();
kmGLPopMatrix();
CHECK_GL_ERROR_DEBUG();
}
创建定时器让世界对象时刻知道刚体的位置.以便在把刚体绘制到窗口时提供位置信息.
点击(此处)折叠或打开
void HelloWorld::tick(float dt)
{
//更新世界中的刚体位置
m_world->Step(0.01f,8, 1);
uint32 flags = 0;
//把刚体轮廓画出来
flags +=b2Draw::e_shapeBit;
//把关节画出来,但是貌似关节画不出来
flags +=b2Draw::e_jointBit;
//绘制出刚体的BOX
flags += b2Draw::e_aabbBit;
flags += b2Draw::e_pairBit;
flags += b2Draw::e_centerOfMassBit;
m_DebugDraw->SetFlags(flags);
//绘制
m_world->DrawDebugData();
}
到现在鼠标拖动刚体的时候刚体就会动了,而且会有真实的物理模拟.
第六步:加移动关节(Prismatic Joint)把两个小的刚体连接起来 ,让他们在受到任何物理力的作用时始终保持相对运动。
点击(此处)折叠或打开
void HelloWorld::CreatePrismaticJoint()
{
//创建移动关节.
b2PrismaticJointDef jd;
//把两个小的关节连接起来
jd.bodyA = bodies[1];
jd.bodyB = bodies[0];
jd.collideConnected = bool(0);
//设置移动关节的锚点.
jd.localAnchorA.Set(0.000000000000000e+00f, 0.000000000000000e+00f);
jd.localAnchorB.Set(-1.219565200805664e+01f, 0.000000000000000e+00f);
//设置沿着哪一条轴做相对运动.
jd.localAxisA.Set(-1.219565200805664e+01f, 0.000000000000000e+00f);
jd.referenceAngle = 0.000000000000000e+00f;
jd.enableLimit = bool(1);
jd.lowerTranslation = -2.000000000000000e+01f;
jd.upperTranslation = 0.000000000000000e+00f;
jd.enableMotor = bool(1);
//设置马达.
jd.motorSpeed = 0.000000000000000e+00f;
jd.maxMotorForce = 1.000000000000000e+01f;
joints[0] = m_world->CreateJoint(&jd);
}
移动关节的用法很简单就是设置bodyA和bodyB就把两个刚体连接起来了.
源代码在附件中.PrismaticJoint.rar
阅读(1410) | 评论(0) | 转发(0) |