前言
到目前为止,飞船,子弹,外星人的创建,移动都已经完成。接下来我们需要完成功能主要就是子弹击中外星人和飞船接触到外星人两个中功能。
碰撞
我们需要实现的功能中,子弹击中外星人和飞船接触外星人本质上就是两个对象的边界互相接触。如果是单个对象,我们可以使用外接矩形的坐标来进行对象接触的判定,但是子弹和外星人的数量都是多个的,这要怎么判定它们是否接触呢?
在之前子弹与外星人的代码实现中,因为这两个对象的数量都是多个数量,所以我们使用了精灵组的概念,让对应的类继承sprite
,从而实现使用组来控制屏幕中的子弹和外星人。而sprite
中,将这种接触定义为碰撞,并带有处理组之间接触碰撞的函数。
pygame.sprite.spritecollideany
pygame.sprite.spritecollideany
是 Pygame
模块中的一个函数,用于检测两个精灵组之间的碰撞。这个函数会检查给定的精灵是否与另一个精灵组中的任何精灵发生碰撞。
参数说明
- sprite: 你要检测其是否与其他精灵发生碰撞的单个精灵对象。
- group: 一个精灵组,pygame.sprite.Group 对象,其中包含了多个精灵。
- collided: 可选参数,一个回调函数,用于自定义碰撞检测逻辑。如果未提供,则使用默认的矩形碰撞检测。
返回值
- 如果 sprite 与 group 中的任意精灵发生碰撞,则返回第一个发生碰撞的精灵;如果没有发生碰撞,则返回 None。
这个函数是用于单个单个精灵对象与精灵组之间的碰撞。那么在飞船与外星人的碰撞判断中,我们可以使用这个函数。
pygame.sprite.groupcollide
pygame.sprite.groupcollide 是 Pygame 中的一个函数,用于检测两个精灵组之间的碰撞。当你需要检查一组精灵是否与另一组精灵中的任何精灵发生碰撞时,这个函数非常有用。
参数说明
- group1: 第一个精灵组,通常为 pygame.sprite.Group 类型。
- group2: 第二个精灵组,同样为 pygame.sprite.Group 类型。
- dokill1: 布尔值,如果为 True,则在发生碰撞后,group1 中的精灵将被销毁。
- dokill2: 布尔值,如果为 True,则在发生碰撞后,group2 中的精灵将被销毁。
- collided: 可选参数,一个回调函数,用于自定义碰撞检测逻辑。如果未提供,则使用默认的矩形碰撞检测。
返回值 - 函数返回一个字典,其中键是 group1 中的精灵,值是与该精灵发生碰撞的 group2 中的精灵列表。
看看多贴心,甚至将碰撞的精灵(对象)都给你销毁了。
这个函数很明显,就非常适合于子弹与外星人的碰撞处理。
那么,开始进行我们实际的代码编写吧!
子弹击中外星人
作为子弹与外星人的碰撞,子弹是主动的一方,去撞击外星人,所以我们直接在子弹更新的函数中,对其与外星人进行碰撞处理。
那么我们的代码要作出如下改动:
- 在
main
模块中,更新子弹位置的函数需要增加一个外星人组的参数,用与碰撞。 - 在
gf
模块中,更新子弹的函数中,调用函数pygame.sprite.groupcollide
main
模块修改gf.update_bullets()
参数,代码如下:
import pygame
from pygame.sprite import Group
import alien_invasion.game_functions as gf
from alien_invasion.setting import Setting
from alien_invasion.ship import Ship
def run_game():
"""启动游戏"""
--snip--
while True:
# 处理监听事件
gf.check_event(ship, setting, screen, bullets)
# 移动飞船
ship.move()
# 更新子弹位置
gf.update_bullets(bullets, aliens)
# 更新外星人
gf.update_aliens(aliens, setting)
# 刷新屏幕
gf.update_screen(setting, screen, ship, bullets, aliens)
if __name__ == '__main__':
run_game()
gf
模块更新子弹函数处理碰撞,代码如下:
--snip--
def update_bullets(bullets, aliens):
"""更新子弹"""
bullets.update()
# 删除已经消失的子弹
for bullet in bullets.copy():
# 当子弹矩形底部坐标小于0,说明子弹已经出了上边界
if bullet.rect.bottom <= 0:
bullets.remove(bullet)
pygame.sprite.groupcollide(bullets, aliens, True, True)
--snip--
在子弹击中后,子弹与外星人同时消失。
此时运行main
模块,按下空格发射子弹,等待子弹命中外星人,就能发现外星人与子弹同时消失不见。
效果如下:
外星人撞击飞船
通常我们这类的游戏设定,是不允许飞船与外星人的接触的,假如接触到,外星人没影响,飞船可能会损害或者直接坠毁。基于这种逻辑,我们在编写外星人与飞船的碰撞给代码时,将碰撞处理编写在外星人的位置变化中,模拟一种外星人主动去撞击飞船的逻辑(类似子弹主动撞击外星人)。
同样,我们要做出如下更改:
- 在
main
模块中,调用外星人移动的函数,需要添加一个飞船的参数 - 在
gf
模块中,外星人移动的函数中进行碰撞处理 - 在
ship
模块中,编写一个居中飞船的函数,废船碰撞后回到底部中央。
main
模块修改gf.update_aliens()
参数,代码如下:
import pygame
from pygame.sprite import Group
import alien_invasion.game_functions as gf
from alien_invasion.setting import Setting
from alien_invasion.ship import Ship
def run_game():
"""启动游戏"""
--snip--
while True:
# 处理监听事件
gf.check_event(ship, setting, screen, bullets)
# 移动飞船
ship.move()
# 更新子弹位置
gf.update_bullets(bullets, aliens)
# 更新外星人
gf.update_aliens(aliens, setting, ship)
# 刷新屏幕
gf.update_screen(setting, screen, ship, bullets, aliens)
if __name__ == '__main__':
run_game()
gf
模块更新外星人函数处理碰撞,代码如下:
--snip--
def update_aliens(aliens, setting, ship):
check_fleet_edges(aliens, setting)
aliens.update()
# 碰撞后处理后续逻辑
if pygame.sprite.spritecollideany(ship, aliens):
# 飞船回到底部中央
ship.center_ship()
# 休眠1帧
sleep(1)
--snip--
与子弹与外星人的碰撞不同,外星人撞击到飞船以后,外星人不做处理,但是飞船需要回到底部中央位置,同时游戏暂停1秒。
ps:这里后面可以进行更丰富的处理,比如减少飞船生命值,减少飞船可用数量的等。
ship
模块更新重置飞船位置的函数,代码如下:
import pygame
class Ship:
def __init__(self, setting, screen):
"""初始化飞船"""
self.screen = screen
self.setting = setting
# 加载图片并外接矩形
self.image = pygame.transform.scale(
pygame.image.load('E:/python_project/alien_invasion/assets/image/ship.bmp'), (50, 50))
# 飞船外接的矩形
self.rect = self.image.get_rect()
# 窗口矩形
self.screen_rect = screen.get_rect()
# 将每艘新飞船放在屏幕底部中央
self.rect.centerx = self.screen_rect.centerx
self.rect.bottom = self.screen_rect.bottom
# 移动标志
self.moving_right = False
self.moving_left = False
self.moving_top = False
self.moving_bottom = False
--sinp--
def center_ship(self):
"""飞船居中"""
self.rect.centerx = self.screen_rect.centerx
self.rect.bottom = self.screen_rect.bottom
完成这些代码以后,照例启动main
模块,这次我们主动驾驶飞船撞击外星人。
在撞击的瞬间,游戏会暂停一秒,然后飞船回到底部中央。
如上两图所示。
结尾
子弹击中外星人,外星人撞击飞船的逻辑我们完成了。到这里,游戏的基础功能就算搭建完了。
后续会对游戏功能优化,比如外星人全部击中,飞船数量限制乃至于计分,开始按钮,暂停按钮等等。
背景音乐也可以弄,其实很简单,难的是找到合适的音乐。
今天的内容就到这里,加油大家!!!