内容很多摘自
Aman JIANG(江超宇)翻译的Box2D v2.0.1 用户手册

第09章 接触(Contacts)

9.1 关于

接触(contact)是由 Box2D 创建的用于管理fixture间碰撞的对象。接触有不同的种类,它们都派生自 b2Contact,用于管理不同类型形状之间的接触。例如, 有管理多边形之间碰撞的类,有管理圆形之间碰撞的类。

这是与接触有关的术语

接触点(contact point)

接触点就两个形状相互接触的点。在Box2D中,近似地认为在少数点处有接触。

接触法线(contact normal)

接触法线是一个单位向量,由一个fixture指向另一个fixture。按照惯例,向量由fixtureA指向fixtureB。

接触分隔(contact separation)

分隔正好与穿透(penetration)相反。当形状相重叠时, 分隔为负。有可能将来的Box2D版本中会以正隔离来创建触点, 所以当有触点的报告时你可能需要检查一下符号。

contact manifold

两个凸多边形相互接触,有可能会产生两个接触点。这些点都有相同的法线,所以就相它们分成一组,构成contact manifold,这是连续区域接触的一个惯例。

(译注: manifold不知道怎么翻译。我猜测manifold是指有相同特性的东西归成一类。接触点有相同的法线,就归成contact manifold。)

法向冲量(normal impulse)

法向力作用于接触点,用于防止形状相互穿透。为方便起见,Box2D使用冲量(impulses)。法向力与时间步相乘,构成法向冲量

切向冲量(tangent impulse)

切向力会在接触点生成,用于模拟摩擦。为方便起见,切向作用使用冲量的方式存储。

接触标识(contact ids)

从最开始的猜测值出发,Box2d得出当前时间步的触点压力,再重新利用当前时间步的压力结果去推测下一个时间步的压力结果。接触标识用于匹配跨越时间步的触点。标识包含了几何特征索引以便区分触点。

当两个fixture的AABB重叠时,接触就被创建了。有时碰撞筛选会阻止接触的创建。当AABB 不再重叠后接触会被摧毁。

也许你会皱起眉头,为了没有发生实际碰撞的形状(只是它们的 AABB)却创建了接触。好吧,的确是这样的,这是一个“鸡或蛋”的问题。我们并不知道是否需要一个接触,除非我们创建一个接触去分析碰撞。如果形状之间没有发生碰撞,我们需要正确地删除接触,或者,我们可以一直等到 AABB 不再重 叠。为了提高性能, Box2D选择了后面这个方法。

9.2 接触类(Contact Class)

之前已经提及过,接触对象是Box2D内部创建和摧毁的,并不是由用户来创建。然而,你还是能够访问接触类并和它交互的。

你可以访问原始的contact manifold:

b2Manifold* GetManifold();

const b2Manifold* GetManifold()const;

你甚至可以修改manifold,一般情况下不提倡你怎样做。修改manifold是较高级的用法。

这个是帮助函数,去获取b2WorldManifold:

void GetWorldManifold(b2WorldManifold*worldManifold) const;

这使用了物体的当前位置去计算出接触点在world坐标下的位置。

传感器(Sensors)并不创建manifolds,所以要使用:

bool touching =sensorContact->IsTouching();

这函数对于非传感器(non-sensors)也有效。

从接触(contact)中你可以得到fixture, 从而再得到body。

b2Fixture* fixtureA = myContact->GetFixtureA();

b2Body* bodyA =fixtureA->GetBody();

MyActor* actorA =(MyActor*)bodyA->GetUserData();

你可以使一个接触失效。这仅仅在b2ContactListener::PreSolve事件中有效,下面会再进行讨论。

9.3 访问接触(Accessing Contacts)

你有几种方法来访问接触。为了访问接触,你可以直接查询world或者body结构,还可以实现一个接触监听器(contact listener)。

在world中,你可以遍历所有的接触:

for (b2Contact* c =myWorld->GetContactList(); c; c = c->GetNext())

{

// process c

}

同样在body中,你也可以遍历所有接触。接触以图的方式存储,使用了接触边数据结构(contact edge structure),

for (b2ContactEdge* ce =myBody->GetContactList(); ce; ce = ce->next)

