Oh!Coder

Coding Life

Box2D C++ 教程-幽灵顶点

| Comments

声明:本文翻译自Box2D C++ tutorial-Ghost vertices,仅供学习参考。

题外话:因为在Box2D的用户手册的第四章中,我把ghost vertices翻译成了幽灵顶点,所以这里还延续这个翻译吧,如果有人有更恰当的翻译,指出来我会改正,:)。好了,下面开始正文。

(注意:本话题接下来要说的内容和其它教程并不太一样,因为其它教程是基于Box2D v2.1.2版本的,此版本没有边缘形状(译者注:edge shapes,以后简称为边缘)这个概念。我刚开始写这个话题的时候差点忽略了这个问题。本次话题的讨论基于Box2D v2.2版本。)

幽灵顶点(Ghost vertices)

在’跳跃问题‘这个话题中我曾经说过,对于大多数学习Box2D的新同学来说,最常问的问题就是“如何告诉我的游戏角色当前正站在地面上?”。现在看来,在这问题上貌似是我错了…由此也引出我们今天的这个话题。

最近出现一个常见的问题是“为什么我的角色在平坦的地面上移动的时候会出现一卡一卡的效果?”。当用一个矩形定制器沿着以多个定制器(多边形或边缘)依次连接组成的平面上进行平移的时候,这种情况确实会发生。

上面这样做反过来通常会引起一些副作用,就像在固定的网格里叠加排放瓷砖一样,有很多简单的办法来完成这项工作,但是在展现这样的平面的时候至少应该保持表面的光滑(一个专用的工具Box2D编辑器或许可以更多的帮助你完成这项工作)。

让我们看下在典型情况下,问题出现在了哪里,以及为什么会出现这种情况,之后会看到有两种方法来解决这个问题。一种办法就是调整形状的布局,虽然这个办法看起来有点笨拙,但是很多情况下这么做的效果还是不错的。另外一种方法就是使用本次话题中所讨论的’幽灵顶点’。

为什么角色会卡一下?

下面这张图展示的就是问题出现时的场景。动态盒子可以看作是玩家或者是敌人正在向右移动。当动态盒子从地面上左边的盒子向右边的盒子移动的时候,会被弹起来,卡一下的问题看起来好像出现在动态盒子的右下方。

pic

碰撞剖析话题中,我们得知Box2D是通过计算两个定制器的重叠来感知碰撞的,然后以最快的速度将它们分开。对于这里的讨论,关键点是要注意由这两个碰撞的点组成的小世界。碰撞并没有在三个定制器之间进行解析,也没有在四个之间进行解析。当然了,更不会是五个。

所以其实有两个独立的碰撞分别被独立的进行了解析:

pic

在每一次的计算中,会以最快的速度将重叠的定制器分开。在左边的例子中,几乎可以确定受力情况是’玩家’向上同时地面向下:

pic

例子中右边的情况就不会有想当然的那么简单,因为角和角之间的碰撞是最复杂的情况。让我们再次回顾一下碰撞剖析话题,定制器之间小的位移也会引起大的冲量变化,来通过计算让物体分开。当’玩家’沿着地面进行平移的时候,其实会在地面的定制器表面持续的发生上下浮动很小的碰撞。下面这张图用夸张的标示进行了展示:

pic

所以当玩家和新的地面盒子发生碰撞的那一刻,有可能会发生两种情况中的一种。放大角与角之间碰撞的那张图,我们可以清楚的看到这两种情况…

pic

…如果玩家已经和新的地面盒子水平方向有交叉并在垂直方向部分有沉浸,我们可以通过左边的图看到。但是玩家如何垂直方向的沉浸程度比水平方向大,右边的图可以看到这种情况,这两种情况表明了为什么当玩家水平在地面上平移的时候瞬间会出现卡顿的现象。

解决问题-裁剪多边形角

正如上面我们看到的,右边的那张图中,我们可以看到当两个物体中的边角发生碰撞时会产生方向相反的冲量让两个物体水平弹开,但反弹的方向并不是我们希望看到的。为了解决这个问题,我们需要把玩家右下角进行裁减,当然也可以是地面物体的左上方的角,也可以是发生碰撞的两个物体的角同时进行裁减。例如,像下面这张图中所示的情况就会很好:

pic

对于多数情况,把玩家像这样做一下改进就可以了。这种方式也比较容易修改。

当然了,如果你把玩家角色做成了环形,那更好。

解决问题-边缘形状(edge shapes)

(注意:Box2D v2.1.2不包括边缘形状)

