Box2D v2.3.0 用户指南(第二章)

翻译 2017年01月03日 21:44:46


第二章 HelloBox2D

Box2D中包含一个HelloWorld项目。程序创建了一个大的地面盒子(ground box)和一个小的动态盒子。工程代码中没有包含任何图形,你仅能在文字输出控制台中看到随时间更新的盒子位置。

从如何搭建并运行一个简单地Box2D模拟程序的角度来讲,这是一个很好的例子。

 

2.1创建一个世界

每个Box2D程序都从创建一个b2World对象开始,b2World是一个管理内存、物体和模拟的中心,你可以在堆栈中或者数据区域中为物理世界对象分配空间。

创建一个Box2D世界对象很容易,首先我们定义一个重力向量:

b2Vec2gravity(0.0f, -10.0f)

接着我们创建世界对象。注意我们这里是在栈中创建世界对象,因此我们需要在程序中保持该对象。

b2Worldworld(gravity);

这样我们就创建好了我们的物理世界,我们继续向其中添加一些其他的东西。

 

2.2创建一个地面盒子

物体通过下面的步骤创建完成:

1.    创建一个具有位置和阻尼(damping)(position)等属性的物体定义(BodyDef

2.    利用世界对象创建物体

3.    使用形状、摩擦力、密度等属性定义装置定义(FixtureDef

4.    在物体上创建装置

第一步我们创建地面盒子,需要一个物体定义来指定盒子的位置。

b2BodyDefgroundBodyDef;

groundBodyDef.position.Set(0.0f,-10.0f);

第二步我们将物体定义作为参数传递给世界对象来创建地面盒子。世界对象并不保存物体定义的引用。默认创建出来的物体都是静态的(static),静态物体不能移动,也不会与其他静态物体发生碰撞。

b2Body*groundBody = world.CreateBody(&groundBodyDef);

第三步我们创建一个地面多边形作为地面盒子的形状,我们使用SetAsBox方法来创建一个矩形,将盒子居中对齐到父物体的原点。

b2PolygonShapegroundBox;

groundBox.SetAsBox(50.0f,10.0f);

SetAsBox方法参数中的长宽属性只是实际长宽的一半,所以在本例中地面盒子的宽实际为100x轴),长为20y轴)。Box2D中的基本单位为米,千克和秒,所以我们可以认为这个例子中多边形的尺寸是以米作单位的。Box2D在处理大小和真实世界的物体相近的东西时,模拟效果最好,例如,在Box2D中一个桶应该大约1米高。相反的,由于浮点数计算的限制,用Box2D来模拟冰川或者模拟灰尘的粒子效果可不是一个好主意。

我们接着做完第四步,创建装置,来完成地面物体的创建。对于这一步,我们有一个小的捷径,由于我们不需要去修改默认的装置属性,所以我们直接将形状作为第一个参数传给物体对象,而不需要去创建一个装置定义(稍后我们会看到如何通过装置定义来定义物体的基本属性),传入的第二个参数是密度,单位是千克/平方米(这个地方应该因为是二维的物体,所以单位是平方米而不是立方米),静态物体默认没有质量,所以密度在这里没有作用。

groundBody->CreateFixture(&groundBox,0.0f);

Box2D不保留形状的引用,而是将形状对象的一个拷贝作为参数传入。

注意每一个装置都有一个父物体,即使装置是静态的。但是,你可以将所有的静态装置附加(attach)到一个相同的静态物体上。

当你将一个形状通过装置附加到物体上时,形状的坐标系就变为物体的本地坐标系了,所以当物体移动时,形状也随之移动。装置的世界变换继承自它的父物体,没有任何装置能够脱离物体进行变换,所以我们不去在物体上调整形状的位置,另一方面,移动和修改物体上的形状也是不支持的,理由很简单:一个形状可变的物体不是刚体,而Box2D是一款基于刚体的物理引擎,Box2D中的很多假设都是基于刚体模型的,所以如果物体不是刚体,很多事情就不对了。

 

2.3创建一个动态物体

现在我们有了一个地面物体,我们通过同样的方法来创建一个动态物体,唯一的不同时(除了尺寸之外),我们需要指定动态物体的质量属性。

首先我们通过CreateBody来创建物体,默认创建出来的是静态物体,所以在构造时我们需要将b2BodyType属性设置为动态的。

b2BodyDefbodyDef;

bodyDef.type= b2_dynamicBody;

bodyDef.position.Set(0.0f,4.0f);

b2Body*body = world.CreateBody(&bodyDef);

接着我们用装置定义来创建并绑定一个形状。首先我们创建一个盒子形状:

b2PolygonShapedynamicBox;

dynamicBox.SetAsBox(1.0f,1.0f);

接着我们用这个创建好的形状来创建一个装置定义,注意我们设置定义的密度属性为1,默认的密度属性为0,此外我们将形状的摩擦力属性设置为0.3

b2FixureDeffixtureDef;

fixtureDef.shape= &dynamicBox;

fixtureDef.density= 1.0f;

fixtureDef.friction= 0.3f;

现在我们可以使用装置定义来定义一个物体了,物体的质量属性会被自动更新,你可以为物体添加多个装置,每个装置添加完成后,物体的质量都会随之增加。

body-CreateFixture(&fixtureDef);

这样我们就完成了初始化,我们下面开始进行模拟。

 

