首页 » 与孩子一起学编程 » 与孩子一起学编程全文在线阅读

《与孩子一起学编程》17.2 嘣 ! 碰撞检测

关灯直达底部

大多数计算机游戏中,你需要知道一个动画精灵在什么时候碰到另一个精灵。例如,可能需要知道保龄球何时碰到球瓶,或者导弹什么时候击中飞船。

你可能认为,如果我们知道每个动画精灵的位置和大小,可以写一些代码对每一个其他动画精灵的位置和大小进行检查,看哪里出现了重叠。不过,编写 Pygame 的人已经为我们完成了这项工作。Pygame 中已经内置有这种碰撞检测。

术语箱
简单地说,碰撞检测(collision detection)指的是了解两个动画精灵何时接触或重叠。两个移动的东西相互碰到一起,这就是一个碰撞(collision)。

Pygame 还提供了一种方法对动画精灵分组。例如,在保龄球游戏中,所有球瓶可能在一组,球则在另一组。

组和碰撞检测密切相关。在保龄球例子中,你可能想检测球何时击倒某个瓶子,因此要寻找球精灵与球瓶组中所有精灵之间的碰撞。还可以检测组内部的碰撞(如球瓶相互碰倒)。

下面来完成一个例子。以我们的反弹沙滩球为基础,不过为了更容易地看出发 生了什么,这里首先建立 4 个球而不是 9 个球。另外与上一个例子中建立球的列表不同,我们将会使用 Pygame 的 group 类。

这里还要对代码稍稍做些整理,把完成球动画的部分(代码清单 17-2 中的最后几行)放在一个函数中,我们把这个函数命名为 animateanimate 函数还包括完成碰撞检测的代码。两个球碰撞时,我们会让它们反向。

代码清单 17-3 显示了相应的代码。

代码清单 17-3 使用一个动画精灵组而不是列表

这里最有意思的新内容是碰撞检测如何工作。Pygame sprite 模块有一个 spritecollide 函数,它会查找一个精灵与一个组中所有精灵之间的碰撞。要检查同一个组中精灵之间的碰撞,必须通过 3 步来完成:

 
  • 首先,从这个组中删除这个精灵;

  • 其次,检查这个精灵与组中其他精灵之间的碰撞;

  • 最后,再把这个精灵添加回原来的组中。

这些工作在第 23 行到第 29 行的 for 循环中(animate 函数的中间部分)完成。如果开始时没有从组中删除这个精灵,spritecollide 会检测到这个精灵与它自身发生了碰撞,因为它也在这个组中。乍一看好像有些奇怪,不过如果再想想看就会发现这是有道理的。

运行程序,看看有什么结果。有没有注意到一些奇怪的行为?我注意到两点:

 
  • 球碰撞时,它们会“颤抖”或者发生两次碰撞;

  • 有时球会“卡”在窗口边界上,颤抖一段时间。

如果加大动画步,就能更容易地看到这一点。可以把速度从 2 增加到 5,另外把各步之间的延迟从 20 增加到 50。

为什么会出现这种情况?嗯,这与我们如何编写 animate 函数有关。注意我们的做法是先移动一个球,检查它的碰撞,然后移动另一个球,再检查这个球的碰撞,依此类推。也许应该先完成所有移动,然后再完成全部碰撞检测。

所以需要把第 31 行(ball.move)放在它自己的循环中,就像这样:

试试看,效果是不是比原来好一些。

可以对这个代码做些试验,改变某些值,比如速度(time.delay 数)、球数、球原先的位置、随机性等等,来看球会有什么变化。

矩形碰撞与像素完美碰撞

你会注意到,球“碰撞”时并不总是完全接触。这是因为 spritecollide 没有使用球的圆形轮廓来检测碰撞。它使用了球的 rect,也就是球的外围矩形。

如果想看看具体是怎样的,可以画一个矩形包围球图像,并且使用这个新图像而不是原先常规的沙滩球图像。我已经为你做好了这个新图像,你可以试一试:

img_file = “b_ball_rect.png”

看上去就像右图显示的这样:

如果希望球的圆形部分(而不是矩形边界)真正接触时球才会相互反弹,就必须使用一种称为“像素完美碰撞检测”的方法。spritecollide 函数没有这样做,而是使用了更简单的“矩形碰撞检测”。

它们的区别如下。使用矩形碰撞检测,两个球矩形区的任何部分相互接触时就会“碰撞”。而使用像素完美碰撞检测,两个球本身接触时才会碰撞。如下:

像素完美碰撞检测更逼真。(在真正的沙滩球周围你不会觉得有任何隐形的矩形,对吧?)但是在程序中实现时就没这么简单了。

对于要在 Pygame 中完成的大多数工作来说,矩形碰撞检测已经足够了。像素完美碰撞检测需要写更多代码,而且会让游戏运行得更慢,所以应该只有在确实有必要的情况下才使用这种方法。完成像素完美碰撞检测有一个单独的模块,不过我们不会在这里使用。这个模块可以在 http://arainyday.se/projects/python/PixelPerfect/ 或本 书的网站上找到。