{

b2Contact* c = ce->contact;

// process c

}

通过下面描述的接触监听器,你也可以访问接触。

注意

通过b2World或者b2Body直接访问,有可能会错过一些时间步中产生的临时接触。而使用b2ContactListener 就可以很精确的得到全部结果。

9.4 接触监听器(Contact Listener)

通过实现 b2ContactListener 你就可以收到接触数据。接触监听器支持几种事件: 开始(begin),结束(end), 求解前(pre-solve), 求解后(post-solve)。

class MyContactListener : publicb2ContactListener

{

public:

void BeginContact(b2Contact*contact)

{ // handle begin event }

void EndContact(b2Contact* contact)

{ // handle end event }

void PreSolve(b2Contact* contact,const b2Manifold* oldManifold)

{ // handle pre-solve event }

void PostSolve(b2Contact* contact,const b2ContactImpulse* impulse)

{ // handle post-solve event }

};

注意

不要保存发送到b2ContactListener的指针。取而代之,用深拷贝的方式将触点数据保存到你自己的缓冲区。下面的例子演示了一种方法。

在运行期(run-time), 你可以创建listener的实例对象,并使用b2World::SetContactListener来注册这个对象。 但要保证当world对象存在时,listener要留在作用域中。

Begin事件

当两个fixture开始有重叠时,事件会被触发。传感器和非传感器都会触发这事件。这事件只能在时间步内(译注: 也就是b2World::step函数内部)发生。

End事件

当两个fixture不再重叠时,事件会被触发。传感器和非传感器都会触发这事件。当一个body被摧毁时,事件也有可能被触发。所以这事件也有可能发生在时间步之外。

Pre-Solve事件

在碰撞检测之后,但在碰撞求解之前,事件会被触发。这样可以给你一个机会,根据当前情况来决定是否使这个接触失效。 举个例子,在回调中使用b2Contact::SetEnabled(false),你就可以实现单侧碰撞的功能。每次碰撞处理时,接触会重新生效,所以你在每一个时间步 中都应禁用那个接触。由于连续碰撞检测,pre-solve事件在单个时间步中有可能发生多次。

void PreSolve(b2Contact* contact,const b2Manifold* oldManifold)

{

b2WorldManifold worldManifold;

contact->GetWorldManifold(&worldManifold);

if (worldManifold.normal.y <-0.5f)

{

contact->SetEnabled(false);

}

}

如果要确认触点状态或得到碰撞速度,可以在pre-solve事件中处理。

void PreSolve(b2Contact* contact,const b2Manifold* oldManifold)

{

b2WorldManifold worldManifold;

contact->GetWorldManifold(&worldManifold);

b2PointState state1[2], state2[2];

b2GetPointStates(state1, state2,oldManifold, contact->GetManifold());

if (state2[0] == b2_addState)

{

const b2Body* bodyA =contact->GetFixtureA()->GetBody();

const b2Body* bodyB =contact->GetFixtureB()->GetBody();

b2Vec2 point =worldManifold.points[0];

b2Vec2 vA = bodyA->GetLinearVelocityFromWorldPoint(point);

b2Vec2 vB =bodyB->GetLinearVelocityFromWorldPoint(point);

float32 approachVelocity =b2Dot(vB – vA, worldManifold.normal);

if (approachVelocity > 1.0f)

{

MyPlayCollisionSound();

}

}

}

Post-Solve事件

当你可以得到碰撞冲量(collision impulse)的结果时,post-solve事件会发生。 如果你不关心冲量,你可能只需要实现pre-solve事件。

在一个接触回调中去改变物理世界是诱人的。例如,你可能会以碰撞来施加伤害,并试图摧毁关联的角色和它的刚体。然而,Box2D并不允许你在回调中改变物理世界,因为你可能会摧毁 Box2D 正在运算的对象, 造成野指针。

处理触点的推荐方法是缓冲所有你关心的触点,并在时间步之后处理它们。一般在时间步之后你应该立即处理它们,否则其它客户端代码可能会改变物理世界,使你的缓冲失效。当你处理触点缓冲的时候,你可以去改变物理世界,但是你仍然应该小心不要造成无效的指针。在 testbed中有安全处理触点以避免无效指针的例子。

这是一小段 CollisionProcessing 测试中的代码,它演示了在操作触点缓冲时如何处理孤立物体。请注意注释。代码假定所有触点都缓冲于 b2ContactPoint 数组 m_points 中。

// 我们打算摧毁和contact有所关联的物体指针.

// 我们必须先缓存那些需要摧毁的物体,因为它们有可能被多个触点所共有。

const int32 k_maxNuke = 6;

b2Body* nuke[k_maxNuke];

int32 nukeCount = 0;

// 遍历contact缓存,摧毁相互接触时,无那么重的物体。

for (int32 i = 0; i <m_pointCount; ++i)

{

ContactPoint* point = m_points[i]

b2Body* body1 =point->shape1->GetBody();

b2Body* body2 =point->shape2->GetBody();

float32 mass1 =body1->GetMass();

float32 mass2 =body2->GetMass();

if (mass1 > 0.0f &&mass2 > 0.0f)

{

if (mass2 > mass1)

{

nuke[nukeCount++] =body1;

}

else

{

nuke[nukeCount++] =body2;

}

if (nukeCount == k_maxNuke)

{

break;

}

}

}

// 将nuke数组排序,使得重复的指针归在一起

std::sort(nuke, nuke + nukeCount);

// 删除body, 跳过重复的

int32 i = 0;

while (i < nukeCount)

{

b2Body* b = nuke[i++];

while (i < nukeCount&& nuke[i] == b)

{

++i;

}

m_world->DestroyBody(b);

}

9.5 接触筛选(Contact Filtering)

通常,你不希望游戏中的所有物体都发生碰撞。例如,你可能会创建一个只有特定角色才能通过的门。 这称之为接触筛选,因为一些交互被筛选出了。

通过实现b2ContactFilter类, Box2D允许定制接触筛选。这个类需要你实现一个ShouldCollide 函数, 这个函数接收两个b2Shape的指针作为参数。如果应该碰撞那么就返回true。

默认的ShouldCollide实现使用了“第06章,夹具(Fixtures)”定义的b2FilterData。

bool b2ContactFilter::ShouldCollide(b2Shape*shape1, b2Shape* shape2)

{

const b2FilterData& filter1 =shape1->GetFilterData();

const b2FilterData& filter2 =shape2->GetFilterData();

if (filter1.groupIndex ==filter2.groupIndex &&

filter1.groupIndex != 0)

{

return filter1.groupIndex> 0;

}

bool collide = (filter1.maskBits &filter2.categoryBits) != 0 &&

(filter1.categoryBits& filter2.maskBits) != 0;

return collide;

}

在运行期(run-time), 你可以创建自己的接触筛选实例,并使用b2World::SetContactFilter函数来注册。 你要保证当world存在时,你的filter要保留在作用域中。

MyContactFilter filter;

world->SetContactFilter(&filter);

// filter留在作用域中

Box2D v2.1.0用户手册翻译 - 第09章 接触(Contacts)相关推荐

  1. Box2D v2.1.0用户手册翻译 - 第06章 夹具(Fixtures)

    原文地址:http://blog.csdn.net/complex_ok/article/details/6719936 [-] 内容很多摘自 Aman JIANG(江超宇)翻译的Box2D v2.0 ...

  2. Box2D v2.1.0用户手册翻译 - 第02章 Hello Box2D

    内容很多摘自 Aman JIANG(江超宇)翻译的Box2D v2.0.1 用户手册 第02章 Hello Box2D Box2D的发布包中有个Hello World程序.程序创建了一个大大的地面盒( ...

  3. Box2D v2.1.0用户手册翻译 - 第10章 世界(World Class)

    内容很多摘自 Aman JIANG(江超宇)翻译的Box2D v2.0.1 用户手册 第10章 世界(World Class) 关于 b2World类包含物体和关节.它管理着模拟的方方面面,并允许异步 ...

  4. Box2D v2.1.0用户手册翻译 - 第08章 关节(Joints)

    内容很多摘自 Aman JIANG(江超宇)翻译的Box2D v2.0.1 用户手册 第08章 关节(Joints) 8.1 关于 关节用于把物体约束到世界,或约束到其它物体上.在游戏中, 典型例子有 ...

  5. (转载)BOX2D V2.3.0 用户手册中文版(第2章)-Hello Box2D

    (转载)BOX2D V2.3.0 用户手册中文版(第2章)-Hello Box2D Chapter 2 Hello Box2D Box2D的发布包中有个Hello World程序.程序创建了一个大大的 ...

  6. Box2D v2.1.0用户手册翻译 - 第12, 13, 14章

    内容很多摘自 Aman JIANG(江超宇)翻译的Box2D v2.0.1 用户手册 第12章 调试绘图(Debug Drawing) 实现 b2DebugDraw 可得到物理世界的细部图,这里是可用 ...

  7. (转载)BOX2D V2.3.0 用户手册中文版(第4章)-碰撞模块

    Chapter 4 碰撞模块 4.1 关于 碰撞模块包含了形状和操作形状的函数.该模块还包含了动态树(dynamic tree)和broad-phase,用于加快大型系统的碰撞处理速度. 碰撞模块被设 ...

  8. (转载)BOX2D V2.3.0 用户手册中文版(第8章)-关节

    Chapter 8 关节 8.1 关于 关节用于把物体约束到世界,或约束到其它物体上.在游戏中,典型例子有木偶,跷跷板和滑轮.用不同的方式将关节结合起来使用,可以创造出有趣的运动. 有些关节提供了限制 ...

  9. (转载)BOX2D V2.3.0 用户手册中文版(第11章)-杂项

    Chapter 11 杂项 11.1 用户数据 b2Fixture, b2Body 和 b2Joint 类都允许你通过一个 void 指针来附加用户数据.当你测试Box2D数据结构,并使其跟自己游戏引 ...

最新文章

  1. a16z基金:顶级风投眼中的2019技术趋势
  2. MIT“人造肌肉”登上Science封面,能提起自重650倍的物体,伸缩10000次都不坏
  3. 淘宝内部分享:MySQL MariaDB性能优化
  4. tensorflow从入门到放弃(二)
  5. 编译bluez-5.25 遇到的错误及解决方法
  6. 第三次学JAVA再学不好就吃翔(part92)--Map集合的遍历
  7. jsp标签在JavaScript中使用时,可能会出现的一个问题。
  8. 2020年什么编程语言最受欢迎,待遇最高?
  9. 大数据预测实战-随机森林预测实战(四)-模型调参
  10. 前端界面请假管理java_小程序新请假界面
  11. 常见移动机器人轮直径校准实现(ROS)方法
  12. 服务器增加驱动器,向存储空间直通添加服务器或驱动器
  13. 限制新闻标题字数输出并用省略号代替
  14. 水面反光如何拍摄_「摄影技巧全解」「水面篇」不一样的水面粼粼波光
  15. 网络型多媒体计算机教室功能是,多媒体网络教室中的信息技术教学
  16. Minecraft Java版1.11_Editing Java版1.11-pre1
  17. 潜艇空气独立推进系统的全球与中国市场2022-2028年:技术、参与者、趋势、市场规模及占有率研究报告
  18. 积分商城系统业务逻辑思维导图_怎么开发积分商城系统_OctShop
  19. 淘淘商城第51讲——从商城首页跳转到搜索页面
  20. 【解决Hibernate异常 identifier of an instance of xxx(实体类) was altered from xxx to xxxPK】

热门文章

  1. html+css基础笔记_CSS样式_part1_2
  2. kylin-1.6.0单节点安装
  3. nw.js node-webkit系列(22).nw执行过程及如何获取.nw真实路径
  4. 合成海报的小程序插件-票圈海报
  5. cam350菜单怎么切换成中文_电子设计软件CAM350各菜单使用说明(二)
  6. 《你好,数智新世界》系列报道丨对话云徙包志刚:逐梦营销数字化一哥
  7. 全速率话音信道matlab,在全部为全速率话音信道情况下,每个载频的话音信道数为:()。...
  8. 《MongoDB入门教程》第14篇 CRUD之更新文档
  9. ACPI基础——ASL语言
  10. git撤销文件的修改