2.4模拟世界(Box2D

我们完成了地面盒子和动态盒子的初始化操作,下面该让牛顿出场了。在此之前,我们还需要考虑一些事情。

Box2D使用一套名为集成器(integrator)的算法在离散的时间点上来模拟物理方程,这和我们传统游戏中使用的循环方法相同(例如我们通过循环帧来在屏幕上表现一本翻动的书)。因此我们需要为Box2D设置一个时间间隔(time step),大多数游戏引擎都希望时间间隔至少为1/60秒(频率为60Hz)。你也可以选择更大的时间间隔,但是你要在初始化世界的时候更加小心。我们不希望对时间间隔做过多的修改,变化的时间间隔导致变化的结果,调试的难度也随之增加,因此不在万不得已的情况下,不要将时间间隔和你的帧频率混为一谈。话不多说,下面我们定义好时间间隔。

float32timeStep = 1.0f/60.0f;

除了集成器外,Box2D还用一大段代码定义了约束解析器(constraint solver),用来在模拟的过程中逐个解析所有的约束。一个单独的约束能够被完美的解析,然而,当我们解析某个约束时,其他约束会被暂时中断,因此,如果需要得到一个好的解析结果,我们需要多次遍历所有的约束。

约束解析器的工作流程分为两个阶段:一个速度阶段,一个位置阶段。在速度阶段解析器计算所有能够使物体正确运动的冲量,在位置阶段,解析器调整物体的位置,以避免重叠,防止关节脱落。每个阶段有自己的迭代次数,在仅需微调的情况下位置阶段有可能提前结束。

Box2D中建议设置速度阶段的迭代次数为8次,位置阶段为3次,你可以自己按照需要做修改,但是请在计算的精确度和程序的运行效率上做好平衡。使用更少的迭代次数能够获得更高的执行效率,但是精确度要差一些,反之,更多的迭代次数带来更高的精度,但是却损失了效率。对于我们这个简单的例子来说,我们不需要太多的迭代次数,我们按照下面的代码来设定。

int32velocityIterations = 6;

int32positionIterations = 2;

注意到时间间隔和迭代次数完全无关,一次迭代并不是在一个时间间隔内,解析器做一次迭代的时间一个时间间隔,一个时间周期内可以出现多次迭代。

我们接下来可以编写用于模拟的循环了,在你的游戏中,模拟的循环代码可以被放到你游戏的循环中。在游戏的每一次循环中,调用b2World::Step,一次循环调用一次就可以了,与你的帧频率和物理时间间隔有关。

“Hello World”小程序应该尽量简洁,所以我们不加入任何的图形输出,仅用代码打印出动态盒子的位置和旋转而已。下面就是用来模拟的循环代码,模拟了60个时间间隔(总计1秒钟)。

for(int32 i = 0; i < 60; ++i)

{

world.Step(timeStep,velocityIterations, positionIterations);

b2Vec2position = body->GetPosition();

float32angle = body->GetAngle();

printf(“%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 1.25  0.00

0.00 1.13  0.00

0.00 1.01  0.00

 

2.5清理工作

当一个世界对象不再被保持存在或者被delete语句清除掉时,所有为了物体、装置和关节保留的内存都会被释放掉,以提高性能。然而,你需要将所有的物体、装置和关节指针指向NULL,否则这些指针将指向被释放的区域。

 

2.6测试床(Testbed

当你完成HelloWorld这个例子后,你应该简单了解一下Box2D的测试床。测试床是一个单元测试架构和演示环境(demo environment)。下面是它的一些功能:

1.    可移动和缩放的摄影机

2.    使用鼠标选择动态物体的形状

3.    可扩展的测试集

4.    可通过界面选择测试,设置参数和调试绘图选项

5.    暂停和单步(step)模拟

6.    文字渲染


在测试床中有许多关于Box2D的测试用例和框架的例子。我们希望你能够通过研究学习和实际操作来更好的学习Box2D

注意:测试床是使用freeglutGLUI写的,测试床不是Box2D库的一部分,渲染对于Box2D来说是透明的,就像在HelloWorld例子中我们看到的一样,我们不需要任何的渲染器。

Box2DTestbed的代码请用SVN下载,checkout地址为:http://box2d.googlecode.com/svn/tags/v2.3.1



Box2D v2.3.0 用户指南(第八章)

Box2D v2.3.0 用户指南

Box2D v2.3.0 用户指南(第九章)

Box2D v2.3.0 用户指南

Box2D v2.3.0 用户指南(第七章)

Box2D v2.3.0 用户指南

Box2D v2.3.0 用户指南(第十一章)

Box2D v2.3.0 用户指南

Box2D v2.3.0 用户指南(第十章)

Box2D v2.3.0 用户指南

box2d 2.3.0

  • 2014年11月20日 09:41
  • 2.22MB
  • 下载

Box2D v2.1.0用户手册(10)——世界(World Class)

第10章 世界(World Class) 关于 b2World类包含物体和关节。它管理着模拟的方方面面,并允许异步查询(就像AABB查询和光线投射)。 你与Box2D的大部分交...

Box2D v2.1.0用户手册(6)——夹具(Fixtures)

第06章 夹具(Fixtures) 6.1 关于 回想一下,形状并不知道物体的存在,可以独立使用。因此Box2D需要提供b2Fixture类,用于将形状附加到物体上。 fi...

Box2D v2.1.0用户手册翻译 - 第12, 13, 14章

内容很多摘自 Aman JIANG(江超宇)翻译的Box2D v2.0.1 用户手册 第12章 调试绘图(Debug Drawing) 实现 b2De...

Box2D v2.1.0用户手册翻译 - 第11章 杂项(Loose Ends)

第11章 杂项(Loose Ends) 11.1 隐式摧毁 Box2D没有使用引用计数。你摧毁了body后,它就确实不存在了。访问指向已摧毁body的指针,会导致未定义...
内容举报
返回顶部
收藏助手
不良信息举报
您举报文章:Box2D v2.3.0 用户指南(第二章)
举报原因:
原因补充:

(最多只允许输入30个字)