![零基础入门Python游戏](https://wfqqreader-1252317822.image.myqcloud.com/cover/70/44510070/b_44510070.jpg)
2.14 碰撞检测
除了定义了Sprite类、Group类以外,pygame.sprite模块还提供了一些功能函数用于碰撞检测。
1. 碰撞检测函数
pygame.sprite模块提供了三个函数用于Sprite之间的碰撞检测,它们是:
![](https://epubservercos.yuewen.com/31D206/23721606009501206/epubprivate/OEBPS/Images/Figure-P70_2541.jpg?sign=1739277014-LPCIFxnyhXe1YlaUbm32zDDoGW8LOYdX-0-5d378120e3705fa1ccf0c7cb414620a3)
该函数检测参数sprite与参数group中的哪些Sprite发生碰撞,并返回group中发生碰撞的Sprite列表。参数dokill如果为True,则表示把发生碰撞的Sprite从group中删除。最后的参数collided为一个回调函数,用来设置检测模式,计算两个Sprite之间如何发生碰撞,关于它的具体用法,将在本书后面讲解。
![](https://epubservercos.yuewen.com/31D206/23721606009501206/epubprivate/OEBPS/Images/Figure-P70_2550.jpg?sign=1739277014-TPh1ca5zEXOuifsMVyphnZjFWxq06LXV-0-e9c32573d1f66030254c05f8c73f51a6)
该函数与spritecollide()的功能类似,只不过它仅返回group中第一个与参数sprite发生碰撞的Sprite,并且它没有dokill参数。与spritecollide()相比,它更加轻量级,所以如果不需要那么多功能,则可以选择使用该函数而非spritecollide()。这里的参数collided与spritecollide()中的参数collided相同,也用来设置检测模式。
![](https://epubservercos.yuewen.com/31D206/23721606009501206/epubprivate/OEBPS/Images/Figure-P71_2562.jpg?sign=1739277014-zrEF0iPH7iEkRRvgqXJZuhX1qAaiBOro-0-3f7156b4d0b17e3908af881c80de5272)
前面两个函数都用来检测Sprite与Group之间的碰撞,而该函数则用来检测两个Group之间的碰撞情况,它返回的是一个字典,字典的key为参数groupa中的Sprite,value为参数groupb中与该Sprite发生碰撞的所有Sprite的列表。参数dokilla表示是否把发生碰撞的Sprite从groupa中删除;参数dokillb表示是否把发生碰撞的Sprite从groupb中删除;参数collided与前面函数中的意义相同。
2. 碰撞检测模式
上面的三个碰撞检测函数都用collided作为参数,现在详细介绍一下collided回调函数。
参数collided代表了碰撞检测的模式,表示以什么样的方式检测两个Sprite之间的碰撞。pygame.sprite模块为collided预置了五个可选的回调函数,它们分别是:
![](https://epubservercos.yuewen.com/31D206/23721606009501206/epubprivate/OEBPS/Images/Figure-P71_2571.jpg?sign=1739277014-4YlI9CvPJMyXnQoU4fZXc0kqDn5v8dtH-0-c3872dc55cb0f84d05473eba97406fc0)
通过检测两个Sprite的rect属性是否有重合判断碰撞情况,这也是默认的检测模式,当碰撞检测函数的collided参数为None时,就是使用了这种检测模式。此时,两个Sprite必须都要包含rect属性。
![](https://epubservercos.yuewen.com/31D206/23721606009501206/epubprivate/OEBPS/Images/Figure-P71_2580.jpg?sign=1739277014-mn4hSlT3bEHK63rjfHU0sMJZXAx4H9np-0-3e8bd4f0969beeffe77c457fc28a3cc2)
与collide_rect类似,只不过可以按比例缩放待检测Sprite的矩形区域。参数ratio为一个浮点数,1.0为原始尺寸,2.0为放大一倍,0.5为缩小1/2。
![](https://epubservercos.yuewen.com/31D206/23721606009501206/epubprivate/OEBPS/Images/Figure-P71_2589.jpg?sign=1739277014-xj9mH9sRts1X27BB4ek3KVJ5JR71oFYC-0-7c243e764dcf5541878f80a9f1126a48)
通过检测两个Sprite所在的圆形区域是否有重合判断碰撞情况。如果Sprite有radius属性,那么将根据该属性的值决定圆形的大小;如果Sprite没有radius属性,只有rect属性,那么该圆形将取自rect矩形的外接圆;其中,该圆形的圆心为Sprite的中心点。所以,如果使用该模式,则Sprite必须有rect属性,radius属性为可选。
![](https://epubservercos.yuewen.com/31D206/23721606009501206/epubprivate/OEBPS/Images/Figure-P71_2597.jpg?sign=1739277014-kJlvhr3yW1IGIQKpq50cZzEJU2Y9TLTr-0-654527443b95fd11862e7e06c8ab9496)
与collide_circle类似,只不过它可以按比例缩放待检测Sprite的圆形区域。参数ratio同样也为一个浮点数。
![](https://epubservercos.yuewen.com/31D206/23721606009501206/epubprivate/OEBPS/Images/Figure-P71_2606.jpg?sign=1739277014-KS0VrCV8gMUjYUUHnkzYXdl4IZVgD3k9-0-a1fbbd44b057c87def50604e6da048df)
通过检测两个Sprite的位掩码(bitmask)是否重合判断碰撞情况。如果Sprite有mask属性,那么该mask即是Sprite的位掩码;如果Sprite没有mask属性,那么将利用Sprite的image属性自动创建mask。所以在使用这种模式时,image是Sprite必须有的属性,mask是可选属性。
不过,基于性能考虑,有必要提前计算Sprite的mask,而不是每次检测时都要重新从image中创建mask。一般利用下面的函数从Sprite的image属性中创建mask:
![](https://epubservercos.yuewen.com/31D206/23721606009501206/epubprivate/OEBPS/Images/Figure-P72_10370.jpg?sign=1739277014-onqYFHVyw5EhiqNMFm8jkb6EorgHtyr6-0-75f1c2c7f8ea830d4ae804b3a634d350)
这里所谓的mask或者bitmask其实标记的是Sprite图片中的不透明部分。通过检测图片的不透明部分是否有重合可以实现Sprite的完美碰撞检测,从而有效避免看似没有碰撞却检测到碰撞的情况的发生。
总结一下,在考虑使用哪种检测模式时,可以根据Sprite图片的内容进行选择。不过collide_mask应该是最常用的,它可以完美地实现大多数场合中的碰撞检测。
3. 具体示例
最后是一个完整的示例。让我们一起完善前面Sprite小节中的程序,为它添加碰撞检测功能,让小狗在遇到骨头时就把它们吃掉。
如图2-18所示,这是程序运行中的一幅截图。
![](https://epubservercos.yuewen.com/31D206/23721606009501206/epubprivate/OEBPS/Images/Figure-P72_2626.jpg?sign=1739277014-dXJWKzbfNxgv5iubyN9fUBntItDzynrs-0-0371828d5335eb0e00832c8cf096f7fa)
图2-18 碰撞检测示例程序
可见,与小狗发生碰撞的一块骨头消失不见了。
完整代码如下。
![](https://epubservercos.yuewen.com/31D206/23721606009501206/epubprivate/OEBPS/Images/Figure-P72_10371.jpg?sign=1739277014-EQ4rPQEUW9LZWocRdXLejH6bMmS3Zrd0-0-4352c57319b568e0e931e7c72bb37819)
在上面的程序中,标记黑色的代码是与之前的Sprite示例程序相比新增的部分,其余的代码与Sprite示例程序中的一致。在这里,小狗与骨头之间的碰撞检测使用的是spritecollide()函数,它使用collide_mask作为检测模式,所以需要在Dog类和Bone类中计算相应的mask属性。让小狗把骨头吃掉其实就是让两者在发生碰撞时把骨头从骨头Group中删除,所以这里需要为spritecollide()函数的dokill参数赋予值True。