当前位置:   article > 正文

Python项目 | 外星人入侵之武装飞船_class setting是啥项目

class setting是啥项目

武装飞船

1、规划项目

游戏《外星人入侵》的描述: 在游戏《外星人入侵》中,玩家控制着一艘最初出现在屏幕底部中央的飞船。玩家可以使用箭头键左右移动飞船,还可使用空格键进行射击。游戏开始时,一群外星 人出现在天空中,他们在屏幕中向下移动。玩家的任务是射杀这些外星人。玩家将所有外星人都消灭干净后,将出现一群新的外星人,他们移动的速度更快。只要有外星人撞到了玩家的飞船或到达了屏幕底部,玩家就损失一艘飞船。玩家损失三艘飞船后,游戏结束。

在第一个开发阶段,创建一艘可左右移动的飞船,这艘飞船在用户按空格键时能够开火。设置好这种行为后,就能够将注意力转向外星人,并提高这款游戏的可玩性。

2、安装Pygame

使用pip安装Python包:

在windows系统中检查是否安装了pip,打开终端,执行命令:

在windows系统安装pygame:

Pygame项目托管在代码分享网站Bitbucket中。要在Windows系统中安装Pygame,访问http://www.lfd.uci.edu/~gohlke/pythonlibs/#pygame ,查找与运行的Python版本匹配的Windows安装程序。我的python是3.6 64位的,所以下载下面版本。

将它复制到项目文件夹中。再打开一个命令窗口,切换到该文件所在的文件夹,并使用pip来运行它:

如果在pycharm不显示这个包,则在创建该项目的时候勾选下面选项:

或者在pycharm 中的project Interpreter中下载安装pygame。

3、开始游戏项目

创建Pygame窗口以及响应响应用户输入:

【1】创建一个空的Pygame窗口,使用Pygame编写的游戏的基本结构如下:

alien_invasion.py

  1. import sys # 玩家退出时,使用模块sys退出游戏
  2. import pygame
  3. def run_game():
  4. # 初始化游戏并创建一个频幕对象
  5. pygame.init() # 初始化背景设置
  6. screen = pygame.display.set_mode((1000,600) ) # 创建显示窗口,并指定了游戏窗口的尺寸px,注意使用了两个括号
  7. pygame.display.set_caption("Alien Invasion")
  8. # 开始游戏的主循环
  9. while True:
  10. # 监视键盘和鼠标
  11. for event in pygame.event.get():
  12. if event.type == pygame.QUIT: # 玩家单击游戏窗口关闭按钮时检测到
  13. sys.exit()
  14. # 不断更新屏幕,让最近绘制的屏幕可见
  15. pygame.display.flip()
  16. run_game()

现在运行该程序,看到的是一个空的pygame窗口。

【2】设置背景颜色

  1. #skip
  2. def run_game():
  3. #skip
  4. pygame.display.set_caption("Alien Invasion")
  5. # 设置背景颜色
  6. bg_color = (65,65,65)
  7. # 开始游戏的主循环
  8. while True:
  9. # 监视键盘和鼠标
  10. #skip
  11. #每次循环时都重新绘制屏幕
  12. screen.fill(bg_color)
  13. # 不断更新屏幕,让最近绘制的屏幕可见
  14. pygame.display.flip()
  15. run_game()

在Pygame中,颜色是以RGB值指定的。这种颜色由红色、绿色和蓝色值组成,其中每个值的可能取值范围都为0~255。颜色值(255, 0, 0)表示红色,(0, 255, 0)表示绿色,而(0, 0, 255)表示蓝色。

【3】创建设置类

下面来编写一个名为settings 的模块,其中包含一个名为Settings 的类,用于将所有设置存储在一个地方,以免在代码中到处添加设置。这样,就能传递一个设置对象,而不是众多不同的设置。

setting.py

  1. class Setting():
  2. # 存储该项目所有设置的类
  3. def __init__(self):
  4. # 初始化游戏的设置
  5. # 屏幕设置
  6. self.screen_width = 1000
  7. self.screen_heighe = 600
  8. self.bg_color = (65,65,65)

