![零基础入门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=1739295837-eO6q4GPNg1YgAAvdGuI3ieiOtdLmVQtm-0-2fc7dd9e2228bf1970d22784a0cb4deb)
该函数检测参数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=1739295837-RLKAgPREg5GPF9zegbJvTbVKc4miIVHA-0-c0b9d3b6c2c326f734194b2f22b81522)
该函数与spritecollide()的功能类似,只不过它仅返回group中第一个与参数sprite发生碰撞的Sprite,并且它没有dokill参数。与spritecollide()相比,它更加轻量级,所以如果不需要那么多功能,则可以选择使用该函数而非spritecollide()。这里的参数collided与spritecollide()中的参数collided相同,也用来设置检测模式。
![](https://epubservercos.yuewen.com/31D206/23721606009501206/epubprivate/OEBPS/Images/Figure-P71_2562.jpg?sign=1739295837-Ev7qf4yG1r4hQk0oKa34B5RPuK7Xe3O0-0-411db37620d4dc6199c89806943ce67f)
前面两个函数都用来检测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=1739295837-wSSMIWHPo8SPqjXbJx63WVpQK2Ms9bos-0-c0b31ad8c126db5de9d1c79468c602e0)
通过检测两个Sprite的rect属性是否有重合判断碰撞情况,这也是默认的检测模式,当碰撞检测函数的collided参数为None时,就是使用了这种检测模式。此时,两个Sprite必须都要包含rect属性。
![](https://epubservercos.yuewen.com/31D206/23721606009501206/epubprivate/OEBPS/Images/Figure-P71_2580.jpg?sign=1739295837-HCYmxOxDEY1SSUG0MgFC21EwxwA9xBwk-0-9e0e2949e630d2711a1f863790ebc792)
与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=1739295837-L21vxO3fVe9kS3OSptpdUUWJu3AQpUnf-0-5504bbfbdbeb38e0b3431160125ff8e1)
通过检测两个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=1739295837-018q5xDXlrEaxxytVmcexxAZS6v2W3A6-0-3d932cd9f2ab7e5ca2c25d887573b2b5)
与collide_circle类似,只不过它可以按比例缩放待检测Sprite的圆形区域。参数ratio同样也为一个浮点数。
![](https://epubservercos.yuewen.com/31D206/23721606009501206/epubprivate/OEBPS/Images/Figure-P71_2606.jpg?sign=1739295837-JtWSuQSAmgeUA5b4KgO1ydQc8OMeUk3l-0-a6ab2187fafaae968ee29deb7f678a08)
通过检测两个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=1739295837-3PHtSUyBhbEM5r4LMHftf8ARWKorNY5O-0-d0e4c3117ecf80f44f7e37d8dd56db3f)
这里所谓的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=1739295837-Y1DiQuZVlWUMFiudKZObySJM0laU2fiX-0-10a360f5e06d03a4f8d474c1126dbca7)
图2-18 碰撞检测示例程序
可见,与小狗发生碰撞的一块骨头消失不见了。
完整代码如下。
![](https://epubservercos.yuewen.com/31D206/23721606009501206/epubprivate/OEBPS/Images/Figure-P72_10371.jpg?sign=1739295837-R8aGUi2F3xFdhxjDoJugUbO3yIvf6zaA-0-ca63971a9d4359abf41a0cfb24a70e74)
在上面的程序中,标记黑色的代码是与之前的Sprite示例程序相比新增的部分,其余的代码与Sprite示例程序中的一致。在这里,小狗与骨头之间的碰撞检测使用的是spritecollide()函数,它使用collide_mask作为检测模式,所以需要在Dog类和Bone类中计算相应的mask属性。让小狗把骨头吃掉其实就是让两者在发生碰撞时把骨头从骨头Group中删除,所以这里需要为spritecollide()函数的dokill参数赋予值True。