使用边缘(edge shapes)相比使用多边形有很大的改进。一个边缘简单来说就是两个点之间的一条线,使用这种方式可以边与边连接起来当作地面,可以实现相同的效果,然后把盒子放到上面。理论上来说,即便如此使用边缘按说也会导致和多边形一样的冲量问题,但是实践中证明,可能由于某些原因,发生这种情况的频率非常小。我真的是不太清楚其中的原因,如果你知道,希望能告诉我们大家:) 。

一旦你使用边缘替换掉多边形,就不用再担心8个顶点(Box2D 默认)的凹凸多边形问题。当你需要一个光滑的并且具有一定面积的地面时,可以用一个完整的边缘替换掉多个细小的边缘。

真正解决问题-幽灵顶点(ghost vertices)

(注意:Box2D v2.1.2不包括边缘形状)

Box2D v2.2中针对边缘(edge shapes)介绍了’幽灵顶点(ghost vertices)’这个概念。很明显的是,边缘有两个主要的顶点用来定义其位置,之后可以探测边缘是否与某些东西发生碰撞。主要的顶点除此以外可以有一个幽灵顶点,像下面这幅图一样:

pic

幽灵顶点之所以而得名,是因为它们在碰撞过程中并不扮演什么角色-其它物体可以在不发生碰撞的情况下,很开心的穿过v0~v1和v2~v3组成的边缘。那么…幽灵边缘有什么作用呢?作用在于当发生碰撞的时候它们可以产生碰撞响应。

比如说当在边缘的底部v2点检测到碰撞时,v1~v2和v2~v3两条线用来计算碰撞响应产生的冲量。注意了,引起卡顿问题的原因不就是因为同一时间解析计算碰撞的时候有超过一个以上的定制器而造成的吗?本质上来说,这正是幽灵顶点系统为我们所作的工作。碰撞响应的结果看起来就像v2点不存在不连续的情况。

通过使用b2ChainShape能够自动的管理所有幽灵顶点,以此可以实现一个连续的边缘串,而且这种方法的使用非常普遍。将每一个边缘上的幽灵顶点与主顶点进行匹配。如果你需要使用幽灵顶点设置单个边缘,需要像下面这样:

1
2
3
4
5
6
b2EdgeShape edgeShape;
edgeShape.Set( v1, v2 );
edgeShape.m_vertex0.Set( v0 );
edgeShape.m_vertex3.Set( v3 );
edgeShape.m_hasVertex0 = true;
edgeShape.m_hasVertex3 = true;

…上面那幅图中的顶点序号从哪来的?注意边缘的主顶点v1~v2在中间,幽灵顶点分别在边缘的两侧,是v0和v3点。

其它需要记住的事情

有一个需要注意的地方是,当使用幽灵顶点组成平滑的边缘时,有可能会让边缘相比较’player’来说非常小,无论怎样它们之间都会发生碰撞。比如说,如果一个碰撞发生在边缘终点v2,而此时v2和v3组成的边缘由于相对于玩家来说太小,而导致完全进入玩家定制器内,那么此时幽灵系统就酸失败了。这里有一个不错的例子,地面由非常小的边缘组成,此时的边缘就应该修改的大一点(红点是组成地面表面的顶点)。

pic

源代码

相比余下的话题而言,既然这里更多需要的是Box2D最近的版本,我就把源代码单独放到这里。这里是testbed框架中的’test’例子。iforce2d_ghost_vertices.h

最后,这里有一个视频(译者注:为了避免大陆朋友打开博客受影响,此处改成了一张图片,这里给出YouTube上的视频网址,能翻墙的同学可以看下,不能翻墙的同学应该学习一下如何翻墙,:P)可以参考比较一下,地面为多边形(下层),没有幽灵顶点的边缘(中间),带幽灵顶点的边缘(上层)。基本使用多边形,我发现’地面上卡一下’的情况也是非常罕见的,事实上为此能做出一个视频就更罕见!所以在演示的例子中,地面做动态的,以此降低碰撞速率,提高出现卡一下效果的概率,玩家的下半身做的很轻,上半身做的很重(相比有50倍的密度),然后用很大的力向下作用在玩家的上半身上。

pic

即便是这样,现象仍然比较罕见,所以我还施加了向下的冲量以便让玩家与其中某个’插入点’离的更近。所作的一切努力都是为了让玩家离非幽灵点更近一点(译者注:我想就是让玩家的身体尽量插入非幽灵顶点,以此尽可能的出现碰撞效果),大部分时候不会出现碰撞现象。但是如果你想要一个万无一失的解决方案,或许应该选择幽灵顶点方案,不管出现怎样恐怖的干预方式,玩家永远都不会再次出现碰撞。

Comments