Python项目实践之一:武装飞船
作者:互联网
Python项目实践之一:武装飞船
一、规划项目
1、游戏规则设定
在游戏《外星人入侵》中,玩家控制着一艘最初出现在屏幕底部中央的飞船。玩家可以使用箭头键左右移动飞船,还可使用空格键进行射击。游戏开始时,一群外星人出现在天空中,他们在屏幕中向下移动。玩家的任务是射杀这些外星人。玩家将所有外星人都消灭干净后,将出现一群新的外星人,他们移动的速度更快。只要有外星人撞到了玩家的飞船或到达了屏幕底部,玩家就损失一艘飞船。玩家损失三艘飞船后,游戏结束。
2、前期准备
1、建立项目文件
游戏《外星人入侵》将包含很多不同的文件,因此请在你的系统中新建一个文件夹,并将其命名为alien_invasion。请务必将这个项目的所有文件都存储到这个文件夹中,这样相关的 import 语句才能正确地工作。
2、安装pip
1、安装pip
安装pip,请访问https://bootstrap.pypa.io/get-pip.py。如果出现对话框,请选择保存文件;如果get-pip.py的代码出现在浏览器中,请将这些代码复制并粘贴到文本编辑器中,再将文件保存为get-pip.py。将get-pip.py保存到计算机中后,你需要以管理员身份运行它,因为pip将在你的系统中安装新包。
2、确认是否安装了pip
在dos窗口输入:pip --version
3、安装Pygame
1、前往python官网https://www.python.org/,点击PypI
2、在搜索框搜索pygame
3、选择要下载的pygame版本
4、点击下载文件
5、下载自己电脑对应的版本
6、把下载好的文件放入python的pip目录下
7、在dos窗口输入对文件对应的指令
8、提示安装成功
3、开始项目
1、创建 Pygame 窗口以及响应用户输入
import sys
import pygame
def run_game():
#初始化游戏并创建一个窗口对象
pygame.init()
#设置窗口大小为1200*800像素
screen = pygame.display.set_mode((1200, 700))
#设置窗口名字
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()
解释:
1、导入 sys 和 pygame模块, pygame 模块包含开发游戏所需的功能, 通过sys 模块实现退出游戏功能。
2、创建一个run_game()函数,用来提供游戏所需要的窗口界面并对玩家键盘鼠标进行监听。
3、screen是属于屏幕的一个面对象,用于显示游戏元素(飞船、外星人),游戏元素也是一个面对象,我们通过while True的死循环,实现对游戏元素在不同时间和状态在游戏界面里的显示。
2、设置背景颜色
import sys
import pygame
def run_game():
#初始化游戏并创建一个窗口对象
pygame.init()
#设置窗口大小为1200*800像素
screen = pygame.display.set_mode((1200, 700))
#设置窗口名字
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()
解释:
1、创建一个背景颜色存于 bg_color 中,在Pygame中,颜色是以RGB值指定的。这种颜色由红色、绿色和蓝色值组成,其中每个值的可能取值范围都为0~255。颜色值(255, 0, 0)表示红色,(0, 255, 0)表示绿色,而(0, 0, 255)表示蓝色。通过组合不同的RGB值,可创建1600万种颜色。在颜色值(230, 230, 230)中,红色、蓝色和绿色量相同,它将背景设置为一种浅灰色。
2、调用方法 screen.fill() ,用背景色填充屏幕。
3、创建设置类
每次给游戏添加新功能时,通常也将引入一些新设置。下面来编写一个名为 settings 的模块,其中包含一个名为 Settings 的类,用于将所有设置存储在一个地方,以免在代码中到处添加设置。这样,我们就能传递一个设置对象,而不是众多不同的设置。另外,这让函数调用更简单,且在项目增大时修改游戏的外观更容易:要修改游戏,只需修改settings.py中的一些值,而无需查找散布在文件中的不同设置。
settings.py:
class Settings():
"""存储《外星人入侵》的所有设置的类"""
def __init__(self):
"""初始化游戏的设置"""
# 屏幕设置
self.screen_width = 1200
self.screen_height = 600
self.bg_color = (230, 230, 230)
alien_invasion.py:
import sys
import pygame
from settings import Settings
def run_game():
#初始化游戏并创建一个窗口对象
pygame.init()
#初始化设置对象
ai_settings = Settings()
#设置窗口大小为1200*800像素
screen = pygame.display.set_mode((ai_settings.screen_width, ai_settings.screen_height))
#设置窗口名字
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(ai_settings.bg_color)
#绘制窗口
pygame.display.flip()
run_game()
解释:在主程序文件中,我们导入 Settings 类,调用 pygame.init() ,再创建一个 Settings 实例,并
将其存储在变量 ai_settings 中。创建屏幕时,使用了 ai_settings 的属性screen_width 和 screen_height ;接下来填充屏幕时,也使用了 ai_settings 来访问背景色。
4、添加飞船图像
4.1、寻找一张飞船图像,使得图像背景处理为透明色,我找到的飞船图像如下:
4.2、创建Ship类
ship.py:
class Ship():
def __init__(self, screen):
"""初始化飞船并设置其初始位置"""
self.screen = screen
#加载飞船,并获取其外形矩阵
self.imge = pygame.image.load(r'images/ship.png')
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)
解释:
1、 pygame.image.load(path)方法:打开需要加载的图像,返回值是一个面对象
2、 get_rect()方法: 获取相应面对象的属性(Pygame的效率之所以如此高,一个原因是它让你能够像处理矩形( rect 对象)一样处理游戏元素,即便它们的形状并非矩形。像处理矩形一样处理游戏元素之所以高效,是因为矩形是简单的几何形状。这种做法的效果通常很好,游戏玩家几乎注意不到我们处理的不是游戏元素的实际形状。)
3、 通过处理rect 对象相对于screen_rect对象的位置来规定飞船的初始化位置位于底部中间,即:让rect 对象的centerx与screen_rect对象的centerx相等,rect 对象的bottom与screen_rect的bottom相等(要将游戏元素居中,可设置相应 rect 对象的属性 center 、 centerx 或 centery 。要让游戏元素与屏幕边缘对齐,可使用属性 top 、 bottom 、 left 或 right ;要调整游戏元素的水平或垂直位置,可使用属性 x 和 y ,它们分别是相应矩形左上角的x和y坐标)。
4、在Pygame中,原点(0, 0)位于屏幕左上角,向右下方移动时,坐标值将增大。在1200×800的屏幕上,原点位于左上角,而右下角的坐标为(1200, 800),如下图:
4.3、在屏幕上绘制飞船
alien_invasion.py:
import sys
import pygame
from settings import Settings
from ship import Ship
def run_game():
#初始化游戏并创建一个窗口对象
pygame.init()
#初始化设置对象
ai_settings = Settings()
#设置窗口大小为1200*600像素
screen = pygame.display.set_mode((ai_settings.screen_width, ai_settings.screen_height))
#设置窗口名字
pygame.display.set_caption("Alien Invasion")
# 创建一艘飞船
ship = Ship(screen)
#设置背景颜色
bg_color = (230, 230, 230)
#开始游戏主循环
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()
5、重构:模块 game_functions
在大型项目中,经常需要在添加新代码前重构既有代码。重构旨在简化既有代码的结构,使其更容易扩展。在本节中,我们将创建一个名为 game_functions 的新模块,它将存储大量让游戏《外星人入侵》运行的函数。通过创建模块 game_functions ,可避免alien_invasion.py太长,并使其逻辑更容易理解。
5.1、管理事件
把管理事件的代码移到一个名为 check_events() 的函数中,以简化 run_game() 并隔离事件管理循环。通过隔离事件循环,可将事件管理与游戏的其他方面(如更新屏幕)分离。
5.2、更新屏幕
为进一步简化 run_game() ,下面将更新屏幕的代码移到一个名为 update_screen() 的函数中。
game_functions.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()
5.3将分离的功能添加到主函数中
alien_invasion.py:
import sys
import pygame
from settings import Settings
from ship import Ship
import game_functions as gf
def run_game():
#初始化游戏并创建一个窗口对象
pygame.init()
#初始化设置对象
ai_settings = Settings()
#设置窗口大小为1200*800像素
screen = pygame.display.set_mode((ai_settings.screen_width, ai_settings.screen_height))
#设置窗口名字
pygame.display.set_caption("Alien Invasion")
# 创建一艘飞船
ship = Ship(screen)
#设置背景颜色
bg_color = (230, 230, 230)
#开始游戏主循环
while True:
#监听键盘和鼠标事件
gf.check_events()
#每次循环重绘屏幕
gf.update_screen(ai_settings, screen, ship)
run_game()
6、驾驶飞船
6.1、响应按键
每当用户按键时,都将在Pygame中注册一个事件。事件都是通过方法 pygame.event.get() 获取的,因此在函数 check_events() 中,我们需要指定要检查哪些类型的事件。每次按键都被注册为一个 KEYDOWN 事件。
game_ functions.py中的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.rect.centerx += 1
解释:
1、在函数 check_events() 中添加了形参 ship ,因为按右箭头键时,需要将飞船向右移动,飞船是操作对象,所以要添加这个形参。
2、KEYDOWN 事件:键盘按键监听事件,每次按键都被注册为一个 KEYDOWN 事件。
3、通过循环不断的监听KEYDOWN 事件,当按下键盘右箭头时,程序作出对应的响应,即:将 ship.rect.centerx 的值加1,从而将飞船向右移动。
6.2、允许不断移动
实现按住右箭头键不放时,飞船不断地向右移动,直到松开为止。要实现这个功能,就必须同时监听KEYDOWN 和 KEYUP事件,根据键盘不同的状态,程序作出不同的响应。为此,我们还需要添加一个 moving_right的标志,它标志着飞船的状态:True为运动/False为静止。
由于 moving_right的标志是飞船的属性,我们现在修改ship.py的内容:
import pygame
class Ship():
def __init__(self, screen):
"""初始化飞船并设置其初始位置"""
self.screen = screen
#加载飞船,并获取其外形矩阵
self.image = pygame.image.load(r'images/ship.png')
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)
下面,我们只需要监听键盘按键,对self.moving_right来进行修改就能完成飞船的不断移动,修改game_functions.py中的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
最后,我们只需要修改 alien_invasion.py 中的 while 循环,以便每次执行循环时都调用飞船的方法 update() :
#开始游戏主循环
while True:
#监听键盘和鼠标事件
gf.check_events(ship)
#调用飞船目前位置
ship.update()
#每次循环重绘屏幕
gf.update_screen(ai_settings, screen, ship)
6.3、左右移动
现在飞船只能向右移动,那么向左移动的思路和向右移动的思路是一致的,只需要修改Ship类和game_functions.py中的check_events函数即可。
1、修改Ship类:
import pygame
class Ship():
def __init__(self, screen):
"""初始化飞船并设置其初始位置"""
self.screen = screen
#加载飞船,并获取其外形矩阵
self.image = pygame.image.load(r'images/ship.png')
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
def update(self):
"""根据移动标志调整飞船的位置"""
if self.moving_right:
self.rect.centerx += 1
if self.moving_left:
self.rect.centerx -= 1
def blitme(self):
"""在指定位置绘制飞船"""
self.screen.blit(self.image, self.rect)
2、修改game_functions.py中的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.key == pygame.K_LEFT:
ship.moving_left = True
elif event.type == pygame.KEYUP:
if event.key == pygame.K_RIGHT:
ship.moving_right = False
elif event.key == pygame.K_LEFT:
ship.moving_left = False
6.4、调整飞船的速度
目前飞船的移动速度是1像素,随着游戏的进行,我们需要加快游戏节奏,所以需要设置飞船的速度为一个可变的值,而且要便于修改,所以我们可以在 Settings 类中添加属性ship_speed_factor ,用于控制飞船的速度。
setting.py中的Settings 类:
class Settings():
"""存储《外星人入侵》的所有设置的类"""
def __init__(self):
"""初始化游戏的设置"""
# 屏幕设置
self.screen_width = 1200
self.screen_height = 600
self.bg_color = (230, 230, 230)
# 飞船的设置
self.ship_speed_factor = 1.5
我们将飞船的默认速度设置成了1.5像素,由于飞船移动像素的大小是通过ship.py中的update函数控制的,但是rect 的 centerx 等属性只能存储整数值,所以我们需要自顶一个center变量来使得移动像素可以是浮点数。
ship.py中的Ship类:
import pygame
class Ship():
def __init__(self, ai_settings, screen):
"""初始化飞船并设置其初始位置"""
self.screen = screen
#获取飞船目前的速度
self.ai_settings = ai_settings
#加载飞船,并获取其外形矩阵
self.image = pygame.image.load(r'images/ship.png')
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):
"""根据移动标志调整飞船的位置"""
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)
解释:
1、首先需要把形参ai_settings传入ship的__init__中,这样飞船在初始化的时候才能获取到当前的速度设置
2、由于rect 的 centerx、center、centery属性只能存储整数值(如果是小数自动取整数部分),而我们设置的初始化速度是1.5像素(浮点数),所以我们要定义一个新的属性center来存储浮点数,通过center来控制rect的属性。
3、现在在 update() 中调整飞船的位置时,将 self.center 的值增加或减去的值就可以是浮点数(如果不这么做,直接用centerx加上或减去1.5像素,那么实际移动还是1像素,原因是rect 的 centerx属性自动取整数部分的值)。
4、最后将移动后的center的值传给rect 的 centerx属性即可,其实这里也是有误差的,但是[0,1)像素的误差肉眼是观察不到的。
最后, alien_invasion.py 中创建 Ship 实例时,把实参 ai_settings 传入:
alien_invasion.py实例Ship:
# 创建一艘飞船
ship = Ship(ai_settings, screen)
6.5、限制飞船的活动范围
当前情况下,如果一直按住左箭头键或者右箭头键,飞船会飞出游戏界面的范围,下面我们需要通过设置使得飞船只能在游戏界面内移动。
其实很简单,我们只需要在Ship类的标志飞船位置的函数update中添加条件限定就可以实现,即飞船向右移动时,飞船的右边界小于游戏界面的右边界,飞船向左移动时,飞船的左边界大于0。
Ship类的函数update:
def update(self):
"""根据移动标志调整飞船的位置"""
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
# 根据self.center更新rect对象
self.rect.centerx = self.center
6.6、重构 check_events()
随着游戏开发的进行,函数 check_events() 将越来越长,我们将其部分代码放在两个函数中:一个处理 KEYDOWN 事件,另一个处理 KEYUP 事件。这么做有助于代码结构更新清晰,后期维护起来更方便。
game_functions.py:
import sys
import pygame
def check_keydown_events(event, ship):
"""响应按键事件"""
if event.key == pygame.K_RIGHT:
ship.moving_right = True
elif 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
elif 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()
7、射击
射击功能:子弹将在屏幕中向上穿行,抵达屏幕上边缘后消失。
7.1、 添加子弹设置
在settings.py中添加子弹设置:
class Settings():
"""存储《外星人入侵》的所有设置的类"""
def __init__(self):
"""初始化游戏的设置"""
# 屏幕设置
self.screen_width = 1200
self.screen_height = 600
self.bg_color = (230, 230, 230)
# 飞船的设置
self.ship_speed_factor = 1.5
#子弹设置
self.bullet_speed_factor = 1
self.bullet_width = 3
self.bullet_height = 15
self.bullet_color =(60, 60, 60)
分析:把子弹设置成宽3像素、高15像素的深灰色子弹,默认速度为1像素
7.2 创建 Bullet 类
bullet.py:
import pygame
from pygame.sprite import Sprite
class Bullet(Sprite):
"""一个对飞船发射的子弹进行管理的类"""
def __init__(self, ai_settings, screen, ship):
"""在飞船所处的位置创建一个子弹对象"""
super(Bullet, self).__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
解释:
1、Bullet 类继承了我们从模块 pygame.sprite 中导入的 Sprite 类。通过使用精灵,可将游戏中相关的元素编组,进而同时操作编组中的所有元素。
2、我们需要获取子弹的设置、相对于游戏界面的位置、相对于飞船的位置,所以需要将对应参数ai_settings、screen、 ship传入到Bullet类的__init__方法中。
3、由于子弹不是我们导入的图像,而是直接创建的,所以首先需要创建一个子弹矩形,然后再通过此子弹矩形调整它相对于飞船的位置
4、与飞船移动一样,rect中的center等方法只能接收int类型参数,如果不是int类型就取整数,所以为了使子弹移动能接收浮点数,所以要进行float转化。
5、最后把获取的子弹的颜色和速度存入变量中
子弹的初始位置设置好了,它和ship一样,也需要重新定义一个子弹的位置函数update,方便于后期维护。
定义bullet.py中Bullet类的update函数:
def update(self):
"""向上移动子弹"""
#更新表示子弹位置的小数值
self.y -= self.speed_factor
#更新表示子弹的rect的位置
self.rect.y = self.y
还需要定义一个函数draw_bullet,用来在游戏界面绘制子弹
定义bullet.py中Bullet类的draw_bullet函数:
def draw_bullet(self):
"""在屏幕上绘制子弹"""
pygame.draw.rect(self.screen, self.color, self.rect)
7.3、 将子弹存储到编组中
需要给子弹储存到一个编组中,就需要用到 pygame.sprite.Group 类,它类似于一个列表,但提供了有助于开发游戏的额外功能。
在主循环中,我们将使用这个编组在屏幕上绘制子弹,以及更新每颗子弹的位置:
alien_invasion.py:
import pygame
from pygame.sprite import Group
from settings import Settings
from ship import Ship
import game_functions as gf
def run_game():
#初始化游戏并创建一个窗口对象
pygame.init()
#初始化设置对象
ai_settings = Settings()
#设置窗口大小为1200*800像素
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()
#设置背景颜色
bg_color = (230, 230, 230)
#开始游戏主循环
while True:
#监听键盘和鼠标事件
gf.check_events(ai_settings, screen, ship, bullets)
#调用飞船目前位置
ship.update()
#调用编组中的有效子弹目前位置
bullets.update()
#每次循环重绘屏幕
gf.update_screen(ai_settings, screen, ship, bullets)
run_game()
解释:
1、我们将 bullets 传递给了 check_events() 和 update_screen() 。在 check_events() 中,需要在玩家按空格键时处理 bullets ;而在 update_screen() 中,需要更新要绘制到屏幕上的 bullets 。
2、当你对编组调用 update() 时,编组将自动对其中的每个精灵调用 update() ,因此代码行bullets.update() 将为编组 bullets 中的每颗子弹调用 bullet.update() 。
7.4、开火
开火:玩家按下空格键就会发射一颗子弹出去,因此,我们需要对空格键按下事件进行监听,并且每次发射出去的子弹都需要重绘到游戏界面,所以我们只需要修改game_functions.py就可以实现。
由于发射子弹只用监听空格键按下的响应,不用监听空格键松开的响应,所以修改game_functions.py中的check_keydown_events方法:
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:
new_bullet = Bullet(ai_settings, screen, ship)
bullets.add(new_bullet)
由于check_events方法调用了check_keydown_events方法,所以也需要修改game_functions.py中的check_events方法:
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)
最后,修改game_functions.py中的绘制update_screen方法即可:
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()
解释:
1、编组 bulltes (注意这里是编组,不是Bullet 的实例)传递给了 check_keydown_events() 。玩家按空格键时,创建一颗新子弹(一个名为 new_bullet 的 Bullet 实例),并使用方法 add() 将其加入到编组 bullets 中。
2、 方 法bullets.sprites() 返回一个列表,其中包含编组 bullets 中的所有精灵。为在屏幕上绘制发射的所有子弹,我们遍历编组 bullets 中的精灵,并对每个精灵都调用 draw_bullet()。
7.5、删除已消失的子弹
目前从视觉效果上可以看到子弹只要飞出游戏界面上边界就消失了,但是实际上它并没有消失,就好比不给飞船设定移动边界,飞船会移出游戏界面,但是它确实是存在的,只不过是超出了游戏界面的范围。我们需要将这些已消失的子弹删除,否则游戏所做的无谓工作将越来越多,进而变得越来越慢。
需要删除这些子弹,直接判断每个子弹的 rect 的 bottom 属性是否小于等于零即可,如果小于等于零,则证明子弹已经穿出上边界。
在主程序alien_invasion.py的while循环中添加此判断:
#开始游戏主循环
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)
解释:
1、在 for 循环中,不应从列表或编组中删除条目,因此必须遍历编组的副本(python在删除第一个元素后,后面的所有元素会向前移一位, 相应的,索引值也会发生改变)。我们使用了方法copy() 来设置 for 循环,这让我们能够在循环中修改 bullets 。
7.6、限制子弹数量
为了让游戏体验性更好,我们应该限制子弹的数量,以鼓励玩家能有效的发射子弹,还能减少服务器的压力。
首先,在settings.py中存储所允许的最大子弹数:
#子弹设置
self.bullet_speed_factor = 1
self.bullet_width = 3
self.bullet_height = 15
self.bullet_color =(60, 60, 60)
self.bullets_allowed = 3
其次,在game_functions.py的 check_keydown_events() 中,我们在创建新子弹前检查未消失的子弹数是否小于该设置:
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:
if len(bullets) < ai_settings.bullets_allowed:
new_bullet = Bullet(ai_settings, screen, ship)
bullets.add(new_bullet)
7.7、 创建函数 update_bullets()
为了让让主程序文件alien_invasion.py 尽可能简单,而且逻辑层次明确,我们应将子弹管理代码移到模块 game_functions 中。
在game_functions.py添加一个新函数 update_bullets(),用于更新子弹的位置,并删除已消失的子弹:
def update_bullets(bullets):
"""更新子弹的位置,并删除已消失的子弹"""
# 更新子弹的位置
bullets.update()
# 删除已消失的子弹
for bullet in bullets.copy():
if bullet.rect.bottom <= 0:
bullets.remove(bullet)
然后再主程序alien_invasion.py 的while循环把子弹管理相关的代码替换成调用格式:
while True:
#监听键盘和鼠标事件
gf.check_events(ai_settings, screen, ship, bullets)
#调用飞船目前位置
ship.update()
#调用管理子弹的函数
gf.update_bullets(bullets)
#每次循环重绘屏幕
gf.update_screen(ai_settings, screen, ship, bullets)
7.8、创建函数 fire_bullet()
将发射子弹的代码移到一个独立的函数中,这样,在 check_keydown_events() 中只需使
用一行代码来发射子弹,让 elif 代码块变得非常简单:
给game_functions.py添加一个新函数 fire_bullet,用于创建子弹:
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)
然后,在game_functions.py中的check_keydown_events调用以上函数即可:
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)
这是一篇读书笔记,如有不妥之处,请各位指正!!!
标签:飞船,Python,self,event,武装,ship,screen,rect,pygame 来源: https://blog.csdn.net/qq_35092730/article/details/112062686