您的位置:首页 > 移动开发 > IOS开发

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

2017-01-03 21:48 387 查看




第六章物体(bodies)
6.1简介
物体具有位置(position)和速度(velocity),你可以对物体施力(force),施加扭矩(torque)和冲量(impulse),物体可以是静态的(static),运动的(kinematic)或者动态的(dynamic),下面是物体类型的定义:

 

b2_staticBody
一个静态的物体在模拟过程中保持不动,就如同它的质量无限大。实际上,Box2D中将物体的mass属性和inversemass属性存储为0。静态物体能够被用户手动移动,它的速度是0。静态物体不会和其他静态物体或者运动的(kinematic)物体发生碰撞。

 

b2_kinematicBody
一个运动的物体在模拟时按照一定的速度运动,运动的物体不受力的影响,他们也能够由用户手动移动,但是通常情况下都是通过设置一定的速度使之运动。运动的物体同样像是有无限大的质量一样(因此力无法影响其运动,使其产生加速度),然而,Box2D同样也将运动的物体的mass属性和inversemass属性存储为0。运动的物体不会和其他运动的物体或者惊天物体发生碰撞。

 

b2_dynamicBody
动态物体(dynamicbody)是完全模拟的,它们也能够由用户手动移动,但是通常情况下,它们都是受力运动的。动态物体可以和所有类型的物体发生碰撞,一个动态物体具有有限的、非零的mass属性值,如果你将动态物体的mass属性设置为0,它会自动被重设为1kg并且它不会转动。
物体是装置(fixture),或者说形状的骨架物体可以携带着装置一起在世界中移动。Box2D中的物体始终是刚体,这意味着Box2D中附加到同一个物体上的两个装置永远也不会发生相对位移,同样它们也不会发生碰撞。
装置具有碰撞几何体和密度属性,通常,物体通过附加到它上面的装置计算出自己的质量,然而,当物体构造好之后,你可以重设它的质量。
通常情况下你会保留所有创建出的物体的指针,这样你就可以获取到物体的位置来更新你的图形实体。同样,如果你保留了物体的指针,你可以用指针去释放掉它们。

 
6.2物体定义
在一个物体被创建之前,你应该首先定义一个物体定义(bodydefinition,b2BodyDef),物体定义包含了所有需要用来创建和初始化物体的数据。
Box2D将物体定义中的数据拷贝出来,它并不保存物体定义的指针,这意味着当你创建一系列的物体时,可以复用同一个物体定义。
让我们过一遍物体定义中的一些关键成员。

 
物体类型(bodytype)
正如我们在本章开头讨论的那样,一共有三个物体类型:静态的(static),运动的(kinematic)和动态的(dynamic),你应该在创建物体的时候就将物体类型指定好,不然留到以后再改代价会很大。

bodyDef.type= b2_dynamicBody;
物体类型一定要设置。

 
位置(position)和角度(angle)
物体定义允许你再创建物体的时候就指定好它的位置,这比起创建完物体再去移动它要好得多。
注意:不要去创建物体之后再去移动它,不然当你创建很多物体的时候,效率问题就会很严重。
对于物体而言有两点比较令人感兴趣的地方,第一点是物体的原点,装置(fixture)和关节(joint)是相对于物体原点附加到物体上的。另一点是物体的质心(centerofmass),物体的质心由附加到物体上的形状的质量分布决定,或者可以显示地有b2MassData属性进行设置。大多数Box2D的内部计算都要依靠物体质心的位置,例如b2Body会存储物体质心的线速度。
当你构造一个物体定义时,你并不知道物体的质心在哪儿,因此你只需要指定物体的原点和物体的初始角度(弧度),因为这些属性不会受物体的质心位置影响,如果之后你修改物体的质量了,那么物体的质心就会发生变化,但是物体的原点和不会改变,附加到物体上的形状和关节就不会发生移动。

bodyDef.position.Set(0.0f,2.0f);    // the body’s origin position.

