第02章 Hello Box2D

内容很多摘自
Aman JIANG(江超宇)翻译的Box2D v2.0.1 用户手册


第02章 Hello Box2D

Box2D的发布包中有个Hello World程序。程序创建了一个大大的地面盒(ground box)和一个小小的动态盒(dynamic box)。盒子的位置随着时间的变化而变化。代码没有涉及到图形界面,你只能在控制台中看到文字输出

这是个很好的例子, 展示了怎么使用Box2D。

2.1 创建世界(Creating a World)

每个Box2D程序开始时都会创建一个b2World对象。b2World是物理枢纽(physics hub), 用于管理内存、对象和模拟。根据自己的实际情况, 你可以在栈, 堆或数据区中创建出world。

创建Box2D的world很简单。首先, 我们要定义重力矢量,另外还要告诉world是否允许body在静止时休眠。休眠中的body不需要任何模拟。

b2Vec2 gravity(0.0f, -10.0f);

bool doSleep = true;

现在可以创建world对象了。注意,在这里我们是在栈中创建world, 所以world不能离开它的作用域。

b2World world(gravity, doSleep);

我们已经有了自己的物理世界, 开始向里面加东西了。

2.2 创建地面盒(Creating a Ground Box)

body用以下步骤来创建:

1.                   用位置(position), 阻尼(damping)等来定义body

2.                   通过world对象来创建body

3.                   用形状(shape), 摩擦(friction), 密度(density)等来定义fixture

4.                   在body上来创建fixture

第一步,创建ground body。我们需要一个body定义。在定义中,我们指定ground body的初始位置。

b2BodyDef groundBodyDef;

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

第二步, 将body定义传給world对象, 创建ground body。world对象并不保留body定义的引用。ground body是作为静态物体(static body)创建的。静态物体和其它静态物体之间并没有碰撞, 它们是固定的。当body的质量为零时, Box2D就认为它是静态的。物体质量的默认值就为零, 所以它们默认就是静态的。

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

第三步, 创建地面多边形。我们用简便函数SetAsBox使得地面多边形构成一个盒子形状,盒子的中心点就是父body的原点。

b2PolygonShape groundBox;

groundBox.SetAsBox(50.0f, 10.0f);

SetAsBox函数接收半个宽度和半个高度作为参数。因此在这种情况下,地面盒就是100个单位宽(x轴),20个单位高(y轴)。Box2D已被调谐到使用米,千克和秒做单位。你可以认为长度单位就是米。当物体的大小跟真实世界一样时,Box2D通常工作良好。例如,一个桶约1米高。由于浮点算法的局限性,使用Box2D模拟冰川或沙尘的运动并不是一个好主意。

第四步, 我们创建shape fixture, 以完成ground body。这步中,我们有个简便方法。我们并不需要修改fixture默认的材质属性, 可以直接将形状传给body而不需要创建fixture的定义。随后的教程中, 我们将会看到如何使用fixture定义来定制材质属性。

groundBody->CreateFixture(&groundBox);

Box2D并不保存shape的引用。它把数据复制到一个新的b2Shape对象中。

注意,每个fixture都必须有一个父body,即使fixture是静态的。然而,你可以把所有静态fixture都依附于单个静态body之上。之所以需要这个静态body, 是为了保证Box2D内部的代码更具一致性,以减少潜在的bug数量。

可能你已经注意到, 多数Box2D类型都有b2前缀。这是为了降低它和你的代码之间名字冲突的机会。

2.3 创建动态物体(Creating a Dynamic Body)

现在我们已经有了一个地面body,我们可以使用同样的方法来创建一个动态body。除尺寸之外的主要区别是, 我们必须为动态body设置质量属性。

首先我们用CreateBody创建body。默认情况下,body是静态的, 所以在构造时候应该设置b2BodyType使得body成为动态

b2BodyDef bodyDef;

bodyDef.type = b2_dynamicBody;

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

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

注意

如果你想body受力的影响而运动, 你必须将body的类型设为b2_dynamicBody。

然后,我们创建一个多边形shapde, 并将它附加到fixture定义上。我们先创建一个box shape:

b2PolygonShape dynamicBox;

dynamicBox.SetAsBox(1.0f, 1.0f);

接下来我们使用box创建一个fixture定义。注意, 我们把密度值设置为1,而密度值默认是0。并且,fixture的摩擦系数设置为0.3。

b2FixtureDef fixtureDef;