为创建Settings 实例并使用它来访问设置,将alien_invasion.py修改成下面这样:

  1. import sys # 玩家退出时,使用模块sys退出游戏
  2. import pygame
  3. from setting import Setting
  4. def run_game():
  5. # 初始化Pygame、设置和屏幕对象
  6. pygame.init() # 初始化背景设置
  7. ai_settings = Setting()
  8. screen = pygame.display.set_mode((ai_settings.screen_width,ai_settings.screen_heighe)) # 创建显示窗口,并指定了游戏窗口的尺寸px,注意使用了两个括号
  9. pygame.display.set_caption("Alien Invasion")
  10. # 开始游戏的主循环
  11. while True:
  12. # skip
  13. #每次循环时都重新绘制屏幕
  14. screen.fill(ai_settings.bg_color)
  15. # 不断更新屏幕,让最近绘制的屏幕可见
  16. pygame.display.flip()
  17. run_game()

添加飞船图像

在游戏中几乎可以使用任何类型的图像文件,但使用位图(.bmp)文件最为简单,因为Pygame默认加载位图。虽然可配置Pygame以使用其他文件类型,但有些文件类型要求你在 计算机上安装相应的图像库。大多数图像都为.jpg、.png或.gif格式,但可使用Photoshop、GIMP和Paint等工具将其转换为位图。

在该项目文件下新建images文件,并将ship.bmp图片保存在该文件下。

【1】创建Ship类:

选择用于表示飞船的图像后,需要将其显示到屏幕上。创建一个名为ship 的模块,其中包含Ship 类,它负责管理飞船的大部分行为。

ship.py

  1. import pygame
  2. class Ship():
  3. def __init__(self,screen):
  4. # 初始化飞船并设置其初始位置
  5. self.screen = screen
  6. # 加载飞船图像并获取其外接矩形
  7. self.image = pygame.image.load('images/ship.bmp')
  8. self.rect = self.image.get_rect()
  9. self.screen_rect = screen.get_rect()
  10. # 将每艘新飞船放在屏幕底部中央
  11. self.rect.centerx = self.screen_rect.centerx
  12. self.rect.bottom = self.screen_rect.bottom
  13. def blitme(self):
  14. # 在指定位置绘制飞船
  15. self.screen.blit(self.image,self.rect)

处理rect 对象时,可使用矩形四角和中心的 x 和 y 坐标。可通过设置这些值来指定矩形的位置。 要将游戏元素居中,可设置相应rect 对象的属性center 、centerx 或centery 。要让游戏元素与屏幕边缘对齐,可使用属性top 、bottom 、left 或right ;要调整游 戏元素的水平或垂直位置,可使用属性x 和y ,它们分别是相应矩形左上角的 x 和 y 坐标。(注意:在Pygame中,原点(0, 0)位于屏幕左上角,向右下方移动时,坐标值将增大。在1200×800的屏幕上,原点位于左上角,而右下角的坐标为(1200, 800)。)

【2】在屏幕上绘制飞船:

alien_invasion.py

  1. #skip
  2. from ship import Ship
  3. def run_game():
  4. # skip
  5. pygame.display.set_caption("Alien Invasion")
  6. # 创建一艘飞船
  7. ship = Ship(screen)
  8. #skip
  9. #每次循环时都重新绘制屏幕
  10. screen.fill(ai_settings.bg_color)
  11. ship.blitme()
  12. # 不断更新屏幕,让最近绘制的屏幕可见
  13. pygame.display.flip()
  14. run_game()

运行alien_invasion.py,看到飞船位于空游戏屏幕底部中央:

5、重构:模块game_functions

在大型项目中,经常需要在添加新代码前重构既有代码。重构旨在简化既有代码的结构,使其更容易扩展。

【1】函数check_events():

