Pygame《飞机游戏》(三)
飞机可以左右移动后,接下来要让它发射子弹。子弹的设置参数可以写在Settings类中,设置子弹的参数值。
class Settings(): """存储《飞机大战》的所有设置的类""" def __init__(self): """初始化游戏设置""" # 屏幕设置,宽度、高度、背景色(护眼色) self.screen_width = 1200 self.screen_heigth = 750 self.bg_color = (199, 237, 204) # 飞船的设置 self.ship_speed_factor = 2.5 # 子弹的设置 self.bullet_speed_factor = 1 self.bullet_width = 3 self.bullet_heigth = 15 self.bullet_color = (200, 100, 100)
然后创建一个Bullet类,存放于实现子弹功能相关的函数。加载pygame.sprite模块的Sprite类,这里Bullet类继承了Sprite类的属性。Sprite又称精灵类,使用时并不需要对它实例化,只需要继承它,然后按需写出自己的类就好了。draw()函数用于在screen上绘制子弹。
import pygame from pygame.sprite import Sprite class Bullet(Sprite): """一个对飞船发射的子弹进行管理的类""" def __init__(self, ai_settings, screen, ship): """在飞船所处的位置创建一个子弹对象""" super().__init__() self.screen = screen # 在(0, 0)处创建一个表示子弹的矩形,再设置正确的位置 self.rect = pygame.Rect(0, 0, ai_settings.bullet_width, ai_settings.bullet_heigth) self.rect.centerx = ship.rect.centerx self.rect.top = ship.rect.top # 存储用小数表示的子弹位置 self.y = float(self.rect.y) self.color = ai_settings.bullet_color self.speed_factor = ai_settings.bullet_speed_factor def update(self): """向上移动子弹""" # 更新表示子弹位置的小数值 self.y -= self.speed_factor # 更新表示子弹的rect的位置 self.rect.y = self.y def draw_bullet(self): """在屏幕上绘制子弹""" pygame.draw.rect(self.screen, self.color, self.rect)
下面实现用空格键发射子弹的功能。每按一下都会发射一枚子弹,在主程序中创建一个group编组,存储所有有效的子弹,及时更新所有子弹的位置。加载pygame.sprite模块的Group类,创建一个名为bullets的Group()类的实例。
import pygame from settings import Settings from ship import Ship import game_functions as gf from pygame.sprite import Group def run_game(): # 初始化pygame、设置和屏幕对象 pygame.init() ai_settings = Settings() screen = pygame.display.set_mode((ai_settings.screen_width, ai_settings.screen_heigth)) pygame.display.set_caption("飞机大战") # 创建一艘飞船 ship = Ship(ai_settings, screen) # 创建一个用于存储子弹的编组 bullets = Group() # 开始游戏的主循环 while True: # 监视键盘和鼠标事件、每次循环时都重绘屏幕、让最近绘制的屏幕可见 gf.check_events(ai_settings, screen, ship, bullets) ship.update() bullets.update() gf.update_screen(ai_settings, screen, ship, bullets) run_game()
空格事件在check_events()函数中监测,按下空格就将创建一个新的子弹精灵,并加入到bullets编组中,bullets.update()会对编组中每一个子弹调用bullet.update(),更新每一个有效子弹位置。最后gf.update_screen()函数会重绘屏幕和bullets中每颗子弹。
import sys import pygame from bullet import Bullet def check_keydown_events(event, ai_settings, screen, ship, bullets): """响应按键""" if event.key == pygame.K_RIGHT: # 右键按下,更新飞船移动标志被True ship.moving_right = True elif event.key == pygame.K_LEFT: # 左键按下,更新飞船移动标志被True ship.moving_left = True elif event.key == pygame.K_DOWN: # 下键按下,更新飞船移动标志被True ship.moving_down= True elif event.key == pygame.K_UP: # 上键按下,更新飞船移动标志被True ship.moving_up = True elif event.key == pygame.K_SPACE: # 创建一颗子弹,并将其加入到编组bullets中 new_bullet = Bullet(ai_settings, screen, ship) bullets.add(new_bullet) def check_keyup_events(event, ship): if event.key == pygame.K_RIGHT: # 右键放开,更新飞船移动标志被False ship.moving_right = False elif event.key == pygame.K_LEFT: # 左键放开,更新飞船移动标志被False ship.moving_left = False elif event.key == pygame.K_DOWN: # 下键放开,更新飞船移动标志被False ship.moving_down= False elif event.key == pygame.K_UP: # 上键放开,更新飞船移动标志被False ship.moving_up = False def check_events(ai_settings, screen, ship, bullets): """响应按键和鼠标事件""" for event in pygame.event.get(): if event.type == pygame.QUIT: sys.exit() elif event.type == pygame.KEYDOWN: check_keydown_events(event, ai_settings, screen, ship, bullets) elif event.type == pygame.KEYUP: check_keyup_events(event, ship) def update_screen(ai_settings, screen, ship, bullets): """更新屏幕上的图像,并切换到新屏幕""" # 每次循环时都重绘屏幕 screen.fill(ai_settings.bg_color) # 在飞船和敌人后面重绘所有子弹 for bullet in bullets.sprites(): bullet.draw_bullet() ship.blitme() # 让最近绘制的屏幕可见 pygame.display.flip()
运行程序可以用上下左右键移动飞机,用空格键发送子弹。
子弹到达顶端后会看不见,但是并未消失,仍然存放在bullets中,只是y坐标的值不停减少。随着子弹发射增多,bullets会存储越来越多子弹,对内存消耗增加。需要去掉那些超过顶端的子弹精灵。子弹的rect的bottom=0时,就可以删除这个子弹。
# 开始游戏的主循环 while True: # 监视键盘和鼠标事件、每次循环时都重绘屏幕、让最近绘制的屏幕可见 gf.check_events(ai_settings, screen, ship, bullets) ship.update() bullets.update() # 删除已消失的子弹 for bullet in bullets.copy(): if bullet.rect.bottom <= 0: bullets.remove(bullet) gf.update_screen(ai_settings, screen, ship, bullets)
为了提高游戏的可玩性,出现在屏幕的子弹必须有限制。所以在Settings类中要增加子弹出现个数的限制。
# 未消失的子弹限制为3 self.bullets_allowed = 3同时在check_keydown_events()函数中创建新子弹之前,进行屏幕子弹数的判断,小于设定值才创建新的子弹。
def check_keydown_events(event, ai_settings, screen, ship, bullets): """响应按键""" if event.key == pygame.K_RIGHT: # 右键按下,更新飞船移动标志被True ship.moving_right = True elif event.key == pygame.K_LEFT: # 左键按下,更新飞船移动标志被True ship.moving_left = True elif event.key == pygame.K_DOWN: # 下键按下,更新飞船移动标志被True ship.moving_down= True elif event.key == pygame.K_UP: # 上键按下,更新飞船移动标志被True ship.moving_up = True elif event.key == pygame.K_SPACE: # 检查屏幕上的子弹数,小于设定值,再创建一颗子弹,并将其加入到编组bullets中 if len(bullets) < ai_settings.bullets_allowed: new_bullet = Bullet(ai_settings, screen, ship) bullets.add(new_bullet)
为了精简主程序,可以将删除子弹放入game_funcations中。创建一个update_bullets()的函数。
def update_bullets(bullets): """更新子弹的位置,并删除已消失的子弹""" # 更新子弹的位置 bullets.update() # 删除已消失的子弹 for bullet in bullets.copy(): if bullet.rect.bottom <= 0: bullets.remove(bullet)
于是,while True又简化了。
# 开始游戏的主循环 while True: # 监视键盘和鼠标事件、每次循环时都重绘屏幕、让最近绘制的屏幕可见 gf.check_events(ai_settings, screen, ship, bullets) ship.update() gf.update_bullets(bullets) gf.update_screen(ai_settings, screen, ship, bullets)
在check_keydown_events()函数中的子弹发射可以单独创建一个函数fire_bullet()。
def check_keydown_events(event, ai_settings, screen, ship, bullets): """响应按键""" if event.key == pygame.K_RIGHT: # 右键按下,更新飞船移动标志被True ship.moving_right = True elif event.key == pygame.K_LEFT: # 左键按下,更新飞船移动标志被True ship.moving_left = True elif event.key == pygame.K_DOWN: # 下键按下,更新飞船移动标志被True ship.moving_down= True elif event.key == pygame.K_UP: # 上键按下,更新飞船移动标志被True ship.moving_up = True elif event.key == pygame.K_SPACE: fire_bullet(ai_settings, screen, ship, bullets) def fire_bullet(ai_settings, screen, ship, bullets): # 检查屏幕上的子弹数,小于设定值,再创建一颗子弹,并将其加入到编组bullets中 if len(bullets) < ai_settings.bullets_allowed: new_bullet = Bullet(ai_settings, screen, ship) bullets.add(new_bullet)