Oh!Coder

Coding Life

第六章 定制器

| Comments

声明:此文章翻译自Box2D v2.2.0用户手册,仅供学习参考。

6.1 关于

回想一下之前所描述的形状,在不了解物体的情况下也可以在物理模拟中独立使用。因此Box2D提供了b2Fixture类来把形状附加到物体上。定制器(Fixture)包括如下:

  • 单一形状(a single shape)
  • broad-phase代理(broad-phase proxies)
  • 密度,摩擦以及恢复(density,friction and restitution)
  • 碰撞过滤标记(collision filtering flags)
  • 返回父物体指针(back pointer to the parent body,译者注:父物体是指具体附加到的物体)
  • 用户数据(user data)
  • 传感器标记(sensor flag)

下面分段进行描述。

6.2 创建定制器(Fixture Creation)

各种定制器是通过初始化一个定制器定义来进行创建的,之后将其传递给父物体。

1
2
3
4
b2FixtureDef fixtureDef;
fixtureDef.shape = &myShape;
fixtureDef.density = 1.0f;
b2Fixture* myFixture = myBody->CreateFixture(&fixtureDef);

这里创建了一个定制器并将其附加到物体上。当物体销毁的时候与其关联的定制器也会随之被销毁,所以并不需要对定制器的指针进行存储。你可以在单一物体上创建多个定制器。

你可以通过父物体对定制器进行销毁,这就像模拟分裂对象一样。否则的话就不要去管定制器,让物体自身当中的销毁方法来销毁自身的定制器。

1
myBody->DestroyFixture(myFixture);
  • 密度(Density)

通过定制器中的密度来计算父物体上的质量属性。密度可以设置为零或者正数。你可以使用相近的密度来设置你所有的定制器。这会提高物体之间的稳定性(stacking stability)。

当你设置好密度之后物体的质量是不变的。你必须调用ResetMassData来完成这次修改。

1
2
fixture->SetDensity(5.0f);
body->ResetMassData();
  • 摩擦(Friction)

摩擦使对象之间相互滑动时更加逼真。虽然Box2D支持静态和动态摩擦,但是这两种摩擦都使用相同的参数进行设置。摩擦力的强度与正交力(也称为库伦摩擦)成正比。摩擦系数通常被设置在0到1之间,但是也可以设置成任何非负数。0意味着没有摩擦,1意味着强摩擦。当计算两个形状之间的摩擦时,Box2D必须结合两个形状的摩擦参数。就是通过下面的方式来完成的:

1
2
float32 friction;
friction = sqrtf(shape1->friction * shape2->friction);

所以如果定制器的摩擦为零时,那么相关联的摩擦也为零。

  • 恢复(Restitution)

恢复可以使物体反弹。恢复数值通常设置在0和1之间。考虑一个落到桌子上的小球。恢复数值如果为零则意味着小球不会反弹,这称为非弹性碰撞(inelastic collision)。如果数值为1则意味着小球原速反向弹回,这称为完全弹性碰撞(perfectly elastic collision)。恢复是通过下面的式子合成的。

1
2
float32 restitution;
restitution = b2Max(shape1->restitution, shape2->restitution);

定制器携带碰撞过滤信息能够允许你防止某些游戏物体碰撞。

当一个形状有大量接触时,此时会对恢复做近似模拟。这是因为Box2D使用了迭代求解器(iterative solver)。当碰撞速度很小的时候Box2D也会使用非弹性碰撞。这么做是为了防止抖动。

  • 过滤(Filtering)

碰撞过滤允许你防止定制器之间进行碰撞。举个例子,比如说有一个人骑着一辆自行车。你希望自行车和人与地面有碰撞,但是你不想让人与自行车之间有碰撞(因为他们必须是重叠的)。Box2D使用类别和组支持这种过滤。

Box2D支持16个碰撞类别。对每一个定制器你都可以指定属于类别中的哪一类。你还可以指定这个定制器可以和其他哪些特定类别进行碰撞。举个例子,你可以在多人游戏中指定所有游戏玩家之间不产生碰撞,怪物之间不能产生碰撞,但是玩家和怪物之间需要碰撞。这是通过使用掩码(masking bits)来实现的。比如说:

1
2
3
4
playerFixtureDef.filter.categoryBits = 0x0002;
monsterFixtureDef.filter.categoryBits = 0x0004;
playerFixtureDef.filter.maskBits = 0x0004;
monsterFixtureDef.filter.maskBits = 0x0002;

下面是发生碰撞的规则:

1
2
3
4
uint16 catA = fixtureA.filter.categoryBits;
uint16 maskA = fixtureA.filter.maskBits;
uint16 catB = fixtureB.filter.categoryBits;
uint16 maskB = fixtureB.filter.maskBits;

碰撞组让你指定一个整数组索引。你可以让具有同一组索引的所有定制器总是可以碰撞(正数索引)或者永远不能碰撞(负数索引)。组索引通常用在具有某种关联的事物上,就像自行车的零部件一样。下面的例子中,fixture1和fixture2总是碰撞,但是fixture3和fixture4永远不会碰撞。

1
2
3
4
fixture1Def.filter.groupIndex = 2;
fixture2Def.filter.groupIndex = 2;
fixture3Def.filter.groupIndex = -8;
fixture4Def.filter.groupIndex = -8;

不同组索引之间的定制器的碰撞情况会根据根据分类和掩码进行过滤。换句话说,组过滤比类别过滤具有更高的优先级。

注意Box2D中发生的其他碰撞过滤(collision filtering)。这里是一个列表:

  • 一个静态物体上的定制器只能与一个动态物体碰撞。
  • 一个运动学(kinematic)物体只能与一个动态物体碰撞。
  • 具有同一个定制器的物体之间永远不能发生碰撞。
  • 当两个物体具有同一个连接器时,你可以选择性的打开/关闭两个物体中定制器之间的碰撞功能。

有时候当定制器创建之后你需要对其进行更改。你可以使用b2Fixture::GetFilterData和b2Fixture::SetFilterData对已有的定制器访问或者设置b2Filter结构。注意更改的过滤器数据直到下一个时间步长不会添加和删除接触(详见World类型)。

6.3 传感器(Sensors)

有时候游戏逻辑需要知道已经重叠的两个定制器之间什么时候不需要碰撞响应。这个功能是通过传感器来实现的。一个传感器是一个进行碰撞检测但是不产生响应的定制器。

你可以将任意一个定制器标记为传感器。传感器可以是静态(static)也可以是动态(dynamic)。记住同一个物体上你可以有多个定制器并且你可以有任意传感器和实体定制器的组合。

传感器不会生成接触(contact points)。有两种方式得到传感器的状态:

1
2
b2Contact::IsTouching
b2ContactListener::BeginContact and EndContact

Comments