先把管理事件的代码移到一个名为check_events() 的函数中,以简化run_game() 并隔离事件管理循环。通过隔离事件循环,可将事件管理与游戏的其他方面(如 更新屏幕)分离。 将check_events() 放在一个名为game_functions 的模块中:

game_functions.py

  1. import sys
  2. import pygame
  3. def check_events():
  4. # 响应按键和鼠标事件
  5. for event in pygame.event.get():
  6. if event.type == pygame.QUIT:
  7. sys.exit()

修改alien_invasion.py,使其导入模块game_functions ,并将事件循环替换为对函数check_events() 的调用:

  1. # skip
  2. import game_functions as gf
  3. def run_game():
  4. # skip
  5. # 开始游戏的主循环
  6. while True:
  7. # 监视键盘和鼠标
  8. gf.check_events()
  9. #每次循环时都重新绘制屏幕
  10. #skip
  11. run_game()

【2】函数update_screen():

为进一步简化run_game() ,将更新屏幕的代码移到一个名为update_screen() 的函数中,并将这个函数放在模块game_functions.py 中:

  1. #skip
  2. def check_events():
  3. #skip
  4. def update_screen(ai_settings,screen,ship):
  5. # 更新屏幕上的图像,并切换到新屏幕
  6. # 每次循环时都重绘屏幕
  7. screen.fill(ai_settings.bg_color)
  8. ship.blitme()
  9. # 不断更新屏幕,让最近绘制的屏幕可见
  10. pygame.display.flip()

现在需要将alien_invasion.py的while 循环中更新屏幕的代码替换为对函数update_screen()的调用:

  1. #skip
  2. # 开始游戏的主循环
  3. while True:
  4. # 监视键盘和鼠标
  5. gf.check_events()
  6. gf.update_screen(ai_settings,screen,ship)
  7. run_game()

6、驾驶飞船

接下来让玩家能够左右移动飞船。编写代码,在用户按左或右箭头键时作出响应。首先专注于向右移动,再使用同样的原理来控制向左移动。

【1】响应按键:

每当用户按键时,都将在Pygame中注册一个事件。事件都是通过方法pygame.event.get() 获取的,因此在函数check_events() 中,需要指定要检查哪些类型的事件。每次按键都被注册为一个KEYDOWN 事件。 检测到KEYDOWN 事件时,我们需要检查按下的是否是特定的键。例如,如果按下的是右箭头键,就增大飞船的rect.centerx 值,将飞船向右移动:

  1. def check_events(ship):
  2. # 响应按键和鼠标事件
  3. for event in pygame.event.get():
  4. if event.type == pygame.QUIT: # 玩家单击游戏窗口关闭按钮时检测到
  5. sys.exit()
  6. elif event.type == pygame.KEYDOWN:
  7. if event.key == pygame.K_RIGHT:
  8. # 向右移动飞船
  9. ship.rect.centerx += 1

在alien_invasion.py中,更新调用的check_events() 代码,将ship 作为实参传递给它:

  1. # 开始游戏的主循环
  2. while True:
  3. # 监视键盘和鼠标
  4. gf.check_events(ship)
  5. gf.update_screen(ai_settings,screen,ship)

【2】允许不断移动:

玩家按住右箭头键不放时,飞船不断地向右移动,直到玩家松开为止。让游戏检测pygame.KEYUP 事件,以便玩家松开右箭头键时能够知道这一点;然 后,结合使用KEYDOWN 和KEYUP 事件,以及一个名为moving_right 的标志来实现持续移动。

飞船不动时,标志moving_right 将为False 。玩家按下右箭头键时,将这个标志设置为True ;而玩家松开时,我们将这个标志重新设置为False 。 飞船的属性都由Ship 类控制,将给这个类添加一个名为moving_right 的属性和一个名为update() 的方法。方法update() 检查标志moving_right 的状态, 如果这个标志为True ,就调整飞船的位置。每当需要调整飞船的位置时,都调用这个方法。 下面是对Ship 类所做的修改:

  1. class Ship():
  2. # skip
  3. # 将每艘新飞船放在屏幕底部中央
  4. self.rect.centerx = self.screen_rect.centerx
  5. self.rect.bottom = self.screen_rect.bottom
  6. # 移动标志
  7. self.moving_right = False
  8. def update(self):
  9. # 根据移动标志调整飞船的位置
  10. if self.moving_right: # 标志为True时向右移动飞船
  11. self.rect.centerx += 1
  12. def blitme(self):

