第七章装置(fixture)
7.1简介
回想一下,我们曾经说过形状并不知道它依附于的物体的存在,它们能够在物理模拟中独立使用。因此Box2D提供了b2Fixture类来讲形状附加到物体上。一个物体可以没有装置,也可以由多个装置,一个有一个以上装置的物体通常称为一个组合体(compoundbody)。
装置有以下组成元素:
·一个单独的形状
·broad-phase代理(可以有多个)
·密度(density),摩擦力(friction)和弹力(restitution)
·碰撞过滤标记(collision filtering flags)
·指回父物体的指针
·用户数据(user data)
·传感器标记(sensor flag)
7.2创建装置
要创建一个装置,首先需要初始化一个装置定义(fixturedefinition),然后将装置定义作为参数传给父物体的方法:
b2FxitureDeffixtureDef;
fixtureDef.shape= &myShape;
fixtureDef.density= 1.0f;
b2Fixture*myFixture = myBody->CreateFixture(&fixtureDef);
这段代码创建好一个装置,并且将这个装置附加到物体上。你不需要去保存这个装置的指针,因为当父物体被析构的时候,这个附加到物体上的装置也会被自动析构。你可以创建多个装置附加到物体上。
你可能需要析构一个物体上的装置,以此来模拟一个可分裂的对象,否则的话,你不需要去管它们,让物体的析构函数来替你处理掉附加的装置就可以了。
myBody->DestoryFixture(myFixture);
密度
装置的密度是用来计算父物体的质量的,密度可以是0甚至是负数,你应该为所有的装置使用相似的密度,这样做可以提高堆叠的稳定性。
当你设置密度时,物体的质量并不会随之发生改变,你必须要手动调用ResetMassData方法来重新计算物体的质量。
fixture->SetDensity(5.0f);
body->ResetMassData();
摩擦力
摩擦力用来让物体更真实的沿着其他对象滑动,Box2D支持静摩擦力(staticfriction)和动摩擦力(dynamicfriction),但是二者使用同一个属性值进行设置。摩擦力在Box2D中能够被精确模拟,摩擦力的大小等于接触面的压力(normalforce)乘以摩擦系数,这种称为Coulombfriction(库伦摩擦)。摩擦系数通常在0到1之间,但是任何非负数都是有效的。当摩擦系数为0时,物体表面没有摩擦力,当摩擦系数为1时,物体表面会具有很大的摩擦力。在计算两个形状之间的摩擦力时,Box2D会结合两个形状的父装置的摩擦系数,通过计算他们的几何平均值得出:
float32friction;
friction= sqrtf(fixtureA->friction * fixtureB->friction);
因此当其中一个装置具有大小为0的摩擦系数时,整个接触面上就不会有摩擦力存在(摩擦力为0)。
你可以重写b2Contact::SetFriction方法来使用不同的计算方程,这通常实在b2ContactListener的回调函数中完成的。
弹性
物体的弹性用来使物体在接触后反弹。弹性系数通常在0到1之间。我们考虑下面的情形:将一个小球坠落在一张桌子上,当弹性系数为0时,小球完全不会弹起(直接就砸到桌子上不动了),这种情况称为(完全)非弹性碰撞;当弹性系数为1时,小球会以完全相同的速度向上弹起(这意味着小球会达到坠落的初始高度,没有能量损失),这称为完全弹性碰撞。两个物体间的弹性系数通过下面的方程来计算:
float32restitution;
restitution= b2Max(fixtureA->restitution, fixtureB->restitution);
弹性系数通过这样的方式来计算,这样你就可以有一个非常有弹力的小球,而不需要有一个非常有弹力的地面了。(注:因为是取二者的较大值,所以小球具有弹力就可以解决问题了)
你可以重写b2Contact::SetRestitution方法来使用不同的计算方程,这通常实在b2ContactListener的回调函数中完成的。
当一个形状执行多次碰撞时,弹性会被近似的模拟,这是由于Box2D使用迭代解析器的缘故。当碰撞的入射速度非常小的时候,Box2D会使用(完全)非弹性碰撞进行模拟,这是为了防止抖动。入射速度的阈值请参考定义在b2Settings.h文件中的b2_velocityThreshold常量。
过滤(filtering)
碰撞过滤允许你避免装置间的碰撞。例如:假设你有一个骑自行车的角色,你希望自行车和地形发生碰撞,角色也和地形发生碰撞,但是你不希望你的角色和自行车发生碰撞(因为显然他们彼此重叠),这时你就需要设置过滤标记。Box2D中通过分类(category)和组(group)来支持这种碰撞过滤。
Box2D支持16种碰撞分类,对于每一个装置,你都可以指定它属于某一个碰撞分类,同时你可以指定那些碰撞分类可以与当前装置发生碰撞。举例来说,在一个多人游戏中,你可以指定所有的玩家彼此之间不发生碰撞,所有的怪兽彼此间不发生碰撞,但是玩家和怪兽之间可以发生碰撞。通过指定蒙版标记(masking bits),例如:
playerFixtureDef.filter.categoryBits= 0x0002;
monsterFixtureDef.filter.categoryBits= 0x0004;
playerFixtureDef.filter.maskBits= 0x0004;
monsterFixtureDef.filter.maskBits= 0x0002;
下面是当碰撞能够发生时的判断条件:
uint16catA = fixtureA.filter.categoryBits;
uint16maskA = fixtureA.filter.maskBits;
uint16catB = fixtureB.filter.categoryBits;
uint16maskB = fixtureB.filter.maskBits;
if((catA & maskB) != 0 && (catB & maskA) != 0)
{
// fixtures can collide
}
碰撞组(collisiongroup)允许你指定一个整数的组索引值,你可以设置具有相同组索引值的装置发生碰撞(正索引),也可以设置所有具有相同组索引值的装置不会彼此碰撞(负索引),组索引值通常用于以某种方式相关联的物体之间,比如自行车的部件。在下面的例子中,fixture1和fixture2会发生碰撞,fixture3和fixture4不发生碰撞:
fixture1Def.filter.groupIndex= 2;
fixture2Def.filter.groupIndex= 2;
fixture3Def.filter.groupIndex= -8;
fixture4Def.filter.groupIndex= -8;
具有不同组索引值的装置在发生接触的时候,通过categoryBits和maskBits来判断是否发生碰撞,也就是说,组索引值相比于蒙版标记具有更高的优先级(higherprecedence)。
注意,除了上述过滤规则,Box2D中还有下面的过滤规则:
·静态物体(static)上的装置只能和动态物体发生碰撞
·运动物体(kinematic)上的装置只能和动态物体发生碰撞
·同一个物体上的装置之间不会发生碰撞
·你可以选择“启用/禁用“关节绑定的物体上面的装置的碰撞
有时候你需要为已经创建出来的装置指定碰撞过滤条件,你可以通过调用b2Fixture::GetFilterData和b2Fixture::SetFilterData方法来获取(get)和设置(set)装置的b2Filter结构。需要注意的一点是,修改过滤条件不会在当前时间间隔(timestep)生效,下一个时间间隔才会起作用(参考世界类)。
7.3传感器(sensor)
有时候游戏逻辑需要知道两个装置重叠在了一起,尽管此时没有发生任何碰撞,这可以通过传感器来实现。传感器是一种能够察觉到碰撞的装置,但是传感器不会响应这个碰撞。
你可以将任何一个装置标记为传感器,传感器可以使静态的(static),运动的(kinematic),或者动态的(dynamic),我们知道一个物体上可以附加多个装置,所以在物体上传感器和真正的装置(非传感器)可以混合存在。此外,当接触的两个物体之中至少有一个动态物体时,传感器才会感知到物体彼此接触,所以传感器无法够捕获这几种物体之间的接触:运动物体-运动物体,运动物体-静态物体,静态物体-静态物体。
传感器不会生成接触点,有两种方法可以得到传感器的状态:
1. b2Contact::IsTouching
2. b2ContactListener::BeginContactand EndContact