fixtureDef.shape = &dynamicBox;

fixtureDef.density = 1.0f;

fixtureDef.friction = 0.3f;

使用fixture定义, 我们现在就可以创建fixture。这会自动更新body的质量。要是你喜欢, 你可以为body添加许多不同的fixture。每个fixture都会增加物体的总质量。

body->CreateFixture(&fixtureDef);

这就是初始化过程。现在我们已经做好准备,可以开始模拟了。

2.4 模拟(Box2D的)世界

我们已经初始化好了地面box和一个动态box。该让牛顿来接手了。我们只有少数几个问题需要考虑。

Box2D使用了一个叫积分器(integrator)的数值算法。 积分器在离散的时间点上模拟连续的物理方程。 它与传统的游戏动画循环一同运行。我们需要为Box2D选取一个时间步。通常来说用于游戏的物理引擎需要至少 60Hz 的速度,也就是 1/60 的时间步。你可以使用更大的时间步,但是你必须更加小心地为你的世界调整定义。我们也不喜欢时间步变化得太大,所以不要把时间步关联到帧频(除非你真的必须这样做)。直截了当地,这个就是时间步:

float32 timeStep = 1.0f / 60.0f;

除积分器外,Box2D代码还使用了约束求解器(constraint solver)。约束求解器用于解决模拟中的所有约束,一次一个。单个的约束会被完美的求解,然而当我们求解一个约束的时候,我们就会稍微耽误另一个。要得到良好的解,我们需要多次迭代所有约束。

约束求解有两个阶段:速度、位置。在速度阶段,求解器会计算必要的冲量,使得物体正确运动。而在位置阶段,求解器会调整物体的位置,减少物体之间的重叠。每个阶段都有自己的迭代计数。此外,如果误差已足够小的话,位置阶段的迭代可能会提前退出。

对于速度和位置,建议的Box2D迭代次数都是10次。你可以按自己的喜好去调整这个数字,但要记得它是性能与精度之间的折中。更少的迭代会增加性能但降低精度,同样地,更多的迭代会降低性能但能提高模拟质量。对于这个简单示例,我们不需要多次迭代。这是我们选择的迭代次数。

int32 velocityIterations = 6;

int32 positionIterations = 2;

时间步和迭代数是完全无关的。一个迭代并不是一个子步。一次迭代就是在时间步之中的单次遍历所有约束,你可以在单个时间步内多次遍历约束。

现在我们可以开始模拟循环了, 在你的游戏中, 模拟循环和游戏循环可以合并起来。每次游戏循环你都应该调用b2World::Step, 通常调用一次就够了, 这取决于帧频以及物理时间步。步进后,你应当调用b2World::ClearForces清除你施加到body上的任何力。

这个Hello World程序设计得非常简单, 它没有图形输出。代码会打印出动态body的位置以及旋转角, 有文字输出总比完全没有输出好。这就是模拟 1 秒钟内 60 个时间步的循环:

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

{

    world.Step(timeStep,velocityIterations, positionIterations);

    world.ClearForces();

    b2Vec2 position =body->GetPosition();

    float32 angle =body->GetAngle();

    printf("%4.2f %4.2f%4.2f\n", position.x, position.y, angle);

}

输出展示了动态box降落到地面的情况。你的输出看起来应当是这样:

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 清理

当world对象超出它的作用域,或通过指针将其 delete 时, 分配给body, fixture, joint使用的内存都会被释放。这能使你的生活变得更简单。然而,你应该将body, fixture或joint的指针都清零,因为它们已经无效了。

2.6 Testbed例子

一旦你征服了 HelloWorld 例子,你应该开始看 Box2D 的 testbed 了。testbed 是个单元测试框架,也是个演示环境, 这是它的一些特点:

•                    可移动和缩放的摄像机

•                    可用鼠标选中依附在动态物体上的形状

•                    可扩展的测试集

•                    通过图形界面选择测试,调整参数,以及设置调试绘图

•                    暂停和单步模拟

•                    文字渲染


在 testbed 中有许多 Box2D 的测试用例,以及框架本身的实例。我鼓励你通过研究和修改它来学习 Box2D。

注意:testbed 是使用 freeglut 和 GLUI 写成的,testbed 本身并不是 Box2D 库的一部分。Box2D本身不知道如何渲染,就像 HelloWorld 例子一样,使用 Box2D 并不一定需要渲染。


转载自 http://blog.csdn.net/complex_ok/article/details/6709772

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值