修改check_events() ,使其在玩家按下右箭头键时将moving_right 设置为True ,并在玩家松开时将moving_right 设置为False :

  1. def check_events(ship):
  2. # 响应按键和鼠标事件
  3. for event in pygame.event.get():
  4. if event.type == pygame.QUIT: # 玩家单击游戏窗口关闭按钮时检测到
  5. sys.exit()
  6. elif event.type == pygame.KEYDOWN:
  7. if event.key == pygame.K_RIGHT:
  8. ship.moving_right = True
  9. elif event.type == pygame.KEYUP:
  10. if event.key == pygame.K_RIGHT:
  11. ship.moving_right = False

修改alien_invasion.py 中的while 循环,以便每次执行循环时都调用飞船的方法update() :

  1. # 开始游戏的主循环
  2. while True:
  3. # 监视键盘和鼠标
  4. gf.check_events(ship)
  5. ship.update()
  6. gf.update_screen(ai_setting,screen,ship)

飞船的位置将在检测到键盘事件后(但在更新屏幕前)更新。这样,玩家输入时,飞船的位置将更新,从而确保使用更新后的位置将飞船绘制到屏幕上。

现在运行alien_invasion.py并按住右箭头键,飞船将不断地向右移动,直到松开为止。

【3】左右移动:

将再次修改Ship 类和函数check_events() :

  1. class Ship():
  2. def __init__(self,screen):
  3. # skip
  4. # 移动标志
  5. self.moving_right = False
  6. self.moving_left = False
  7. def update(self):
  8. # 根据移动标志调整飞船的位置
  9. if self.moving_right: # 标志为True时向右移动飞船
  10. self.rect.centerx += 1
  11. if self.moving_left: # 标志为True时向左移动飞船
  12. self.rect.centerx -= 1

需对check_events() 作两方面的调整:

game_function.py

  1. def check_events(ship):
  2. # 响应按键和鼠标事件
  3. # skip
  4. elif event.type == pygame.KEYDOWN:
  5. if event.key == pygame.K_RIGHT:
  6. ship.moving_right = True
  7. elif event.key == pygame.K_LEFT:
  8. ship.moving_left = True
  9. elif event.type == pygame.KEYUP:
  10. if event.key == pygame.K_RIGHT:
  11. ship.moving_right = False
  12. elif event.key == pygame.K_LEFT:
  13. ship.moving_left = False

此时运行alien_invasion.py,将能够不断地左右移动飞船;如果你同时按左右箭头键,飞船将纹丝不动。

下面进一步优化飞船的移动方式:调整飞船的速度;限制飞船的移动距离,以免它移到屏幕外面去。

【4】调整飞船的速度:

可以在Settings 类中添加属性ship_speed_factor ,用于控制飞船的速度。根据这个属性决定飞船在每次循环时最多移动多少距离。

setting.py

  1. class Setting():
  2. # 存储该项目所有设置的类
  3. def __init__(self):
  4. # skip
  5. #飞船的设置
  6. self.ship_speed_factor = 1.5 # 移动1.5px

将速度设置指定为小数值,可在后面加快游戏的节奏时更细致地控制飞船的速度。然而,rect 的centerx 等属性只能存储整数值,因此我们需要对Ship 类做些修改:

ship.py

  1. class Ship():
  2. def __init__(self,ai_settings,screen):
  3. # 初始化飞船并设置其初始位置
  4. self.screen = screen
  5. self.ai_settings = ai_settings
  6. # skip
  7. # 将每艘新飞船放在屏幕底部中央
  8. #skip
  9. #飞船的属性centerx中存储小数值
  10. self.centerx = float(self.rect.centerx)
  11. # 移动标志
  12. self.moving_right = False
  13. self.moving_left = False
  14. def update(self):
  15. # 根据移动标志调整飞船的位置
  16. #更新飞船的centerx值,而不是rect
  17. if self.moving_right: # 标志为True时向右移动飞船
  18. self.centerx += self.ai_settings.ship_speed_factor
  19. if self.moving_left: # 标志为True时向左移动飞船
  20. self.centerx -= self.ai_settings.ship_speed_factor
  21. # 根据self.centerx更新rect对象
  22. self.rect.centerx = self.centerx

在__init__() 的形参列表中添加了ai_settings ,让飞船能够获取其速度设置。接下来,我们将形参ai_settings 的值存储在一个属性中,以便能够 在update() 中使用它。为准确地存储飞船的位置,定义了一个可存储小数值的新属性self.center 。使用函数float() 将self.rect.centerx 的值转换为小数,并将结果存储到self.center 中。

在alien_invasion.py中创建Ship 实例时,需要传入实参ai_settings :

  1. def run_game():
  2. # skip
  3. # 创建一艘飞船
  4. ship = Ship(ai_settings,screen)
  5. # skip

【5】限制飞船的活动范围:

如果玩家按住箭头键的时间足够长,飞船将移到屏幕外面消失。修改Ship 类的方法update()让飞船到达屏幕边缘后停止移动。

  1. def update(self):
  2. # 根据移动标志调整飞船的位置
  3. #更新飞船的centerx值,而不是rect
  4. if self.moving_right and self.rect.right < self.screen_rect.right: # 标志为True时向右移动飞船
  5. self.centerx += self.ai_settings.ship_speed_factor
  6. if self.moving_left and self.rect.left > 0: # 标志为True时向左移动飞船
  7. self.centerx -= self.ai_settings.ship_speed_factor
  8. # 根据self.centerx更新rect对象
  9. self.rect.centerx = self.centerx

self.rect.right 返回飞船外接矩形的右边缘的 x 坐标,如果这个值小于self.screen_rect.right 的值, 就说明飞船未触及屏幕右边缘。左边缘的情况与此类似:如果rect 的左边缘的 x 坐标大于零,就说明飞船未触及屏幕左边缘。这确保仅当飞船在屏幕内时, 才调整self.center 的值。

运行alien_invasion.py,飞船将在触及屏幕左边缘或右边缘后停止移动。

【6】重构check_events():

将其部分代码放在两个函数中:一个处理KEYDOWN 事件,另一个处理KEYUP 事件:

  1. def check_keydown_events(event,ship):
  2. # 响应按键
  3. if event.key == pygame.K_RIGHT:
  4. ship.moving_right = True
  5. elif event.key == pygame.K_LEFT:
  6. ship.moving_left = True
  7. def check_keyup_events(event,ship):
  8. # 响应松开
  9. if event.key == pygame.K_RIGHT:
  10. ship.moving_right = False
  11. elif event.key == pygame.K_LEFT:
  12. ship.moving_left = False
  13. def check_events(ship):
  14. # 响应按键和鼠标事件
  15. for event in pygame.event.get():
  16. if event.type == pygame.QUIT: # 玩家单击游戏窗口关闭按钮时检测到
  17. sys.exit()
  18. elif event.type == pygame.KEYDOWN:
  19. check_keydown_events(event,ship)
  20. elif event.type == pygame.KEYUP:
  21. check_keyup_events(event, ship)

7、回顾

【1】alien_invasion.py

主文件alien_invasion.py创建一系列整个游戏都要用到的对象:存储在ai_settings 中的设置、存储在screen 中的主显示surface以及一个飞船实例。文件alien_invasion.py还包含游 戏的主循环,这是一个调用check_events() 、ship.update() 和update_screen() 的while 循环。

要玩游戏《外星人入侵》,只需运行文件alien_invasion.py。其他文件(settings.py、game_functions.py、ship.py)包含的代码被直接或间接地导入到这个文件中。

