9.2 工作流程
这里直接从Box2d自带的HelloWorld示例开始,从Box2d官网下载的源码包中可以找到这个例子,这是一个没有图形显示的HelloWorld,正因为如此,呈现的内容更加精简,后面的内容会介绍怎样把Box2d的模拟效果显示在Cocos2d-x上面,现在先把渲染抛到脑后吧。首先需要了解一下最纯粹的Box2d。
Box2d的运行流程大致如图9-1所示,首先是初始化Box2d,然后可以执行各种操作,如创建对象,操作对象、在循环中更新物理世界,Box2d会自动执行碰撞处理,并触发碰撞监听。需要注意的是,创建对象等操作不能在碰撞监听回调中执行。
图9-1 运作流程
Box2d的物理模拟全部发生在b2World这个类中,其表示整个物理世界,使用Box2d的第一步就是要把b2World创建出来,构造函数很简单,只有一个参数,表示这个世界的重力加速度,地球的重力加速度是-9.8f,一般可以取近似值-10.0f(月球上的重力为地球的1/6大约为1.63f)。Box2d用b2Vec2来描述一个向量,重力也是一个向量,其结构等于一个Vec2,下面这段代码创建了一个世界。
b2Vec2 gravity(0.0f, -9.8f); b2World world(gravity);
有了世界之后,需要在世界里面添加各种各样的对象,这些对象不需要并且不允许使用new来创建,应该直接调用World的Create系列函数,由World来创建。
Box2d中使用了SOA(小对象分配器)来快速有效地使用内存(能够高效地创建大量50~300字节大小的对象), Box2d每创建一个对象,都需要先填充一个对象的描述结构(这点和PhysX非常相似,这里使用一个b2BodyDef结构来填充描述。
b2BodyDef groundBodyDef; groundBodyDef.position.Set(0.0f, -10.0f); b2Body* groundBody = world.CreateBody(&groundBodyDef);
有了一个b2Body对象之后,还需要描述这个对象的形状,Box2d提供一个b2PolygonShape对象来描述一个多边形,调用它的SetAsBox()函数传入宽和高可以得到一个四边形,调用它的Set()函数传入一个顶点数组,可以得到一个N边形,把这个形状直接设置到b2Body对象中。
//创建一个宽为50.0f,高为10.0f的四边形,并设置到groundBody b2PolygonShape groundBox; groundBox.SetAsBox(50.0f, 10.0f); groundBody->CreateFixture(&groundBox, 0.0f);
上面设置了地板的形状,它的质量为0,默认是一个静态的物体,静态的物理在物理世界中不会移动,但在游戏中,还需要创建一些动态的物体。要创建动态刚体的话,需要更多的设置,首先需要设置b2BodyDef的type,默认为b2_staticBody,动态物体需要将它设置为b2_dynamicBody。动态物体的形状描述也需要更多的内容,如质量、摩擦力……Box2d提供b2FixtureDef结构体来描述这些内容。
//定义动态Body并创建 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); //定义动态Body的配置 b2FixtureDef fixtureDef; fixtureDef.shape = &dynamicBox; //动态对象的密度不为0 fixtureDef.density = 1.0f; //设置摩擦力 fixtureDef.friction = 0.3f; //将形状配置到Body body->CreateFixture(&fixtureDef);
初始完一个World以及相关的对象之后,需要让整个世界动起来,这需要调用World对象的Step()方法,其需要几个参数,第一个是从上一次调用到现在的时间间隔,这个参数可以直接使用Cocos2d-x在Update()函数中传入的参数。一般情况下,在每一帧的Update()调用一次Step。Box2d当运行比较复杂的运算的时候,需要一定的迭代次数来保证模拟的精度,Step要求传入两个迭代次数,即需要传入速度迭代数和位置迭代数,Box2d给出的默认值是8和3,如果对精度要求比较高,可以适当地增加这两个值,该值越大,相应地性能越低。velocityIterations表示速度约束的遍历次数,positionIterations表示位置约束的遍历次数,可以根据游戏的具体情况来设置这两个值。
world.Step(dt, velocityIterations, positionIterations);
在游戏更新的过程中,会不断地创建新的对象到世界中,也会不断地删除一些旧的对象,不管怎样,在World被析构的时候,这个世界的一切,都会从内存中被释放。