项目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(‘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

时间: 2024-10-17 02:21:01

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

第 12 章 命令模式【Command Pattern】

以下内容出自:<<24种设计模式介绍与6大设计原则>> 今天讲命令模式,这个模式从名字上看就很简单,命令嘛,老大发命令,小兵执行就是了,确实是这个意思,但是更深化了,用模式来描述真是是世界的命令情况.正在看这本书的你,我猜测分为两类:已经工作的和没有工作的,先说没有工作的,那你为啥要看这本书,为了以后工作呗,只要你参见工作,你肯定会待在项目组,那今天我们就以项目组为例子来讲述命令模式. 我是我们部门的项目经理,就是一个项目的头,在中国做项目,项目经理就是什么都要懂,什么都要管,做好

《构建之法》第10、11、12章

第10章问题:书上说一些好的功能还会有副作用,这里所说的副作用是指什么?是指功能没完善还是?(第十章10.2.1  P196第五) 第11章问题:开发阶段还有日常管理,这样不会浪费团队做项目的时间,做一个项目既要做Sprint计划,又要每日例会,还要规格说明书.功能说明书.列典型用户与典型场景                   描述等等,当这些完成后,确定还有时间弄项目吗?(第十一章11.2节) 第12章问题:用户体验和质量,有时候并不能同时做到很好,那么这两个当中那个要重要一点?要牺牲哪个取

3.28日第七次作业12章沟通管理13章合同管理

3.28日第七次作业12章沟通管理13章合同管理   第12章.项目沟通管理   1.项目沟通管理包括哪些过程?(记)P349 答:1).沟通计划编制 2).信息分发 3).绩效报告 4).项目干系人管理 2.阻碍有效沟通的因素有哪些?P351-352 答:1).沟通双方的物理距离 2).沟通的环境因素 3).缺乏清晰的沟通渠道 4).复杂的组织结构 5).复杂的技术术语 6).有害的态度 3.沟通计划编制的第一步是什么?目的是什么?P353 答:沟通计划编制的第一步是干系人分析.其目的是得出项

设计模式之第12章-享元模式(Java实现)

设计模式之第12章-享元模式(Java实现) “怎么回事,竟然出现了OutOfMemory的错误.鱼哥,来帮我看看啊.”“有跟踪错误原因么?是内存泄露么?”“不是内存泄露啊,具体原因不知道啊.对了,有说新对象申请不到内存空间.”“这个原因么,我曾写过一篇博文:叫OutOfMemory简单分析.不过你的明显是因为代码问题,产生对象太多,导致内存被耗尽,正好一会有堂课,讲的正好能解决你的问题.”(嘿嘿,轮到我享元模式出场了~) 享元模式之自我介绍 我,享元模式乃是池技术中的重要实现方式,具体定义如下

阅读10,11,12章

第10章 本章主要讲的是典型用户与场景,那么怎样定义典型用户? 答:(1)名字(越自然越好). (2)年龄(不同年龄和收入的用户有不同的需求). (3)收入. (4)代表的用户在市场上的比例和重要性(比例大不等同于重要性高,如付费的用户比例较少,但是影响大,所以更重要). (5)使用这个软件的典型场景. (6)使用本软件/服务的环境 (在办公室/家里/沙发/床上/公共汽车/地铁…). (7)生活/工作情况. (8)知识层次和能力(教育程度,对电脑.万维网的熟悉程度). (9)用户的动机.目的和困

《构建之法》第11,12章读后感

第11章 软件设计与实现 1.  关于小飞拿到spec之后做的估计开发任务所需时间,他是根据以前同类任务所需花费的实际时间以及其他同事的时间估计的.以现阶段我们学生的角度,该如何估计一个项目开发所需时间呢? 2.  修改集是什么?集成是什么意思?怎么样才是集成呢? 3.  每日构建是什么意思呢? 每日构建意味着自动地,每天,完整地构建整个代码树.(译者按:"代码树",原文为source tree,        意思是将整个项目源代码的目录,子目录,文件的位置尽可能事先固定下来,这样在

阅读&lt;构建之法&gt;第三10、11、12章并提出问题

<构建之法>第10.11.12章 第10章: 问题:对我们了解了用户的需求后,但是我们想法和做出来的软件会和用户的需求有偏差,比如风格.界面的修饰等等,那么我们程序猿怎样才能让自己的想法更加靠近用户的想法呢?是设身处境么? 第11章: 问题:我们现在这个阶段是在做四则运算APP,如果按照这章的步骤走下去,每天都要进行进度更新,和每日会议还有每日构建的,会不会不太符合我们现在的处境?毕竟我们的所有时间不能只为一门课程服务,还要大量的时间花在其他的课程上呢. 第12章: 问题:在实际的项目中,我们

公开课视频-《第11章 配置-Citrix-企业网盘-第12章 配置-UPM》

***************************************************************************************** <大企业云桌面部署实战>-培训班-即将开课,包学包会,欢迎咨询:3313395633(QQ) ***************************************************************************************** <大企业云桌面部署实战>-精讲课

JS读书笔记:《JavaScript框架设计》——第12章 异步处理

一.何为异步   执行任务的过程可以被分为发起和执行两个部分. 同步执行模式:任务发起后必须等待直到任务执行完成并返回结果后,才会执行下一个任务. 异步执行模式:任务发起后不等待任务执行完成,而是马上执行下一个任务,当任务执行完成时则会收到通知. 面对IO操作频繁的场景,异步执行模式可在同等的硬件资源条件下提供更大的并发处理能力,也就是更大的吞吐量. 但由于异步执行模式打破人们固有的思维方式,并且任务的发起和任务的执行是分离的,从而提高编程的复杂度. 多线程.多进程均可实现异步模式. 二.从回调