【2】settings.py

文件settings.py包含Settings 类,这个类只包含方法__init__() ,它初始化控制游戏外观和飞船速度的属性。

【3】 game_functions.py

文件game_functions.py包含一系列函数,游戏的大部分工作都是由它们完成的。函数check_events() 检测相关的事件,如按键和松开,并使用辅助函 数check_keydown_events() 和check_keyup_events() 来处理这些事件。就目前而言,这些函数管理飞船的移动。模块game_functions 还包含函 数update_screen() ,它用于在每次执行主循环时都重绘屏幕。

【4】ship.py 文件ship.py包含Ship 类,这个类包含方法__init__() 、管理飞船位置的方法update() 以及在屏幕上绘制飞船的方法blitme() 。表示飞船的图像存储在文件夹images下的 文件ship.bmp中。

8、射击

编写玩家按空格键时发射子弹(小矩形)的代码。子弹将在屏幕中向上穿行,抵达屏幕上边缘后消失。

【1】添加子弹设置

更新setting.py,在其方法__init__() 末尾存储新类Bullet 所需的值:

  1. class Setting():
  2. # 存储该项目所有设置的类
  3. def __init__(self):
  4. # skip
  5. # 子弹设置
  6. self.bullet_speed_factor = 1
  7. self.bullet_width = 3 # 些设置创建宽3像素、高15像素的深红色子弹
  8. self.bullet_height = 15
  9. self.bullet_color = 178,34,34

【2】创建Bullet类

bullet.py

  1. import pygame
  2. from pygame.sprite import Sprite
  3. class Bullet(Sprite):
  4. """一个对飞船发射的子弹进行管理的类"""
  5. def __init__(self,ai_settings,screen,ship):
  6. # 在飞船所在的位置创建一个子弹对象
  7. super(Bullet, self).__init__()
  8. self.screen = screen
  9. # 在(0,0)处创建一个表示子弹的矩形,再设正确的位置
  10. self.rect = pygame.Rect(0,0,ai_settings.bullet_width,ai_settings.bullet_height)
  11. self.rect.centerx = ship.rect.centerx
  12. self.rect.top = ship.rect.top # 将表示子弹的rect 的top属性设置为飞船的rect 的top 属性,让子弹看起来像是从飞船中射出的
  13. # 存储用小数表示的子弹位置
  14. self.y = float(self.rect.y)
  15. self.color = ai_settings.bullet_color
  16. self.speed_factor = ai_settings.bullet_speed_factor

为创建子弹实例,需要 向__init__() 传递ai_settings 、screen 和ship 实例,还调用了super() 来继承Sprite 。代码super(Bullet, self).__init__() 使用了Python 2.7语法。这种语法也适用于Python 3,也可以将这行代码简写为super().__init__()

是bullet.py的第二部分——方法update() 和draw_bullet() :

  1. def update(self):
  2. # 向上移动子弹
  3. # 更新表示子弹位置的小数值
  4. self.y -= self.speed_factor # 发射出去后,子弹在屏幕中向上移动,这意味着y 坐标将不断减小
  5. # 更新表示子弹的rect的位置
  6. self.rect.y = self.y
  7. def draw_bullet(self):
  8. # 在屏幕上绘制子弹
  9. pygame.draw.rect(self.screen, self.color, self.rect)

【3】将子弹存储到编组中

编写代码:在玩家每次按空格键时都射出一发子弹。将在alien_invasion.py中创建一个编组(group),用于存储所有有效的子 弹,以便能够管理发射出去的所有子弹。这个编组将是pygame.sprite.Group 类的一个实例;pygame.sprite.Group 类类似于列表,但提供了有助于开发游戏的额外功能。

