文/Steffen Itterheim、Andreas Löw 为了更好地使用Box2D物理引擎,本文我们将制作一个真实的弹球游戏。弹球游戏桌利用各种物理世界的效果来创造有趣的体验。然而,在使用物理引擎时,并不局限于真实世界中的物理定律。 通过设定合适的摩擦力、弹性和密度参数,可以创建出弹球游戏中的一些元素,如反弹器(bumper)和球 (ball) 。其他的元素则需要使用关节 (joint) :挡板(flipper)需要转动关节(revolute joint),发射器(plunger)需要移动关节(prismatic joint)。当然还需要一些静态图形来定义弹球桌上的碰撞多边形。

由于用代码来定义碰撞多边形是不切实际的,至少对于一个逼真的弹球游戏所需的复杂度来说是这样,因此下面介绍另一个非常有用的工具—— PhysicsEditor 。依靠这个工具,只须画出一个个顶点就可以创建碰撞多边形。更快的做法是单击一次鼠标,让 PhysicsEditor 跟踪形状的轮廓。 [caption id="attachment_14056" align="aligncenter" width="229" caption="图1 弹球游戏"]
[/caption]
图形:凸多边形和逆时针方式 我们先从碰撞多边形的要求说起。首先需要知道的是,在Box2D和Chipmunk物理引擎中定义碰撞多边形时,需要遵循以下两条原则: ● 逆时针定义各个顶点 ● 多边形必须是凸多边形 凸多边形是指图形上任意两点的连线都在图形内部。这和凹多边形正好相反,凹多边形中两点的连线可以不完全包含在图形内部。图2将有助于你理解凸多边形和凹多边形的区别。 [caption id="attachment_14057" align="aligncenter" width="406" caption="图2 凸多边形和凹多边形"]
[/caption] 可以在心中画一下,就能明白如何按逆时针方向定义凸多边形的顶点。首先在任意位置放置一个顶点,然后在它的左边放置另一个顶点,接着是下面,最后再回到右面。这样就用逆时针方式绘制了一个长方形。或者也可以先放置一个顶点,然后在右边、上面、左边绘制其他
3 个顶点,这样就绘制出一个以逆时针方式定义的图形。在哪里绘制第一个顶点并不重要,重要的是顶点要沿逆时针方向绘制。 好消息是,在使用 PhysicsEditor 时,你不需要关心多边形的顶点顺序 ( 方向 ) ,或者多边形是凸多边形还是凹多边形。 PhysicsEditor 会自动处理这些问题。它把凹多边形分割为一个或更多个凸多边形。然后, PhysicsEditor 自带的物理对象加载器会把所有图形分配给单个 Box2D 刚体。应该尽力避免分割图形,以便使每个刚体的碰撞形状最少,从而获得最佳的性能。 提示: 如何才能知道是否错误地创建了一个顺时针图形或凹多边形?每个物理引擎的反应都不一样。有些会事先抛出错误来告知。但是在Box2D中,如果一个移动的刚体碰到上述错误的多边形,这个刚体会在接近那个多边形的时候停下来。如果在Box2D游戏中碰到类似的情况,请检查一下周围的碰撞多边形。 使用PhysicsEditor 知道了如何定义碰撞多边形,现在是时候来学习 PhysicsEditor 工具了。该工具可从 www. physicseditor.de 下载。下载完成后,打开 PhysicsEditor 磁盘镜像,并把 PhysicsEditor.app拖动到应用程序的文件夹中,就可以运行 PhysicsEditor 了 ( 见图 3) 。在 PhysicsEditor 磁盘镜像中,可以找到一个名为 Loaders 的文件夹,其中包含了由 PhysicsEditor 创建的 Box2D 和 Chipmunk plist 文件的加载器代码 ( 图形缓存 ) 。本章的示例项目中将使用 GB2ShapeCache 类来加载 PhysicsEditor 创建的图形。 [caption id="attachment_14058" align="aligncenter" width="423" caption="图3 PhysicsEditor应用程序"]
[/caption] 现在应该把
PhysicsBox2D03 项目的 Assets/pinball 文件夹中的 PNG 文件拖放到 PhysicsEditor 中最左边的 Shapes 窗格中。 注意: 创建物理图形时将只使用HD分辨率的图像,而不需要分别创建HD和SD分辨率的物理图形。物理模拟世界与物体的图形表示无关,所以与屏幕分辨率也无关。 在 PhysicsEditor 中,首先要修改导出器的设置。 PhysicsEditor 可以导出为多个游戏引擎的格式,支持 Box2D 和 ChipMunk 物理引擎,甚至允许创建自定义导出格式。要编写与 cocos2d 兼容的文件,必须将最右边窗格中的 Exporter 设置为 Box2D generic(PLIST) 。导出器根据目标物理引擎的功能启用或禁用 PhysicsEditor GUI 的某些功能,所以首先设置导出器很重要。 接下来应该将 PTM-Ratio 设置为 240 。该值的单位为每米的像素数,意味着 240 个像素等于 Box2D 物理模拟世界中的 1 米。因为 Box2D 经过了优化,最适合处理 1 到 10 米大小的物体,所以 Box2D 物理世界的尺寸很重要。用更大或更小的物体进行模拟也很容易,但是在物体非常大 ( 几十米甚至几百米 ) 或非常小 ( 零点几米 ) 时, Box2D 的精度会降低,并可能呈现奇怪的行为。 我们在 PhysicsEditor 中使用的是高分辨率图像,所以 PTM-Ratio 为 240 将创建一个高 4 米 (Retina 显示屏幕的分辨率 960 除以 240) 、宽 2.6 米 (Retina 显示屏幕分辨率 640 除以 240) 的弹球桌。 cocos2d 中像素与米的实际比率是 PhysicsEditor 中 PTM-Ratio 设置的一半,在这里就是每米 120 个像素。这是因为 cocos2d 的坐标系使用点作为单位,所以标准分辨率显示屏幕和 Retina 显示屏幕的尺寸是相同的,均为 320 × 480 个点。 1 个点在标准分辨率显示屏幕上为 1 个像素,在 Retina 显示屏幕上为 2 个像素。在 Box2D 物理世界中,弹球桌的尺寸不受实际屏幕分辨率的影响。如果只使用标准分辨率的图像,而禁用了对 Retina 显示屏幕的支持,那么 PhysicsEditor 中的 PTM-Ratio 设置将与 cocos2d 相同。 定义发射器形状 首先设置发射器,即把球弹到游戏区的弹簧。在最左边的窗格中选择发射器图像,然后在中间视图的工具栏上单击“添加多边形”按钮。这会在中央工作区创建一个新的三角形并选中它。因为我们需要一个矩形,所以单击某个边,添加第 4 个顶点。如果添加了过多的顶点,可以右击或者在按住 Option 键的同时单击一个顶点,然后选择 Delete point 删除该顶点。 应该把 4 个顶点拖放到发射器的 4 个角上。创建一个包围整个发射器的矩形,将弹簧包含在内。这可以避免球偶然落入发射器内出现的问题。 你可能已经注意到了那个包含加号的小蓝圆圈,它是形状的定位点,刚好与形状精灵的定位点相同。后面我们在 cocos2d 中定位形状时,将使形状的定位点在我们提供的坐标位置居中。 我们要把定位点放到发射器底部的中心位置,以便简化定位发射器的工作。拖动蓝色圆圈是可以的,但是在许多时候这样做不够精确。此时,可以在 Image Parameters 下的 Parameters 窗格中修改定位点的绝对像素位置或相对位置。 在图 4 中,可以看到正在编辑的发射器形状。 [caption id="attachment_14061" align="aligncenter" width="499" caption="图4 在PhysicsEditor中手动定义形状"]
[/caption] 如果不想从多边形创建形状,也可以使用“添加矩形”按钮,但是这样一来,我就无法解释如何添加或删除顶点,以及如何拖动它们了。 还要在
Parameters 窗格的 Fixture parameters 部分设置发射器的碰撞位。这些碰撞位设定了哪些形状会碰撞,哪些不会。它们还用于防止发射器与球以外的其他形状发生碰撞。 为了简化碰撞位的使用,可以改变它们的名称。这些名称只是用于提醒你自己各个位的用途,它们不会被导出。默认情况下,这些位的名称为从 bit_0 到 bit_15 。这里要将前 5 个位的名称改为 Ball 、 Bumper 、 Flipper 、 Plunger 和 Wall 。 只有当两个形状的类别位 ( 标记为 Cat 的复选框列 ) 和掩码位 ( 标记为 Mask 的复选框列 ) 都被选中时, Box2D 形状才会发生碰撞。通常会把每个形状分配给某个特定的类别。就发射器而言,只将 Plunger 类别的类别位置位。换句话说,是将发射器的形状分配到了 Plunger 掩码位类别中。然后使用 Mask 复选框指定这个形状可以与其他哪些类别发生碰撞。对于发射器,应只设置 Ball 类别的 Mask 复选框,因为只允许发射器与球发生碰撞。图 5 显示了 Plunger 碰撞类别和掩码标志的正确设置。 [caption id="attachment_14062" align="aligncenter" width="293" caption="图5 发射器的碰撞参数"]
[/caption]   注意: 到目前为止,发射器可以与球发生碰撞,但是球不会与发射器发生碰撞。必须记住,碰撞的定义是一个双向的过程,在本例中还要把球放到
Ball 类别中,并为球选中与 Plunger 类别对应的 Mask 位,这样球和发射器就可以彼此发生碰撞了。 还可以为相同的类别设置掩码位,从而允许相同类别的多个对象彼此发生碰撞。对于球,将 Ball 类别的 Mask 位置位很合理,因为球与球可以发生碰撞。当想要扩展弹球游戏,以支持在弹球桌上同时有多个球的情况时,这么做很有帮助。 在 Cat 和 Mask 类别的底部会看到 All 、 None 和 Inv 按钮,它们分别用于选中全部复选框、清除所有复选框以及反转复选框的选中状态。使用它们可以避免连续选中几十个复选框的情况。

