项目1 外星人入侵 第12章(武装飞船)

  终于,《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('imagesship.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('imagesship.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('imagesship.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