在主循环中,使用这个编组在屏幕上绘制子弹,以及更新每颗子弹的位置:

  1. from pygame.sprite import Group
  2. # skip
  3. def run_game():
  4. # skip
  5. # 创建一艘飞船
  6. ship = Ship(ai_settings,screen)
  7. # 创建一个用于存储子弹的编组
  8. bullets = Group()
  9. # 开始游戏的主循环
  10. while True:
  11. # 监视键盘和鼠标
  12. gf.check_events(ai_settings,screen,ship,bullets)
  13. ship.update()
  14. bullets.update()
  15. gf.update_screen(ai_settings,screen,ship,bullets)
  16. run_game()

将bullets传递给了check_events() 和update_screen() 。在check_events() 中,需要在玩家按空格键时处理bullets ;而在update_screen() 中,需要 更新要绘制到屏幕上的bullets 。 当对编组调用update() 时,编组将自动对其中的每个精灵调用update() ,因此代码行bullets.update() 将为编组bullets 中的每颗子弹调用bullet.update()

【4】开火

在game_functions.py中,需要修改check_keydown_events() ,以便在玩家按空格键时发射一颗子弹。无需修改check_keyup_events() ,因为玩家松开空格键时什么都不会发生。还需修改update_screen() ,确保在调用flip() 前在屏幕上重绘每颗子弹。下面是对game_functions.py所做的相关修改:

  1. from bullet import Bullet
  2. # skip
  3. def check_keydown_events(event, ai_settings, screen, ship, bullets):
  4. # skip
  5. elif event.key == pygame.K_SPACE:
  6. # 创建一颗子弹,并将其加入到编组bullets中
  7. new_bullet = Bullet(ai_settings,screen,ship)
  8. bullets.add(new_bullet)
  9. # skip
  10. def check_events(ai_settings, screen, ship, bullets):
  11. # skip
  12. elif event.type == pygame.KEYDOWN:
  13. check_keydown_events(event, ai_settings, screen, ship, bullets)
  14. elif event.type == pygame.KEYUP:
  15. check_keyup_events(event, ship)
  16. def update_screen(ai_setting,screen,ship,bullets):
  17. # skip
  18. # 在飞船和外星人后面重新重绘所有子弹
  19. for bullet in bullets.sprites():
  20. bullet.draw_bullet()
  21. ship.blitme()

方法bullets.sprites() 返回一个列表,其中包含编组bullets 中的所有精灵。为在屏 幕上绘制发射的所有子弹,我们遍历编组bullets 中的精灵,并对每个精灵都调用draw_bullet()。

 运行alien_invasion.py,将能够左右移动飞船,并发射任意数量的子弹。子弹在屏幕上向上穿行,抵达屏幕顶部后消失。

【5】删除消失的子弹

当前,子弹抵达屏幕顶端后消失,是因为Pygame无法在屏幕外面绘制它们。这些子弹实际上依然存在,它们的 y 坐标为负数,且越来越小。这是个问题,因为它们将继续消耗内存和处理能力。 需要将这些已消失的子弹删除,否则游戏所做的无谓工作将越来越多,进而变得越来越慢。

为此,需要检测这样的条件,即表示子弹的rect 的bottom 属性为零,它表明子弹已穿过屏幕顶端:

  1. # 开始游戏的主循环
  2. while True:
  3. # skip
  4. bullets.update()
  5. # 删除已经消失的子弹
  6. for bullet in bullets.copy():
  7. if bullet.rect.bottom <= 0:
  8. bullets.remove(bullet)
  9. print(len(bullets))
  10. gf.update_screen(ai_settings,screen,ship,bullets)

在for 循环中,不应从列表或编组中删除条目,因此必须遍历编组的副本。使用方法copy() 来设置for 循环,能够在循环中修改bullets 。检查每颗子弹,看看它是否已从屏幕顶端消失。如果是这样,就将其从bullets 中删除。另外使用了一条print 语句,以显示当前还有多少颗子弹, 从而核实已消失的子弹确实删除了。 如果这些代码没有问题,我们发射子弹后查看终端窗口时,将发现随着子弹一颗颗地在屏幕顶端消失,子弹数将逐渐降为零。运行这个游戏并确认子弹已被删除后,将这条print语句删除。

【6】限制子弹的数量

