b2AABB worldAABB; worldAABB.lowerBound.Set(-100.0f, -100.0f); worldAABB.upperBound.Set(100.0f, 100.0f);包围盒b2AABB是一个结构体,但是提供了成员方法(相当于类了),其定义在文件b2Collision.h中。它只有两个成员,lowerBound和upperBound,都是b2Vec2类型。
b2Vec2 gravity(0.0f, -10.0f); bool doSleep = true; b2World world(gravity, doSleep);
类似于上面世界对象不会保存对物体定义的引用,Box2D不会保存对shape的引用,它会将数据复制到一个新的b2Shape对象中。
下面是创建地面盒相关的代码:
// 2. create a ground body // a. body definition b2BodyDef groundBodyDef; groundBodyDef.position.Set(0.0f, -10.0f); // b. creaet body b2Body* groundBody = world.CreateBody(&groundBodyDef); // c. create shape b2PolygonShape groundBox; groundBox.SetAsBox(50.0f, 10.0f); // size of box is 100m*20m // d. create fixture groundBody->CreateFixture(&groundBox, 0.0f); // 0.0f is density, for static body, mass is zero, so density is not used.5. 创建动态物体
由于默认的body是静态的,我们可以在刚体定义的时候使用b2BodyType来设置其为动态的。同时,这里,我们创建一个fixture definition配件定义,用于设置其形状、密度、摩擦系数等,然后利用配件定义来创建fixture配件。(上面创建地面盒的时候我们是直接将形状用于创建配件,是类似的,这里也不是必须的,只是动态物体一般都需要定义密度摩擦力等。)下面是创建动态物体的方法:
// 3. create a dynamic body // a. body definiton && create body b2BodyDef bodyDef; bodyDef.type = b2_dynamicBody; // Set it as dynamic body as default is static. bodyDef.position.Set(0.0f, 4.0f); b2Body* body = world.CreateBody(&bodyDef); // b. create shape b2PolygonShape dynamicBox; dynamicBox.SetAsBox(1.0f, 1.0f); // c. fixture defition b2FixtureDef fixtureDef; fixtureDef.shape = &dynamicBox; fixtureDef.density = 1.0f; fixtureDef.friction = 0.3f; // d. create fixture body->CreateFixture(&fixtureDef);
参考后面的理解。
7. 清理工作
当一个世界对象超出它的作用域,或通过指针将其 delete 时,所有物体和关节的内存都会被释放。
完整的代码:
/* filename: 1.cpp compile: gcc -Iinclude -Llib -lBox2D -Wl,-rpath=`pwd`/lib 1.cpp */ #include <Box2D/Box2D.h> #include <stdio.h> int main() { // 1. create a world b2Vec2 gravity(0.0f, -10.0f); bool doSleep = true; b2World world(gravity, doSleep); // 2. create a ground body (static body) // a. body definition b2BodyDef groundBodyDef; groundBodyDef.position.Set(0.0f, -10.0f); // b. creaet body b2Body* groundBody = world.CreateBody(&groundBodyDef); // c. create shape b2PolygonShape groundBox; groundBox.SetAsBox(50.0f, 10.0f); // size of box is 100m*20m // d. create fixture groundBody->CreateFixture(&groundBox, 0.0f); // 0.0f is density, for static body, mass is zero, so density is not used. // 3. create a dynamic body // a. body definiton && create body b2BodyDef bodyDef; bodyDef.type = b2_dynamicBody; // Set it as dynamic body as default is static. bodyDef.position.Set(0.0f, 4.0f); b2Body* body = world.CreateBody(&bodyDef); // b. create shape b2PolygonShape dynamicBox; dynamicBox.SetAsBox(1.0f, 1.0f); // c. fixture defition b2FixtureDef fixtureDef; fixtureDef.shape = &dynamicBox; fixtureDef.density = 1.0f; fixtureDef.friction = 0.3f; // d. create fixture body->CreateFixture(&fixtureDef); // 4. simulate the world 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(); printf("%4.2f %4.2f %4.2f\n", position.x, position.y, angle); } // 5. clean up return 0; }
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 ......关于模拟Box2D的世界中step()的理解:
模拟Box2D的一个关键函数是:world.Step(timeStep, velocityIterations, positionIterations);
根据上面的例子的运行,很容易知道,这个step会改变动态对象body的位置(上面的例子只有y坐标改变了)。那么,这些数据如何理解?是如何计算出来的呢?要理解这个问题,就分析每一次调用step会影响些什么。
在分析前,首先说明,Step()的velocityIterations和positionIterations参数不会影响body的坐标改变,所以暂且先忽略这两个参数的作用,同时,body的初始坐标是(0.0f, 4.0f)。
PS:理解了很久还是没有理解到底是怎么计算的。。。纠结中。。。下面的内容根据个人理解慢慢更新。。。
1. timeStep理解为时间步或者刷新时间粒度,这个值并不是帧率。在Step中,进行了碰撞的检测和速度位置的更新等。而,velocityIterations(速度更新迭代数)和positionIterations(位置迭代更新数)是表示每一次时间步内,需要进行的计算次数,很显然,计算次数更多,那么更接近真实情况,自然也需要消耗更多的时间,所以迭代次数是性能和质量的一个平衡,不能太大也不能太小。但是,这两个参数并不会影响最终的step的结果(比如上面的,修改这两个参数,并不会影响body的坐标改变)。
2. 对于timeStep时间步的理解,有点像是Box2D的一个“逻辑时间”。每调用一次,表示时间“推进”一个单位。因为Box2D不能用物理时间来进行模拟,所以提供一个时间步表示时间推进,这样也使得Box2D的模拟不会因为真实时间的不同而不同。比如上面的例子,body只有重力,所以是自由落体运动,如果是物理时间,那么显然s=g*t*t/2,这样来更新时间,但是在一个游戏引擎中,t应该是由游戏来控制的,类似于说,游戏说时间前进一步就前进一步。所以Box2D的world提供了Step(),每调用一次Step()表示,当前的world的时间向前推进了一步,所以其是根据时间步来计算位置速度的改变,而不是根据真实的物理时间,所以不会因为不同的机器运行的速度不一样,物理时间不一样导致结果不一样,游戏逻辑的不一样。简单来说,就是Box2D有一个“时钟”,但是它是不会自己走的,每调用一次Step(),相当于我们把“时钟”向前拨动一个单位。个人理解。。。
3. 如果上面2的理解正确。。。那。。好像还是无法理解上面的例子中的数据是如何计算的,只能解释为何上面的例子每次运行结果是一样的以及在不同配置的机器都是一样的,因为它不依赖于物理时间。大概算了一下,发现不管如何修改timeStep,第一次调用Step()之后的结果,倒总是可以符合s=g*t*t的(不除以2),我想,既然这是一个逻辑时间,单位除以不除以2好像不重要,但是,到了第二次调用Step()的结果,好像不符合s=g*t*t,不知道这个时间步对应于相当于多少的物理时间啊。。。下面是timeStep=5.0f/60.0f和10.0f/60.0f的时候的结果:
timeStep=5.0f/60.0f
0.00 3.93 0.00 0.00 3.79 0.00 0.00 3.58 0.00 0.00 3.31 0.00 0.00 2.96 0.00 0.00 2.54 0.00 0.00 2.06 0.00 0.00 1.50 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 。。。。。。timeStep=10.0f/60.0f
0.00 3.72 0.00 0.00 3.17 0.00 0.00 2.33 0.00 0.00 1.22 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 。。。。。。说明:在调用Step()若干次之后,发现位置不再变化(y=1.01),这是由于,碰到了地面,不会再“下落”了。回顾前面的代码,地面的位置为(0,-10),为一个box,大小为100m*20m。而动态body的初始位置为:(0,4),大小为2*2的box。所以,地面的上边正好为x轴,当物体受到重力,就会"下落",当y=1.01(y=1)时候,其下边就碰到了x轴,即地面,所以不会下落了,这就解释了上面的1.01之后不变了。当timeStep=5/60,下落的位移为h=4 - 3.93,自由落体的公式为:s=g*t*t/2。如果假设这里的timeStep就是表示物理时间推进的时间,单位为s,那么倒是发现:s=g*t*t能解释得到s=10*(5/60)*(5/60)=0.0694444= h = 4 - 3.93,但是如果同样的理解来计算第二次调用Step()之后的结果,就不对了:s=10*(2*5/60)*(2*5/60)=0.277777,而实际结果中h=4-3.79=0.21。当timeStep=10/60的时候,也能得到类似的结果。这就有点纠结了。。。。对于每一个timeStep,那么如果用物理公式计算,t到底对应于多少呢?(不可能Box2D进行模拟不符合物理规律吧。。。我表示压力很大。。。)