终于,《Python编程从入门到实践》看到了实践的部分,有三个项目,第一个是外星人入侵(12~14章)
在系统中新建一个文件夹,并将其命名为alien_invasion。这样相关的import语句才能正确地工作。
12.1 规划项目
在游戏《外星人入侵》中,玩家控制着一艘最初出现在屏幕底部中央的飞船。玩家可以用箭头键左右移动飞船,还可以使用空格键进行射击。游戏开始时,一群外星人出现在天空中,他们在屏幕中向下移动。玩家的任务是射杀这些外星人。玩家将所有外星人消灭干净之后,将会出现新的外星人,他们移动的速度更快。只要有外星人撞到了玩家的飞船或达到了屏幕的地步,玩家就损失一艘飞船。玩家损失三艘飞船之后,游戏结束。
在游戏的开发阶段,我们将创建一艘可以左右移动的飞船,这艘飞船在用户按空格键时能够开火。设置好这种行为后,我们就能够将注意力转向外星人,并提高这个游戏的可玩性。
12.2 安装Pygame
笔者使用的是Windows系统,首先检查自己的python版本:
再按照书上给出的链接:https://www.lfd.uci.edu/~gohlke/pythonlibs/#pygame,找到与运行的Python版本匹配的Windows安装程序。
点击下载,下载合适的文件后,将它复制到你的项目文件夹中。然后,选中你的项目文件夹按shift键右击选择在此处打开命令窗口,接着安装。
其实也不太懂这个命令,就是照着书上敲,不知道为啥最后安装成功了,但是安装在了
不一样的目录下,这里我就偷个懒也不探究了,直接到这个目录下面去找了,然后直接把文件夹拷贝到了项目文件夹下。这样后面import语句才能正常运行。
12.3 开始游戏项目
首先创建一个空的Pygame窗口,供后面用来绘制游戏元素,如飞船和外星人。我们还将让这个游戏响应用户输入、设置背景颜色以及加载飞船图像。
12.3.1 创建Pygame 窗口以及响应用户输入
首先创建一个空的Pygame窗口。使用Pygame编写的游戏的基本结构如下:
import sys import pygame def run_game(): # 初始化游戏并创建一个屏幕对象 pygame.init() screen = pygame.display.set_mode((1200,720)) pygame.display.set_caption("Alien Invasion") # 开始游戏的主循环 while True: # 监视键盘和鼠标事件 for event in pygame.event.get(): if event.type == pygame.QUIT: sys.exit() # 让最近绘制的屏幕可见 pygame.display.flip() run_game()
pygame.init()初始化背景设置
pygame.display.set_mode()创建一个名为screen的显示窗口
实参(1200,720)是一个元组,制定了游戏窗口的尺寸。
对象screen是一个surface,surface是屏幕的一部分,用于显示游戏元素。在这个游戏中,每个元素都是一个surface。每经过一次循环都将自动重绘这个surface。
while循环中包含一个事件循环以及管理屏幕更新的代码。
事件是用户玩游戏时执行的操作,如按键或移动鼠标。为让程序响应事件,我们编写一个事件循环,以侦听事件,并根据发生的事件执行相应的任务,for循环就是一个事件循环。在这个循环中,编写一系列if语句来检测并2特定的事件。例如,玩家单击游戏窗口的关闭按钮时,将检测到pygame.QUIT事件,再调用sys.exit()来退出游戏。
pygame.display.flip()将不断更新屏幕,以显示元素的新位置,并在原来的位置隐藏元素,从而营造平滑移动的效果。
12.3.2 设置背景色
import sys import pygame def run_game(): # 初始化游戏并创建一个屏幕对象 pygame.init() screen = pygame.display.set_mode((1200,720)) pygame.display.set_caption("Alien Invasion") # 设置背景色 bg_color = (230,230,230) # 开始游戏的主循环 while True: # 监视键盘和鼠标事件 for event in pygame.event.get(): if event.type == pygame.QUIT: sys.exit() # 每次循环时都会重绘屏幕 screen.fill(bg_color) # 让最近绘制的屏幕可见 pygame.display.flip() run_game()
在Pygame中,颜色是以RGB值指定的。这种颜色由红色、绿色和蓝色组成,其中每个值的可能取值范围都为0~255。颜色值为(255,0,0)表示红色,(0,255,0)表示绿色,而(0,0,255)表示蓝色。在颜色值(230,230,230)中,红色、蓝色绿色量相同,它将背景设置为一种浅灰色。
调用screen.fill(),用背景颜色填充屏幕,这个方法只接受一个实参:一种颜色。
12.3.3 创建设置类
下面编写一个名为settings的模块,其中包含一个名为Settings的类,用于将所有设置存储在一个地方,一面代码中到处添加设置。
下面是最初的Settings类:
class Settings(): """存储《外星人入侵》的所有设置的类""" def __init__(self): """初始化游戏的设置""" # 屏幕设置 self.screen_width = 1200 self.screen_height = 720 self.bg_color = (230,230,230)
为创建Settings实例并使用它来访问设置,将alien_invasion.py修改成下面这样:
import sys import pygame from settings import Settings def run_game(): # 初始化pygame、设置和屏幕对象 pygame.init() ai_settings = Settings() screen = pygame.display.set_mode( (ai_settings.screen_width,ai_settings.screen_height)) pygame.display.set_caption("Alien Invasion") # 开始游戏的主循环 while True: # 监视键盘和鼠标事件 for event in pygame.event.get(): if event.type == pygame.QUIT: sys.exit() # 每次循环时都会重绘屏幕 screen.fill(ai_settings.bg_color) # 让最近绘制的屏幕可见 pygame.display.flip() run_game()
12.4 添加飞船图像
在主项目文件夹(alien_invasion)中创建一个文件夹,将其命名为images,并将文件ship.bgm保存到这个文件夹中。
12.4.1 创建Ship类
创建一个ship模块,其中包含Ship类,它负责管理飞船大部分行为。
import pygame class Ship(): def __init__(self,screen): """初始化飞船并设置其初始位置""" self.screen = screen # 加载飞船图像并获取其外接矩形 self.image = pygame.image.load(‘images\ship.bmp‘) 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 def blitme(self): """在指定位置绘制飞船""" self.screen.blit(self.image,self.rect)
调用 pygame.image.load()加载图像,这个函数返回一个表示飞船的surface,而我们将这个surface存储到了self.image中。
调用get_rect()获取相应的surface属性rect,pygame的效率之所以如此高,一个原因是它让你能够像处理矩形(rect对象)一样处理游戏元素,即便它们的形状并非矩形。
处理rect对象时,可使用矩形死角和中心的x和y坐标。可通过设置这些值来指定矩形的位置。
要将游戏元素居中,可设置相应rect对象的属性center、centerx或centery。要让游戏元素与屏幕边缘对齐,可使用属性top、bootom、left或right;要调整游戏元素的水平或垂直位置,可使用属性x和y。原点(0,0)位于屏幕左上角,右下角坐标为(1200,720)
首先将表示屏幕的矩形存储在self.screen_rect中,再将self.rect.centerx(飞船中心的x坐标)设置为表示屏幕的矩形属性centerx。并将self.rect.bottom(飞船下边缘的y坐标)设置为表示屏幕的矩形的属性bottom。
12.4.2 在屏幕上绘制飞船
下面来更新alien_invasion.py,使其创建一艘飞船。
import sys import pygame from settings import Settings from ship import Ship def run_game(): # 初始化pygame、设置和屏幕对象 pygame.init() ai_settings = Settings() screen = pygame.display.set_mode( (ai_settings.screen_width,ai_settings.screen_height)) pygame.display.set_caption("Alien Invasion") # 创建一艘飞船 ship = Ship(screen) # 开始游戏的主循环 while True: # 监视键盘和鼠标事件 for event in pygame.event.get(): if event.type == pygame.QUIT: sys.exit() # 每次循环时都会重绘屏幕 screen.fill(ai_settings.bg_color) ship.blitme() # 让最近绘制的屏幕可见 pygame.display.flip() run_game()
导入Ship类,并在创建屏幕后创建一个名为ship的Ship实例。必须在while循环前面创建该实例,以免每次循环时创建一艘飞船。
12.5 重构:模块game_functions
在本节中,我们将创建一个名为game_functions的新模块,它将存储大量让游戏《外星人入侵》运行的函数。
12.5.1 函数 check_events()
我们首先把管理实践的代码移到一个名为check_events()的函数中,以简化run_game()并隔离实践管理循环。
将check_events()放在一个名为game_functions()的模块中:
import sys import pygame def check_events(): """响应按键和鼠标事件""" for event in pygame.event.get(): if event.type() == pygame.QUIT: sys.exit()
下面来修改alien_invasion.py,使其导入模块game_functions,并将实践循环替换为对函数check_events()的调用:
import pygame from settings import Settings from ship import Ship import game_functions as gf def run_game(): # 初始化pygame、设置和屏幕对象 pygame.init() ai_settings = Settings() screen = pygame.display.set_mode( (ai_settings.screen_width,ai_settings.screen_height)) pygame.display.set_caption("Alien Invasion") # 创建一艘飞船 ship = Ship(screen) # 开始游戏的主循环 while True: gf.check_events() # 每次循环时都会重绘屏幕 screen.fill(ai_settings.bg_color) ship.blitme() # 让最近绘制的屏幕可见 pygame.display.flip() run_game()
12.5.2 函数update_screen()
为进一步简化run_game(),下面将更新屏幕的代码移到一个名为update_screen()的函数中,并将这个函数放在模块game_funtions.py中:
import sys import pygame def check_events(): """响应按键和鼠标事件""" for event in pygame.event.get(): if event.type == pygame.QUIT: sys.exit() def update_screen(ai_settings,screen,ship): """更新屏幕上的图像,并切换到新屏幕上""" # 每次循环时都会重绘屏幕 screen.fill(ai_settings.bg_color) ship.blitme() # 让最近绘制的屏幕可见 pygame.display.flip()
import pygame from settings import Settings from ship import Ship import game_functions as gf def run_game(): # 初始化pygame、设置和屏幕对象 pygame.init() ai_settings = Settings() screen = pygame.display.set_mode( (ai_settings.screen_width,ai_settings.screen_height)) pygame.display.set_caption("Alien Invasion") # 创建一艘飞船 ship = Ship(screen) # 开始游戏的主循环 while True: gf.check_events() gf.update_screen(ai_settings,screen,ship) run_game()
p216 12-2动手试一试 将图像的背景色设置为屏幕的颜色,我上网查了资料,,,,不会,,,然后就用了ps。。。终于实现了图像背景色和屏幕背景色一样的蓝色。
12.6 驾驶飞船
12.6.1 相应按键
每当用户按键时,都会将在Pygame中注册一个事件。事件都是通过方法pygame.event.get()获取的,因此在函数check_events()中,我们需要指定检查哪些类型的事件。每次按键都被注册为一个KEYDOWN事件。
检测到KEYDOWN事件时,我们需要检查按下的是否是特定的键。例如,按下的是右箭头键,我们就增大飞船的rect.centrex值,将飞船向右移动。
修改game_functions.py:
import sys import pygame def check_events(ship): """响应按键和鼠标事件""" for event in pygame.event.get(): if event.type == pygame.QUIT: sys.exit() elif event.type == pygame.KEYDOWN: if event.key == pygame.K_RIGHT: # 向右移动飞船 ship.rect.centerx +=1 def update_screen(ai_settings,screen,ship): """更新屏幕上的图像,并切换到新屏幕上""" # 每次循环时都会重绘屏幕 screen.fill(ai_settings.bg_color) ship.blitme() # 让最近绘制的屏幕可见 pygame.display.flip()
读取属性event.key,以检查按下的是否是右箭头键(pygame.K.RIGHT)。在alien_invasion.py中,我们需要更新调用check_events()代码,将ship作为实参传递它:
# 开始游戏的主循环 while True: gf.check_events(ship) gf.update_screen(ai_settings,screen,ship)
12.6.2 允许不断移动
玩家按住右箭头键不放时,我们希望飞船不断向右移动,直到玩家松开为止。我们将让游戏检测到pygame.KEYUP事件,以便玩家松开右箭头键时我们能够直到这一点;然后,我们将结合使用KEYDOWN和KEYUP事件,以及一个名为moving_right的标志来实现持续移动。
飞船不动时,标志moving_right为False。玩家按下右箭头键,我们将这个标志设置为True;而玩家松开,我们将这个表中重新设置为Flase。
飞船的属性都由Ship控制,因此我们将给这个类添加一个名为moving_right的属性和一个名为update()的方法。方法update()检查标志moving_right的状态,如果这个标志为True,就调整飞船的位置。
下面对Ship类做修改:
import pygame class Ship(): def __init__(self,screen): """初始化飞船并设置其初始位置""" self.screen = screen # 加载飞船图像并获取其外接矩形 self.image = pygame.image.load(‘images\ship.bmp‘) 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 def update(self): """根据移动标志调整飞船的位置""" if self.moving_right: self.rect.centerx += 1 def blitme(self): """在指定位置绘制飞船""" self.screen.blit(self.image,self.rect)
下面来修改check_events():
def check_events(ship): """响应按键和鼠标事件""" for event in pygame.event.get(): if event.type == pygame.QUIT: sys.exit() elif event.type == pygame.KEYDOWN: if event.key == pygame.K_RIGHT: ship.moving_right =True elif event.type == pygame.KEYUP: if event.key == pygame.K_RIGHT: ship.moving_right =False
添加了一个新的elif代码块,用于响应KEYUP事件:玩家松开右箭头键(K_RIGHT)时,我们将moving_right设置为Flase。
最后,我们需要修改alien_invasion.py中的while循环,以便每次执行循环都调用飞船的方法update():
while True: gf.check_events(ship) ship.update() gf.update_screen(ai_settings,screen,ship)
飞船的位置将在检测到键盘事件后(但在更新屏幕之前)更新。这样,玩家输入时,飞船的位置将更新,从而确保使用更新后的位置将飞船绘制到屏幕上。
12.6.3 左右移动
根据向右移动,来写向左移动。
12.6.4 调整飞船的速度
我们可以在Settings类中添加属性ship_speed_factor,用于控制飞船的速度。下面演示了在settings.py中添加属性:
def_init_(self): --ship-- # 飞船的位置 self.ship_speed_factor = 1.5
通过将速度设置指定为小数值,可在后面加快游戏的节奏时更细致地控制飞船的速度。然而,rect的centerx等属性只能存储整数值,因此我们需要对Ship做一些修改:
import pygame class Ship(): def __init__(self,ai_settings,screen): """初始化飞船并设置其初始位置""" self.screen = screen self.ai_settings = ai_settings # 加载飞船图像并获取其外接矩形 self.image = pygame.image.load(‘images\ship.bmp‘) 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 # 在飞船的属性center中存储小数值 self.center = float(self.rect.centerx) # 移动标志 self.moving_right = False self.moving_left = False def update(self): """根据移动标志调整飞船的位置""" # 更新飞船的center值,而不是rect if self.moving_right: self.center += self.ai_settings.ship_speed_factor if self.moving_left: self.center -= self.ai_settings.ship_speed_factor # 根据self.center更新rect对象 self.rect.centerx = self.center def blitme(self): """在指定位置绘制飞船""" self.screen.blit(self.image,self.rect)
在alien_invasion.py中创建Ship实例,需要传入实参ai_settings:
12.6.5 限制飞船的活动范围
当前,如果玩家按住箭头键的时间足够长,飞船将移到屏幕外面。下面来修复这种问题,让飞船到达屏幕边缘后停止移动。为此我们将修改Ship类的方法update():
def update(self): """根据移动标志调整飞船的位置""" # 更新飞船的center值,而不是rect if self.moving_right and self.rect.right < self.screen_rect.right: self.center += self.ai_settings.ship_speed_factor if self.moving_left and self.rect.left > 0: self.center -= self.ai_settings.ship_speed_factor
12.6.6 重构 check_events()
随着游戏开发的进行,函数check_events()将越来越长,我们将其部分代码放在两个函数中:
一个用来处理KEYSOWN事件,另一个处理KEYUP事件:
game_functions.py
import sys import pygame def check_keydown_events(event,ship): """响应按键""" if event.key == pygame.K_RIGHT: ship.moving_right = True if event.key == pygame.K_LEFT: ship.moving_left = True def check_keyup_events(event,ship): if event.key == pygame.K_RIGHT: ship.moving_right = False if event.key == pygame.K_LEFT: ship.moving_left = False def check_events(ship): """响应按键和鼠标事件""" for event in pygame.event.get(): if event.type == pygame.QUIT: sys.exit() elif event.type == pygame.KEYDOWN: check_keydown_events(event,ship) elif event.type == pygame.KEYUP: check_keyup_events(event,ship) def update_screen(ai_settings,screen,ship): """更新屏幕上的图像,并切换到新屏幕上""" # 每次循环时都会重绘屏幕 screen.fill(ai_settings.bg_color) ship.blitme() # 让最近绘制的屏幕可见 pygame.display.flip()
12.7 简单回顾
12.7.1 alien_invasion.py
主文件alien_invasion.py创建一系列整个游戏都要用到的对象:存储ai_settings中的设置、存储在screen再的主显示surface以及一个飞船实例。文件alien_invasion.py还包含游戏的朱迅,这是一个调用check_events()、ship.update()和uodate_screen()的while循环。
12.7.2 settings.py
初始化控制游戏外观和飞船的速度属性
12.7.3 game_function.py
管理飞船的移动,屏幕的重绘
12.7.4 ship.py
管理飞船位置的方法update()以及在屏幕上绘制飞船的方法blitme()。表示飞船的图像存储位置。
12.8 射击
下面来添加射击功能。我们将编写玩家按空格键时发射子弹(小矩形)的代码。子弹将在屏幕中向上穿行,抵达屏幕上边缘消失。
12.8.1 添加子弹设置
首先,更新settings.py,在其方法_init_()末尾存储新类Bullet所需的值。
# 子弹设置 self.bullet_speed_factor = 1 self.bullet_width = 3 self.bullet_height =15 self.bullet_color = 60,60,60
12.8.2 创建Bullet类
下面创建存储Bullet类的文件bullet.py,其前半部分如下:
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_height) 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
Bullet类继承了我们从模块pygame.sprite中导入的Sprite类。通过使用精灵,可将游戏中相关的元素编组,而进而同时操作编组中的所有元素。为创建子弹实例,需要向_init_()传递ai_settings、screen和ship实例,还调用了super()来继承Sprite。
创建了子弹的属性rect,子弹并非基于图像的,因此我们必须使用 pygame.Rect()类从空白开始创建一个矩形。创建这个类的实例,必须提供矩形左上角的x坐标和y坐标,还有矩形的宽度和高度。我们在(0,0)出创建这个矩形,但接下来的两行代码将其移到了正确的位置,因为子弹的初始位置取决于飞船当前的位置。子弹的宽度和高度是从ai_settings中获取的。
子弹的conterx就是飞船的rect.conterx。将子弹的y坐标存储为小数值,以便能够微调子弹的速度。
下面是bulet.py的第二部分——方法update()和draw_bullet():
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)
方法update()管理子弹的位置,y坐标不断减小,x坐标始终不变。
12.8.3 将子弹存储到编组中
在玩家每次按空格键时都会发射出一发子弹。首先,我们将alien_invasion.py中创建一个编组(group),用于存储所有有效的子弹,以便能够管理发出去的所有子弹。这个编组将是pygame.sprite.Group类的一个实例;pygame.sprite.Group类类似于一个列表,但提供了有助于开发游戏的额外功能。在主循环中,我们将使用这个编组在屏幕上绘制子弹,以及更新每颗子弹的位置:
import pygame from pygame.sprite import Group from settings import Settings from ship import Ship import game_functions as gf def run_game(): # 初始化pygame、设置和屏幕对象 pygame.init() ai_settings = Settings() screen = pygame.display.set_mode( (ai_settings.screen_width,ai_settings.screen_height)) pygame.display.set_caption("Alien Invasion") # 创建一艘飞船 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;而在update_screen()中,需要更新要绘制到屏幕上的bullets。当你对编组调用update()时,编组将自动对其中的每个精灵调用update(),因此代码行bullets.update()将为编组bullets中的每颗子弹调用bullet.update()。
12.8.4 开火
在game_function.py中,我们需要修改check_keydown_events(),以便在玩家按空格键时发射一颗子弹。我们无需修改check_keyup_events(),因为玩家松开空格键时什么也不会发生。我们还需要修改update_screen(),确保在调用flip()前在屏幕上重绘每一颗子弹。
import sys import pygame from bullet import Bullet def check_keydown_events(event,ai_settings,screen,ship,bullets): """响应按键""" if event.key == pygame.K_RIGHT: ship.moving_right = True elif event.key == pygame.K_LEFT: ship.moving_left = 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: ship.moving_right = False if event.key == pygame.K_LEFT: ship.moving_left = 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传递给了check_keydown_events()。玩家按空格键时,创建一颗新子弹(一个名为new_bullet的Bullet实例),并使用方法add()加入到编组中。
12.8.5 删除已消失的子弹
当前,子弹并未删除,知识屏幕外无法绘制。它们继续消耗内存和处理能力。我们需要检测这样的条件,即表示子弹的rect的bottom属性为零,它表示子弹已穿过屏幕顶端:
import pygame from pygame.sprite import Group from settings import Settings from ship import Ship import game_functions as gf def run_game(): # 初始化pygame、设置和屏幕对象 pygame.init() ai_settings = Settings() screen = pygame.display.set_mode( (ai_settings.screen_width,ai_settings.screen_height)) pygame.display.set_caption("Alien Invasion") # 创建一艘飞船 ship = Ship(ai_settings,screen) # 创建一个用于存储子弹的编组 bullets = Group() # 开始游戏的主循环 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) print(len(bullets)) gf.update_screen(ai_settings,screen,ship,bullets) run_game()
在for循环中,不应从列表或编组中删除条目,因此必须遍历编组的副本。
如果这些代码没有问题,我们发射子弹后查看终端窗口时,将发现随着子弹一颗颗从屏幕顶端消失,子弹数逐渐降为0。运行这个游戏并确认子弹已被删除后,就讲这条print语句删掉。
12.8.6 限制子弹数量
首先,在settings.py中存储所允许的最大子弹数:
# 子弹设置 self.bullet_speed_factor = 1 self.bullet_width = 3 self.bullet_height =15 self.bullet_color = 60,60,60 self.bullet_allowed = 3
这将未消失的子弹数量限制为3颗,在game_function.py的check_keydown_events()中,我们在创建新子弹前检查未消失的子弹数是否小于该设置:
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)
12.8.7 创建函数 update_bullets()
编写并检查子弹管理代码之后,可将其移到模块game_functions中,让主程序尽可能简单。
def update_bullets(bullets): """更新子弹的位置,并删除已消失的子弹""" # 更新子弹的位置 bullets.update() # 删除已消失的子弹 for bullet in bullets.copy(): if bullet.rect.bottom <= 0: bullets.remove(bullet)
alien_invasion.py
gf.update_bullets(bullets) gf.update_screen(ai_settings,screen,ship,bullets)
12.8.8 创建函数fire_bullet()
下面将发射子弹的代码移到一个独立的函数中。
import sys import pygame from bullet import Bullet def check_keydown_events(event,ai_settings,screen,ship,bullets): """响应按键""" if event.key == pygame.K_RIGHT: ship.moving_right = True elif event.key == pygame.K_LEFT: ship.moving_left = 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.bullet_allowed: new_bullet = Bullet(ai_settings, screen, ship) bullets.add(new_bullet)
原文地址:https://www.cnblogs.com/cathycheng/p/11215660.html