Oh!Coder

Coding Life

Box2D C++ 教程-设置世界

| Comments

声明:本教程翻译自: Box2D C++ tutorials-World settings ,仅供学习参考。

  • 世界(Worlds)

世界(Worlds)作为Box2D世界里主要的实体,在之前的讨论话题中已简单的介绍过。当你创建或者删除物体的时候,可以调用世界里的方法来完成这些功能,所以世界也管理着所有对象的空间分配。这也就意味着世界非常重要,那么就让我们来看看世界到底可以做些什么吧。

-定义重力加速度
-调用物理模拟
-发现定制器的作用域
-切断射线并找到相交的定制器

最后两个特性会在稍后的话题里展开讲解,目前我们只看第一个特性以及世界的生命周期。testbed框架为我们完成了这些,而且作为m_world类成员变量我们也已经使用过了,所以相比于创建一个测试场景来说,我们只对它做一个快速查看。

世界(world)的创建像其它普通类型一样,在构造函数里进行基本的设置。

1
2
3
4
b2Vec2 gravity(0, -9.8); //normal earth gravity, 9.8 m/s/s straight down!
bool doSleep = true;

b2World* myWorld = new b2World(gravity, doSleep);

重力加速度的数值大小会影响世界里的每个动态物体,但是你可以以后通过SetGravity()方法调整重力加速度。比如说,试着添加一个之前我们所创建的场景,并将重力加速度改为0,看看会发生什么:

1
myWorld->SetGravity( b2Vec2(0,0) );

睡眠参数设定了当物体不受外界作用的时候是否允许睡眠(‘sleep’),此举可以提高程序运行的效率。如果这个参数为真(true),那么物体当不受作用时候会进入睡眠状态,,直到有外界作用而被叫醒(‘wake’),之后会重新对其进行模拟。例如来自其它物体的碰撞,产生外界作用力作用于物体上,等等。

注意:Box2D v2.2.1版本开始,睡眠参数被移除,并默认为真(true)。如果想改变参数状态需要调用b2World::SetAllowSleeping(bool)方法。

一旦像上面那样创建了一个世界,就可以像我们之前做的那样往世界里添加物体。为了让好玩的事情发生,我们需要不停的调用Step()方法来模拟物理世界的运动。当然了这部分testbed框架做了相应的处理,且作为Test类的一部已经完成了。

1
2
3
4
5
float32 timeStep = 1/20.0;  //the length of time passed to simulate (seconds)
int32 velocityIterations = 8;  //how strongly to correct velocity
int32 positionIterations = 3; //how strongly to correct position

myWorld->Step( timeStep, velocityIterations, position iterations);

在这个例子中,1/20秒调用一次Step方法,所以在场景中的物体如果每秒移动5米,那么每调用Setp一次物体就移动5/20=0.25米。其中timeStep参数会影响到世界中所有受到重力的物体。你可能发现通过调整时间步长,能够对物体的加速度产生影响。为了让模拟看起来更逼真,在游戏中通常会将timeStep的值设置成每次调用Step()方法的频率值。比如说testbed中,默认的帧率为每秒60帧,所以Step()方法也会在一秒内调用60次,并且把timeStep设置成1/60秒。

当物体之间发生碰撞时,速度迭代和位置迭代的设置将会对其产生影响。通常在Box2D中,当两个物体进行碰撞检测时,物体之间会发生重叠(互相进入),所以需要做一些计算来得出哪个物体应该移动或者旋转,来使它们不再发生重叠。让这两个数值越大,模拟的精度也就越高,当然了性能消耗也就会越大。

(更新于2013/1/20):应Nikolai在评论中的要求,根据我所了解的知识,讨论一下迭代变量实际都做了些什么。

首先,这些变量仅仅用来解析碰撞冲突,如果当前没有发生碰撞的话这些变量根本就不会被调用。当两个物体发生了碰撞,为了解析碰撞(推开两个物体并使它们不再重叠),需要改变它们的位置和速度。很显然需要改变物体的位置-为了修正重叠关系。速度也应该被改变,例如小球撞到墙之后能够产生正确的反弹,或者撞到物体的非中心点导致小球产生旋转。

具体来说就是物体会移到哪个位置,碰撞后的速度是什么样的,角速度是否会受到影响等等,这些都由迭代求解器处理。这意味着第一次求解不会产生一个完美的结果,但是每迭代一次精度就会更接近一点。或许使用Newton-Raphson方法求解方程根的迭代求解例子看起来更贴切一点。这里有一个gif动画很好的展示了这一过程(如果你没看太明白这个’寻根’过程,其实你只要知道整个过程都在渐近的找出蓝线和x轴的交点就行了)。

正如你在gif动画中看到的那样,相同的计算被一遍又一遍的重复计算,每一次的迭代计算都会让计算结果更精确一点。通常情况下当计算结果变动很小的时候就应该停止计算了,因为此时再多的迭代计算也不会对计算结果的精度有大幅度的提高。如果计算时间有限,你应该通过设定一个迭代次数来终止迭代计算,即便此时的结果并不是一个完全精确的数值。

通常使用Box2D是用来快捷的搭建非精确模拟的,每秒60帧的速度几乎看不出什么瑕疵。所以通常我们需要设置迭代上限,以此保证CPU时间。

为了可以更好的针对你的情况设定一个合适的数值,可以先设定一个小一点的数值,对于稀疏模拟(sparse simulations)来说即使设置为1也可以。如果有快速碰撞,或者许多物体在同一时间发生碰撞(特别是成堆成堆的物体),此时需要调整数值的大小来避免出现物体之间互相渗透的情况,让物体之间的碰撞看起来更糟糕。当数值到达某个点,你会发现不管数值如何增加不再有任何的帮助。

最后需要注意一点的是,这些数值仅仅是一个上限数值。如果Box2D认为结果已经足够好了,它将会停止迭代计算,并不是非要执行到你指定的数值次数。如果你设定到50,并不意味着每次都会执行10倍于5的时长。这仅仅意味着在一种糟糕的情况下(例如一大堆方块儿)允许执行10倍时长。这意味着你的游戏运行的很流畅和平滑,直到出现一大堆物体,然后鬼使神差的开始变的缓慢和停顿,此时应该检查一下迭代数值看看是否是此处影响到了你的游戏性能。

  • 清除

当世界对象完成了所有工作的时候,就可以将其简单的删除:

1
delete myWorld;

当世界像这样被删除之后,它也会它所关联的所有连接器和物体都删除掉。记住!当进行了此操作之后就不要再使用删除的物体指针!

Comments