转载标明出处:http://write.blog.csdn.net/postedit/38567579
上篇回顾
本篇名言:成功不是全垒打,而要靠每天的、经常的打击出密集安打。[Robert J. Ringer]
上篇中,我们学习了游戏中背景音乐特效播放,接下去蛤蟆将学习物理引擎了。At last 终于我们还是到了这一步,哈哈,是不是期待很久了。时间不多,就让我们开始吧。
理论介绍
在启动Box2d物理引擎前,我们需要进行如下操作
进入Visual Studio,选定HelloWorld工程,右键选择属性。然后在弹出的菜单中,选择C/C++ -->预处理器 -->预处理器定义,将预处理器定义中的CC_ENABLE_CHIPMUNK_INTEGRATION=1改成CC_ENABLE_BOX2D_INTEGRATION=1
然后再编译即可启动Box2d引擎,使用CHIPMUNK引擎同理。。
下面进行一下Box2D引擎介绍,蛤蟆看了一些资料,然后将这些资料提取出来和小伙伴们分享:
Box2D是一个用于模拟2D刚体物体的C++引擎。而物理引擎是一个动画的系统,这样就不需要由动画师去移动你的物体。你可以让物理引擎来做导演。
此外,小伙伴们需要记住的是Box2D 是用C++ 来写成的。源码中定义的大部分类型都有b2前缀,注意区分。。
物理世界的概念(以下摘自《Box2Dv2.0.1 用户手册.pdf》):
--------------------------------------------此处是分界线---------------------------------------------------
刚体(rigid body)
一块十分坚硬的物质,它上面的任何两点之间的距离都是完全不变的。它们就像钻石那样坚硬。在后面的讨论中,我们用物体(body)来代替刚体。
形状(shape)
一块严格依附于物体(body)的 2D 碰撞几何结构(collisiongeometry)。形状具有摩擦(friction)和恢复(restitution)的材料性质。
约束(constraint)
一个约束(constraint)就是消除物体自由度的物理连接。在 2D 中,一个物体有 3 个自由度。如果我们把一个物体钉在墙上(像摆锤那样),那我们就把它约束到了墙上。这样,此物体就只能绕着这个钉子旋转,所以这个约束消除了它 2 个自由度。
接触约束(contact constraint)
一个防止刚体穿透,以及用于模拟摩擦(friction)和恢复(restitution)的特殊约束。你永远都不必创建一个接触约束,它们会自动被 Box2D 创建。
关节(joint)
它是一种用于把两个或多个物体固定到一起的约束。Box2D 支持的关节类型有:旋转,棱柱,距离等等。关节可以支持限制(limits)和马达(motors)。
关节限制(joint limit)
一个关节限制(joint limit)限定了一个关节的运动范围。例如人类的胳膊肘只能做某一范围角度的运动。
关节马达(joint motor)
一个关节马达能依照关节的自由度来驱动所连接的物体。例如,你可以使用一个马达来驱动一个肘的旋转。
世界(world)
一个物理世界就是物体,形状和约束相互作用的集合。Box2D 支持创建多个世界,但这通常是不必要的。
--------------------------------------------此处是分界线---------------------------------------------------
蛤蟆补充几个:
夹具(fixture)
夹具绑定一个形状Shape到一个Body,增加属性如密度,摩擦力和恢复特性。夹具将形状Shape带入到碰撞系统,这样Shape就能和其他形状进行碰撞了。
求解器(solver)
这个相当于物理世界仲裁者,它来推进时间,计算约束。
蛤蟆再来介绍下这个Box2D中的代码,我想介绍完代码其他的操作使用就都是浮云了。
先来看下主要的头文件Box2D.h文件,这些文件在\cocos2d-x-3.2\external\Box2D路径下,该头文件包含了所有和Box2D物理引擎相关的头文件。
此外主要是3个模块,分别是collision,common和Dynamics。
Collision顾名思义就是碰撞相关的代码所在地,common文件夹是主要是分配、数学函数和设置所在地,而dynamics文件夹是主要的类定义场所,包括物理世界,刚体,关节等。
class b2Fixture;
class b2Joint;
class b2Contact;
class b2Controller;
class b2World;
class b2Body等
结构体
struct b2FixtureDef;
struct b2JointEdge;
struct b2ContactEdge;
struct b2BodyDef
枚举类型
enum b2BodyType
等,小伙伴们看到了么,都是用b2前缀开始的。
OK,我们先简单学习一下,大概怎么使用,
创建函数如下:
b2Body* b2World::CreateBody(constb2BodyDef* def)
b2Joint* b2World::CreateJoint(constb2JointDef* def)
销毁函数如下:
void b2World::DestroyBody(b2Body* body)
void b2World::DestroyJoint(b2Joint* joint)
重要的一点是Body的使用需要一下几个步骤:
1. 定义Body的位置和阻尼
2. 使用世界对象(b2world)创建之
3. 使用形状,摩擦力和密度来定义fixture.
4. 在Body上创建fixtures.
依他类推,或者可以去Box2D官网看哦,不过蛤蟆觉得看完蛤蟆这些就差不多可以入个小门了哈,让我们开始吧。
具体步骤
我们还是在HelloWorld.cpp文件中找,首先加入头文件如下:
#include <Box2D/Box2D.h>
接着选中解决方案右键->添加->添加现有项目,选择当前工程目录下的
\cocos2d\external\Box2D\proj.win32\Box2D.vcxproj。
右键选中libBox2D 工程项目,然后点击生成libBox2D.lib库。
完了之后,
选中我们项目工程右键属性->配置属性->连接器->输入->附加依赖项中,我们输入libBox2D.lib库名字。完毕。
然后到bool HelloWorld::init()函数,加入如下代码,不要怕,接下去蛤蟆会逐行解释哈。
b2Vec2 gravity(0.0f, -10.0f);
b2World world(gravity);
b2BodyDef groundBodyDef;
groundBodyDef.position.Set(0.0f,-10.0f);
b2Body* groundBody = world.CreateBody(&groundBodyDef);
b2PolygonShape groundBox;
groundBox.SetAsBox(50.0f,10.0f);
groundBody->CreateFixture(&groundBox,0.0f);
b2BodyDef bodyDef;
bodyDef.type= b2_dynamicBody;
bodyDef.position.Set(0.0f,4.0f);
b2Body* body = world.CreateBody(&bodyDef);
b2PolygonShape dynamicBox;
dynamicBox.SetAsBox(1.0f,1.0f);
b2FixtureDef fixtureDef;
fixtureDef.shape= &dynamicBox;
fixtureDef.density= 1.0f;
fixtureDef.friction= 0.3f;
body->CreateFixture(&fixtureDef);
float32 timeStep = 1.0f / 60.0f;
int32 velocityIterations = 6;
int32 positionIterations = 2;
for (int32 i =0; i < 60; ++i)
{
world.Step(timeStep,velocityIterations, positionIterations);
b2Vec2 position = body->GetPosition();
float32 angle = body->GetAngle();
CCLog("%4.2f %4.2f %4.2f\n", position.x, position.y,angle);
}
编译运行得到输出如下,当然这个蛤蟆没有图形化输出哈,下一次一定有高逼格的图形化输出。
0.00 4.00 0.00
0.00 3.99 0.00
0.00 3.98 0.00
0.00 3.97 0.00
0.00 3.96 0.00
0.00 3.94 0.00
0.00 3.92 0.00
0.00 3.90 0.00
0.00 3.87 0.00
0.00 3.85 0.00
0.00 3.82 0.00
0.00 3.78 0.00
0.00 3.75 0.00
0.00 3.71 0.00
0.00 3.67 0.00
0.00 3.62 0.00
0.00 3.57 0.00
0.00 3.52 0.00
0.00 3.47 0.00
0.00 3.42 0.00
0.00 3.36 0.00
0.00 3.30 0.00
0.00 3.23 0.00
0.00 3.17 0.00
0.00 3.10 0.00
0.00 3.02 0.00
0.00 2.95 0.00
0.00 2.87 0.00
0.00 2.79 0.00
0.00 2.71 0.00
0.00 2.62 0.00
0.00 2.53 0.00
0.00 2.44 0.00
0.00 2.35 0.00
0.00 2.25 0.00
0.00 2.15 0.00
0.00 2.05 0.00
0.00 1.94 0.00
0.00 1.83 0.00
0.00 1.72 0.00
0.00 1.61 0.00
0.00 1.49 0.00
0.00 1.37 0.00
0.00 1.25 0.00
0.00 1.13 0.00
0.00 1.01 0.00
0.00 1.01 0.00
0.00 1.01 0.00
0.00 1.01 0.00
0.00 1.01 0.00
0.00 1.01 0.00
0.00 1.01 0.00
0.00 1.01 0.00
0.00 1.01 0.00
0.00 1.01 0.00
0.00 1.01 0.00
0.00 1.01 0.00
0.00 1.01 0.00
0.00 1.01 0.00
0.00 1.01 0.00
代码解释
下面我们来解释下代码的含义:
bool HelloWorld::init()函数中代码解释。
//定义一个重力向量。
b2Vec2 gravity(0.0f, -10.0f);
//创建一个物理世界。接着我们可以在这个物理世界中添加东西了。
b2World world(gravity);
//创建一个接地气的body使用,这个要满足前面我们描述的4个步骤哦。
//第一步:定义一个body,定义初始位置
b2BodyDef groundBodyDef;
groundBodyDef.position.Set(0.0f,-10.0f);
//第二步:传递body定义到世界对象,以此创建一个body.body是静态的,不会与其他静态body碰撞,也不会移动。
b2Body* groundBody = world.CreateBody(&groundBodyDef);
//第三步:创建一个底面多边形,宽度为100units,长度为20units.
b2PolygonShape groundBox;
groundBox.SetAsBox(50.0f,10.0f);
//第四步:创建一个夹具,我们可以直接将shape传递给body即可。第二个参数 0.0f表示密度。
记住,每个fixture必须有一个父亲body,即使fixtures 是静态的。当然,你可以把所有静态的fixtures添加到单个静态的body上。移动和修改body 上的fixture是不行的。
groundBody->CreateFixture(&groundBox,0.0f);
//创建一个动态的body
b2BodyDef bodyDef;//第一步:定义body的属性,位置。
bodyDef.type= b2_dynamicBody;//必须定义为b2_dynamicBody才能收到外力运动。
bodyDef.position.Set(0.0f,4.0f);
b2Body* body = world.CreateBody(&bodyDef);//第二步,创建body.
//第三步:创建一个多边形Shape。设置属性
//注意:动态的body必须有非零的密度,不然会非常的奇怪的。
b2PolygonShape dynamicBox;
dynamicBox.SetAsBox(1.0f,1.0f);
b2FixtureDef fixtureDef;
fixtureDef.shape= &dynamicBox;
fixtureDef.density= 1.0f;
fixtureDef.friction= 0.3f;
//第四步:在body上创建一个夹具fixture.
body->CreateFixture(&fixtureDef);
//现在可以进行仿真了
//需要设置一个时间步长,通常物理引擎的时间步长最慢1/60 秒。
float32 timeStep = 1.0f / 60.0f;
//之前我们介绍的概念中有个solver,在我们的约束solver中有两个
//一个是速度相一个是位置相。我打个比方,物理移动都就我们才让solver计算一次呢?
//这个就是需要设置的,一般速度相设置为8,位置相设置为3。主要是性能和质量的平衡。
//这两个相是没有任何关系的。
int32 velocityIterations = 6;
int32 positionIterations = 2;
//下面模拟一个1秒内的 60个时间步长(我们设置的时间步长是1/60f,所以1秒有60个时间点。
for (int32 i =0; i < 60; ++i)
{
world.Step(timeStep,velocityIterations, positionIterations);
b2Vec2 position = body->GetPosition();
float32 angle = body->GetAngle();
CCLog("%4.2f %4.2f %4.2f\n", position.x, position.y,angle);
}
OK,完毕,是不是有点头绪了,后续蛤蟆慢慢深入哈。
总结
又快深夜了,蛤蟆终于输出了第一篇物理引擎的笔记。洗洗睡去了,明天上班有个数据库问题需要去定位了。
下次是不是要发点数据库相关的知识?不过笔记都在公司,公司的东西是不能外带的包括上传,额。那还是再议吧。小伙伴们,Good night!