bodyDef.angle= 0.25f * b2_pi;      // the body’s angle in radians.
一个刚体也是一个参照系,你可以在这个参照系中定义装置(fixture)和关节(joint),这些装置和关节锚点不会相对物体的本地参照系发生位移。

 
阻尼(damping)
阻尼用来降低世界中物体的移动速度,阻尼和摩擦力不同,摩擦力只有物体之间存在接触时才存在,阻尼不是用来替代摩擦力的,这两个效果需要同时使用。
阻尼系数的范围在0到无穷大之间,0意味着没有阻尼效果,无穷大意味着满阻尼。通常情况下,你可以设置阻尼系数在0到0.1之间,一般我不会使用线性阻尼,因为这会使物体看起来像在漂浮一样。

bodyDef.linearDamping= 0.0f;

bodyDef.angularDamping= 0.01f;
阻尼类似于稳定性和性能,当阻尼系数较小的时候,阻尼的效果几乎独立于时间间隔(timestep),当阻尼系数较大时,阻尼效果将随时间间隔的变化而变化,如果你使用固定的时间间隔(推荐)的话,这就不再是问题了。

 
重力比例(gravityscale)
你可以使用重力比例来调整重力对于单个物体的影响,需要小心的是,当你增加重力的影响的同时,稳定性会降低。

//Set the gravity scale to zero so this body will float

bodyDef.gravityScale= 0.0f;

 
休眠参数
休眠是什么意思?你知道模拟物体的计算成本是非常高的,因此我们需要模拟的物体越少越好。当一个物体停止运动的时候,我们就停止对它的模拟。
当Box2D发现一个物体(或者一组物体)停止运动的时候,物体就进入休眠状态,消耗很少的CPU计算。如果一个“醒着的”物体和一个休眠的物体发生碰撞,这是休眠的物体就会被唤醒,如果附加到物体上的关节或者接触被摧毁了,也会唤醒物体。你也可以手动唤醒一个物体。
物体定义中你可以设置一个物体是否能够休眠,也可以指定物体初始的时候是否是休眠的。

bodyDef.allowSleep= true;

bodyDef.awake= true;

 
固定旋转(fixedrotation)
你可能需要一个刚体,例如某个角色,其旋转被固定住,这样即使物体在负载的情况下,也不会旋转。你可以通过固定旋转设置来实现:

bodyDef.fixedRotation= true;
固定旋转标记使得物体的旋转惯性(rotationalinertia)和反向旋转惯性(inverse rotational inertia)被置0。

 
子弹(bullet)
游戏模拟通常会按一定的帧频率来生成一系列的图片,也就是所谓的离散模拟(discretesimulation)。在离散模拟中,刚体可以在一个时间间隔(timestep)内移动很大一段距离,如果物理引擎不能很好地考虑到这种大范围的移动,就可能导致物体不正确的穿过其他物体,也就是我们之前提到的穿透(tunneling)现象。
默认情况下,Box2D采用连续碰撞检测(continuouscollisiondetection,CCD)来防止动态物体(dynamic)穿过静态物体,这是通过扫描物体从旧的位置到新的位置的运动过程来实现的,物理引擎在扫描的过程中会查找新的碰撞并计算这些碰撞的碰撞时间(TOI)。物体会被移动到他们的第一个TOI对应的位置上去,接着解析器会模拟一个子时间间隔(sub-step)以完成整个时间间隔。在子时间间隔内还有可能有额外的TOI。
通常CCD不会用来处理动态物体和动态物体的碰撞,这是从效率角度考虑的。在一些场景中,你需要动态物体使用CCD,例如,你可能想要向一堆动态的砖块发射高速子弹,如果没有CCD,子弹很可能会穿过砖块。
Box2D中高速移动的物体被统称为子弹,子弹会使用CCD进行碰撞处理,无论是何静态物体还是动态物体发生碰撞。基于你的游戏设计,你需要考虑那些物体作为子弹来处理,如果你决定将某个物体作为子弹来处理,使用下面的设置:

bodyDef.bullet= true;
bullet标记只会对动态物体有效。

 
激活态(activation)
你可能希望一个物体被创建之后不参与碰撞或者动态计算,这和休眠状态很像,唯一不同的是这种物体不会被唤醒,并且物体的装置不会放置到broad-phase中进行计算。这也意味着这个物体不会参与碰撞,无法做射线投射计算等等。
你可以首先创建一个非激活态的物体,然后再激活它:

bodyDef.active= true;
(注:注意我们上面描述中那种状态是非激活态,也就是bodyDef.active= false的状态,而且默认创建出来的物体是激活状态的,所以这里不太清楚原文档中为什么有上面这行语句)
如果已经将关节关联到非激活态的物体,这些关节不会被模拟。当你激活一个物体的时候要注意的一点是,绑定的关节是不会发生扭动的。
注意,激活一个物体和从缓存中创建一个物体的成本是差不多的,所以请不要对于流体世界(streamingworld)使用激活语句(这样既占内存,效率也不高),我们使用构造和析构可以更节省内存。

 
用户数据(userdata)
用户数据是一个void指针,它将你的应用程序中的对象和物体关联起来。你应该使用相同类型的对象类型来连接用户数据。

b2BodyDefbodyDef;

bodyDef.userData= &myActor;

 
6.3物体工厂(body factory)
物体使用世界类(worldclass)中提供的工厂方法来构造和析构,这样世界(world)就能够使用高效的分配器(allocator)来创建物体,并将物体加入到世界数据结构中。

b2Body*dynamicBody = myWorld->CreateBody(&bodyDef);

…do stuff …

myWorld->DestroyBody(dynamicBody);

dynamicBody= NULL;
注意:永远不要使用new或者malloc来创建物体,这样创建出来的物体不会被世界所识别,物体的属性也不能正确的初始化。
Box2D不会保留对物体定义(bodyDef)的引用或者对于其中存储的数据保留引用(除了user
data指针)。因此你可以为物体定义创建临时变量,并且重用之。
在Box2D中,你可以不去逐个析构物体,而是直接析构b2World对象,Box2D会帮你完成所有的清理工作。然而,你应该留神将物体的指针指向空(NULL)。
当你析构一个物体时,附加到上面的装置和关节自动被析构。知道这点了,你就知道应该如何管理形状和关节指针了。

 
6.4使用物体
创建完物体后,我们可以在物体上执行很多操作,例如设置mass属性,获取物体的位置和速度,对物体施力,变换点和向量等等。

 
质量数据(massdata)
每个物体都有质量(标量),质心(2维向量)和转动惯性(标量)。静态物体的质量和转动惯性为0,当一个物体具有固定旋转(fixedrotation)时,它的转动惯性为0.
通常质量属性在装置附加到物体之后就被计算好了。你也可以在运行时修改质量属性,当你有特殊的使用场景,必须要修改质量属性时你可以这样来设置。

voidSetMassData(const b2MassData* data);
当你直接设置一个物体的质量之后,你有时可能需要再次通过附加到物体的装置重新计算出物体的质量,你可以通过以下方式来做:

voidResetMassData();
物体的质量数据可以通过以下方法获得:

float32GetMass() const;

float32GetInertia() const;

constb2Vec2& GetLocalCenter() const;

voidGetMassData(b2MassData* data) const;

 
状态信息(stateinformation)
关于物体的状态有很多方面,你可以通过以下的函数方便的获取和设置这些状态:

voidSetType(b2BodyType type);

b2BodyTypeGetType();

 

voidSetBullet(bool flag);

boolIsBullet() const;

 

voidSetSleepingAllowed(bool flag);

boolIsSleepingAllowed() const;

 

voidSetAwake(bool flag);

boolIsAwake() const;

 

voidSetActive(bool flag);

boolIsActive() const;

 

voidSetFixedRotation(bool flag);

boolIsFixedRotation() const;

 
位置和速度(positionand velocity)
通常当你需要渲染一个相关的角色时,你需要获取物体的位置和旋转,你也可以设置物体的位置,尽管这并不常见(因为通常都是由Box2D来模拟运动的)。

boolSetTransform(const b2Vec2& position, float32 angle);

constb2Transform& GetTransform() const;

constb2Vec2& GetPosition() const;

float32GetAngle() const;
你可以获取物体的质心在本地坐标系和世界坐标系中的坐标,尽管Box2D中的很多模拟都用到了质心,但是通常情况下你不需要去用到它。相比之下,你会更常用到的是使用物体变换,例如,你有一个矩形的物体,物体的原点可能在四边形的一个角上,但是它的质心却在四边形的中心。

constb2Vec2& GetWorldCenter() const;

constb2Vec2& GetLocalCenter() const;
你可以获取物体的线速度和角速度,物体的线速度是对物体质心而言的,因此当物体的质量属性发生改变的时候,物体的线速度也会发生变化。

内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签:  IOS box2d 用户指南