在settings.py中存储所允许的最大子弹数:

  1. # 子弹设置
  2. self.bullet_speed_factor = 1
  3. self.bullet_width = 3 # 些设置创建宽3像素、高15像素的深灰色子弹
  4. self.bullet_height = 15
  5. self.bullet_color = 178,34,34
  6. self.bullet_allowed = 3 # 将未消失的子弹数限制为3颗

在game_functions.py的check_keydown_events() 中,在创建新子弹前检查未消失的子弹数是否小于该设置:

  1. def check_keydown_events(event, ai_settings, screen, ship, bullets):
  2. # skip
  3. elif event.key == pygame.K_SPACE:
  4. # 创建一颗子弹,并将其加入到编组bullets中
  5. if len(bullets) < ai_settings.bullet_allowed:
  6. new_bullet = Bullet(ai_settings,screen,ship)
  7. bullets.add(new_bullet)

【7】创建函数update_bullets()

编写并检查子弹管理代码后,可将其移到模块game_functions 中,以让主程序文件alien_invasion.py尽可能简单。创建一个名为update_bullets() 的新函数,并将其添 加到game_functions.py的末尾:

  1. def update_bullet(bullets):
  2. """更新子弹的位置,并删除已消失的子弹"""
  3. # 更新子弹的位置
  4. bullets.update()
  5. # 删除已消失的子弹
  6. for bullet in bullets.copy():
  7. if bullet.rect.bottom <= 0:
  8. bullets.remove(bullet)
  9. # print(len(bullets))

update_bullets() 的代码是从alien_invasion.py剪切并粘贴而来的,它只需要一个参数,即编组bullets 。 alien_invasion.py中的while 循环又变得很简单了:

  1. # 开始游戏的主循环
  2. while True:
  3. # 监视键盘和鼠标
  4. gf.check_events(ai_settings,screen,ship,bullets)
  5. ship.update()
  6. gf.update_bullets(bullets)
  7. gf.update_screen(ai_settings,screen,ship,bullets)

让主循环包含尽可能少的代码,这样只要看函数名就能迅速知道游戏中发生的情况:

  1. 主循环检查玩家的输入。
  2. 然更新飞船的位置。
  3. 更新所有未消失的子弹的位置。
  4. 使用更新后的位置来绘制新屏幕。

【8】创建函数fire_bullet()

将发射子弹的代码移到一个独立的函数中,这样,在check_keydown_events() 中只需使用一行代码来发射子弹,让elif 代码块变得非常简单:

game_function.py

  1. def check_keydown_events(event, ai_settings, screen, ship, bullets):
  2. # skip
  3. elif event.key == pygame.K_SPACE:
  4. fire_bullet(ai_settings,screen,ship,bullets)
  5. def fire_bullet(ai_settings, screen, ship, bullets):
  6. """如果还没有到达限制,就发射一颗子弹"""
  7. # 创建新子弹,并将其加入到编组bullets中
  8. if len(bullets) < ai_settings.bullet_allowed:
  9. new_bullet = Bullet(ai_settings, screen, ship)
  10. bullets.add(new_bullet)

运行程序后,发射子弹没有错误。

9、小结

通过目前项目学习了:

  • 游戏开发计划的制定;
  • 使用Pygame编写的游戏的基本结构;
  • 如何设置背景色;
  • 如何将设置存储在可供游戏的各个部分访问的独立类中;
  • 如何在屏幕上绘制图像;
  • 如何让玩家控制游戏元素的移动;
  • 如何创建自动移动的元素,如在屏幕中向上飞驰的子弹;
  • 以及如何删除不再需要的对象;
  • 如何定期重构项目的代码,为后续开发提供便利。 
声明:本文内容由网友自发贡献,不代表【wpsshop博客】立场,版权归原作者所有,本站不承担相应法律责任。如您发现有侵权的内容,请联系我们。转载请注明出处:https://www.wpsshop.cn/w/知新_RL/article/detail/123283
推荐阅读
相关标签
  

闽ICP备14008679号