定义弹球桌形状

弹球桌包含 3 个单独的形状,分别叫做 table-bottom 、 table-left 和 table-top 。弹球桌图像被分割开,以方便编辑形状,并且这样一来,不必替换整张图像就可以创建不同的弹球游戏布局。 在 Shapes 窗格中选择 table-top 图像,开始编辑弹球桌最上边的部分。从图 6 可以看到,这是一个凹多边形。前面手动定义了发射器,但是手动地创建这个半圆形的所有顶点十分困难且容易出错,更别说保证得到的形状是凸多边形了。使用 PhysicsEditor 可以大大简化这些工作:只要用鼠标单击一次,它就会跟踪形状的轮廓,并创建一个合适的形状。 在中心窗格的工具栏上有一个魔棒图标,叫做 Shape Tracer 。单击该图标将打开如图 6 所示的 Shape Tracer 对话框。 [caption id="attachment_14084" align="aligncenter" width="369" caption="图6 Shape Tracer会自动创建形状"]
[/caption]   Shape Tracer
显示了单击 OK 按钮后它会创建的形状的图像以及形状的覆盖层。在图像下方有一个滑动条,两侧有一些按钮,控制图像的缩放级别。图像的缩放设置对创建的形状没有影响。 在 Shape Tracer 中要调整的最重要的一个设置是 Tolerance 。 Tolerance 可以改变在创建形状时图像跟踪操作的精确度,这将直接影响形状中使用的顶点数,进而影响物理模拟的性能。一般来说,应该在实现足够的碰撞响应的前提下,使用尽可能少的顶点。对游戏而言,碰撞的精确度越重要,一些物体需要的顶点数越多。另一方面,如果添加了许多使用同一种形状的物体,那么该形状的顶点数越少,游戏的性能就越高。 通过多次试用 Tolerance 设置,我发现本例中一个不错的折中是将 Tolerance 设为 (4,0) 。这会创建一个顶点数为 18 的形状,而且是能够保证非常精细地跟踪图像形状的 Tolerance 最大值。默认的 Tolerance 设置 (1,0) 会创建一个顶点数为 31 的形状,所以我的设置减少了 3 个顶点。但是如果遍历 Tolerance 值的话会发现,甚至 Tolerance 值 (1,5) 也可以将顶点数减少为 20 。 提示: Shape Tracer 中的 Frame Mode 设置可以用来为动画 ( 一个图像序列 ) 创建形状。要在 PhysicsEditor 中创建形状,需要在默认的 PhysicsEditor 窗口中找到 Parameters 窗格的 Image Parameters 部分,向形状添加多个图像文件。单击 Filename 设置旁边的“ + ”按钮即可向形状添加额外的形状,然后就可以让 Shape Tracer 创建每个动画帧的形状的交集或并集。 对得到的形状感到满意后,单击 OK 按钮关闭 Shape Tracer 对话框。新形状将会创建出来。你仍然需要手动做一些调整,使弹球桌的碰撞更加真实自然。由于屏幕区域将成为弹球桌的碰撞边界,因此应该把弹球桌左下角和右下角的顶点以及左上角和右上角的顶点拖到屏幕稍微靠外的地方,并分别向下和向上拖动一些,使得它们位于 table-top 图像周围的黑色边框区域以外,如图 7 所示。 [caption id="attachment_14085" align="aligncenter" width="525" caption="图7 最终确定弹球桌的table-top形状"]
[/caption]   通过以这种方式处理顶点,形状可以平滑地与屏幕边框衔接起来。否则,由于物理模拟中不可避免的小误差,球可能会在这些顶点位置反弹出去。 现在,使用
Image Parameters 下 Parameters 窗格中的定位点设置把定位点 ( 包含“ + ”的蓝色圆圈 ) 移动到左上角。将 Relative 的值分别设为 (0,0) 和 (1,0) ,将定位点移动到左上角。由于有了这个定位点,后面可以简单地将图像定位到 (0,480) 坐标位置,实现图像与屏幕边框的精确对齐。 注意: 如果看不到定位点圆圈, Image Parameters 下也没有关于定位点的设置,就说明 Parameters窗格中的 Exporter 没有被设为 Box2D generic (PLIST) 格式。 最后一步是调整 table-top 形状的碰撞位。选中 Wall 的类别 (Cat) 复选框,然后选中 Ball 的 Mask 复选框。这样弹球桌就可以和弹球发生碰撞了。相应的,需要设置 table-left 和 table-bottom 形状的相同碰撞位,因为它们都是 Wall 类别的一部分,并且应该能够与 Ball 类别发生碰撞。 使用 Shape Tracer ,以相同的方式为 table-left 图像创建形状。在 Image Parameters 下,将定位点移动到像素坐标 (0,0) 和 (50,0) 。不要忘了像前面的 table-top 那样设置碰撞位。 现在跟踪 table-bottom 图像的形状。需要的步骤要多一些,因为 table-bottom 图像实际上包含 4 个彼此不相连的单独元素,它们都需要有自己的形状。 PhysicsEditor 1.0.4 只能跟踪连续的形状,所以这里就需要打开 Shape Tracer 4 次。每次打开时,单击想要跟踪的图像部分,然后单击 OK 按钮创建形状。对于这 4 个形状,将 Tolerance 设为 (4,0) 的效果都很好。最后得到的 4 个形状就代表了 table-bottom 图像的 4 个元素。不要忘了像 table-top 那样设置碰撞位,如图 7 所示。 定义挡板 选择图像 flipper-left 并打开 Shape Tracer 。 Shape Tracer 的初始建议形状并不合理,实际的形状要大得多。 这是因为这幅图应用了闪光和阴影效果,在形状周围创建出光环,但是在浅色背景中几乎看不到。为使 Shape Tracer 创建更好的形状,需要调整 Alpha threshold 设置。默认值是 0 ,表示在跟踪形状时会考虑所有不完全透明的像素。本例中我们只想考虑形状的完全不透明元素。如图 8 所示,如果将 Alpha threshold 设为 254 ,得到的结果会好很多。 [caption id="attachment_14086" align="aligncenter" width="423" caption="图8 在跟踪挡板形状时,设置好Alpha threshold可以忽略图像的阴影"]
[/caption] 提示: 如果由于某些原因,
Shape Tracer 得到的结果不符合要求,而 Tolerance 和 Alpha threshold 设置都不能解决问题,那么可以在关闭 Shape Tracer 后手动编辑形状。为此,单击任意顶点并进行拖动即可。另外,双击一个顶点可以删除它,双击两个顶点之间的线段可以添加一个新顶点。 为 flipper-right 图像重复相同的过程,并且不要忘了为两幅图像设置碰撞位:选中 Flipper 类别,选中 Ball 的 Mask 复选框。碰撞位的设置参见图 8 。 左挡板的定位点应设为像素坐标位置 (27,78) ,右挡板的定位点应设为像素坐标位置 (97,79) 。 作者Steffen Itterheim,从20世纪90年代开始就一直热衷于游戏开发。他在Doom和Duke Nukem 3D社区表现活跃,并因此获得了他的第一份自由职业,成为3D Realms的一名beta测试人员。作为职业游戏开发者,Steffen拥有10多年的丰富经验,其中大部分时间担任Electronic Arts Phenomic的游戏和工具程序员。2009年Steffen第一次接触cocos2d,那时他与其他人共同创办了一家iOS游戏公司——Fun Armada。他乐于将自己的宝贵经验传授给其他游戏开发者,以帮助他们更上一层楼。有机会你可能会在白天看到他在住所附近茂密的葡萄园周围散步,也可能在晚上看到他在Nevada沙漠收集瓶盖。 作者Andreas Löw,在10岁的时候有了一台Commodore C16,从那时起他就对计算机产生了狂热的兴趣。他自学了编写游戏的技术,并在1994年发布了自己的第一款游戏Gamma Zone,这是一款针对Commodore Amiga平台的游戏,用纯汇编语言编写完成。在获得电子工程学的学位后,他进入Harman International公司,负责为汽车行业开发具有语音识别功能的导航和娱乐系统。他开发了自己的编程语言和开发工具,现在世界上采用语音识别技术的每辆汽车都在使用他的编程语言和开发工具。 本文节选自《iOS 5 cocos2d游戏开发实战》(第2版)一书。(美)伊特海姆 、(德)勒夫著 ; 由清华大学出版社出版。    
Logo

20年前,《新程序员》创刊时,我们的心愿是全面关注程序员成长,中国将拥有新一代世界级的程序员。20年后的今天,我们有了新的使命:助力中国IT技术人成长,成就一亿技术人!

更多推荐