Python3学习笔记---项目1:外星人入侵①

一、武装飞船

游戏《外星人入侵》将包含很多不同的文件,因此请在你的系统中新建一个文件夹,并将其命名为alien_invasion。请务必将这个项目的所有文件都存储到这个文件夹中,这样相关的import语句才能正常工作。

游戏《外星人入侵》的描述:   

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

1、安装pygame:

这里以Windows为例,简单介绍一下安装步骤:

首先,检查Windows系统中是否安装了pip, 打开CMD命令窗口,执行以下命令:

如果没有安装pip,访问https://bootstrap.papy.io/get-pip.py。如果出现对话框,请保存文件,在此文件夹的路径打开cmd命令窗口,运行get-pip.py:

>python get-pip.py

运行此命令后,执行命令:

>python -m pip --version 来确认成功安装了pip。

下面安装Pygame:访问https://bitbucket.org/pygame/pygame/downloads/

下载跟python对应版本的Pygame,如果是.exe文件直接运行,如果是.whl文件使用pip来运行。打开命令窗口,切换到pygame的.whl文件路径下,用pip运行:

>python -m pip install --user pygame-1.9.2-cp35-cp35m-win_amd64.whl

2、开始游戏项目

创建Pygame窗口以及相应用户输入、背景颜色的设置

使用Pygame编写的游戏的基本结构如下:

alien_invasion.py

import sys
import pygame   #模块pygame包含开发游戏所需的功能

def run_game():
    #初始化游戏并创建一个屏幕对象
    pygame.init()  ##初始化背景设置
    screen = pygame.display.set_mode((1000, 700))  #创建一个名为screen的窗口,元组指定了游戏窗口的尺寸:宽1200像素,高800像素
    pygame.display.set_caption("Alien Invasion")

    #设置背景颜色
    bg_color = (20,230,255)  #Pygame中颜色以RGB值指定。图片三原色:红、绿、蓝。每个值的取值范围都是(0-255).
    #开始循环游戏
    while True:
        #监视键盘和鼠标事件
        for event in pygame.event.get():   #包含一个时间循环和以及管理屏幕更新的代码
            if event.type == pygame.QUIT:  #玩家关闭游戏窗口的按钮时,将检测到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),通过不同的RGB值进行组合,可创建1600万种颜色。颜色值(230,230,230)为浅灰色。

调用方法screen.fill(),用背景色填充屏幕;这个方法只接受一个实参:一种颜色。

创建设置类:

每次给游戏添加新功能时,通常也将引入一些 新设置。编写一个settings的模块,其中包含一个Settings的类,用于将所有设置存储在一个地方,以免在代码中到处添加设置。让函数调用更加简单,且在项目增大时修改游戏的外观更加容易,要修改游戏只要修改settings中的一些值,而不需查找散布在文件中的不同设置。

下面是最初的Settings类:

settings.py

class Settings():
    """存储《外星人入侵》的所有设置的类"""

    def __init__(self):
        """初始化设置"""
        #屏幕设置
        self.screen_width = 1000
        self.screen_height = 700
        self.bg_color = (230, 230, 230)

alien_invasion.py

from settings import Settings

def run_game():
    #初始化游戏并创建一个屏幕对象
    pygame.init()
    ai_setting = Settings()  ##导入类setting
    screen = pygame.display.set_mode((ai_setting.screen_width, ai_setting.screen_height))  #创建一个名为screen的窗口,元组指定了游戏窗口的尺寸
    pygame.display.set_caption("Alien Invasion")
   
    #开始循环游戏
    while True:
        #监视键盘和鼠标事件
        for event in pygame.event.get():   #包含一个时间循环和以及管理屏幕更新的代码
            if event.type == pygame.QUIT:  #玩家关闭游戏窗口的按钮时,将检测到pygame.QUIT事件
                sys.exit()                 #退出游戏
        #每次循环时都会重绘屏幕
        screen.fill(ai_setting.bg_color)               #填充背景颜色
        #让最近绘制的屏幕可见
        pygame.display.flip()  #将绘制的屏幕可见,移动游戏元素时,不断更新屏幕,以显示元素的新位置,并在原来的位置隐藏元素,从而营造平滑移动的效果

run_game() #初始化游戏并开始主循环

主文件程序中,导入Settings类,调用pygame.init(),再创建一个Settings实例,并将其存储在变量ai_settings中。创建屏幕时,使用了ai_settings的属性screen_width和screen_height; 填充屏幕时,也使用了ai_settings来访问背景色。

添加飞船图像:

将飞船加入到游戏中。我们将加载一幅图像,再使用Pygame方法blit()绘制它。为游戏选择素材时,务必要注意许可。最安全、最不费钱的方式是使用https://pixabay.com/等网站提供的图形,这些图形无需许可。

在游戏中使用位图(.bmp)文件最为简单,因为Pygame默认加载位图。.jpg、.png、.gif格式的图片,可使用Photoshop、GIMP和Paint等工具将其转换为位图。

选择图像时,要特别注意其背景色,尽可能选择背景透明的图像,这样可使用图像编辑器将其背景颜色设置为任何颜色。图像的背景色与游戏的时背景色相同时,游戏看起来最漂亮。

Python编程从入门到实战的配套资源(https://github.com/ehmatthes/pcc)中有ship.bmp的文件,在主文件夹(

alien_invasion)中新建一个文件夹,将其命名为images,并将文件ship.bmp保存到这个文件夹中。

创建ship类:

ship类,负责管理飞船的大部分行为:

ship.py

import pygame


class Ship():
    def __init__(self, screen):
        """初始化飞船"""
        self.screen = screen

        #加载飞船图像并获取其外接矩形
        self.image = pygame.image.load('images/ship.bmp')  #返回表示飞船的surface
        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)

加载图像后,我们使用get_rect()获取相应surface的属性rect。Pygame的效率之所以如此高,是因为他让你能够像处理矩形(rect对象)一样处理游戏元素,即便它们的形状并非矩形。像处理矩形一样处理游戏元素之所以高效,时因为矩形时简单的集合形状。

处理rect对象时,可使用矩形四角和中心的x和y坐标。可通过设置这些值来指定矩形的位置。

要将游戏元素居中,可设置相应rect对象的属性center、centerx或centery。要让游戏元素与屏幕边缘对齐,可使用属性top、bottom、left或right。要调整游戏元素的水平或者垂直位置,可使用属性x和y,它们分别是相应矩形左上角的x和y坐标。

#在Pygame中,原点(0,0)位于屏幕左上角,向右下方移动时,坐标值增大。在1200x800的屏幕上,原点位于左上角,右下角的坐标(1200,800)。

在屏幕上绘制飞船:

更新alien_invasion.py 使其创建一艘飞船,并调用其方法blitme():

alien_invasion.py

import sys
import pygame   #模块pygame包含开发游戏所需的功能

from settings import Settings
from ship import Ship

def run_game():
    #初始化游戏并创建一个屏幕对象
    pygame.init()
    ai_setting = Settings()  ##导入类setting
    screen = pygame.display.set_mode((ai_setting.screen_width, ai_setting.screen_height))  #创建一个名为screen的窗口,元组指定了游戏窗口的尺寸
    pygame.display.set_caption("Alien Invasion")
    #创建一艘飞船
    ship = Ship(screen)
    #开始循环游戏
    while True:
        #监视键盘和鼠标事件
        for event in pygame.event.get():   #包含一个时间循环和以及管理屏幕更新的代码
            if event.type == pygame.QUIT:  #玩家关闭游戏窗口的按钮时,将检测到pygame.QUIT事件
                sys.exit()                 #退出游戏
        #每次循环时都会重绘屏幕
        screen.fill(ai_setting.bg_color)               #填充背景颜色
        ship.blitme()   #将飞船绘制到屏幕上,确保它出现在背景前面

        #让最近绘制的屏幕可见
        pygame.display.flip()  #将绘制的屏幕可见,移动游戏元素时,不断更新屏幕,以显示元素的新位置,并在原来的位置隐藏元素,从而营造平滑移动的效果

run_game() #初始化游戏并开始主循环

重构:模块game_functions

大型项目中,经常需要在添加新代码前重构既有代码。重构旨在简化既有代码的结构,使其更容易扩展。创建一个名为game_functions的新模块,它将存储大量让游戏《外星人入侵》运行的函数。

check_events()函数:

首先将管理事件的代码移到一个名为check_events()的函数中,以简化run_game()并隔离事件管理循环。通过隔离事件循环,可将事件管理与游戏的其他方面分离。

game_functions.py

import  sys
import pygame

def check_events():
    """响应按键和鼠标事件"""
    for event in pygame.event.get():
        if event.type == pygame.QUIT:
            sys.exit()

此模块导入了事件检查循环要使用的sys和pygame。当前,函数check_events()不需要任何形参,其函数体复制了alien_invasion的事件循环。

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

驾驶飞船

响应按键:每当用户按键时,都将在Pygame中注册一个事件。事件都是通过方法Pygame.event.get()获取的,因此在函数check_events()中,我们需要指定要检查哪些类型的事件。每次按键都被注册为一个KEYDOWN事件。

检测到KEYDOWN事件时,我们需要检查按下的是否是特定的按键。例如:如果按下的是右箭头键,我们就增大飞船的rect.centerx值,将飞船向右移动:

game_functions.py

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
                #向右移动飞船
                ship.rect.centerx += 1       #将飞船向右移动

alien_invasion.py

alien_invasion.py

while True:
    #监视键盘和鼠标事件
    gf.check_events(ship)
    gf.update_screen(ai_setting, screen, ship)

现在运行alien_invasion.py,每按右键头键一次,飞船向右移动1像素。这是一个开端,但非控制飞船的高效方式。下面改进控制方式,允许持续移动。

允许不断移动:

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

飞船不动时,标识moving_right将为False。玩家按下右箭头键时,我们将这个标识设置为True;而玩家松开时,我们将这个标识重新设置为False。

飞船的属性都由Ship类来控制,因此我们将给这个类添加一个名为moving_right的属性和一个名为update()的方法。方法update()检查标识moving_right的状态,如果这个标识为True,就调整飞船的位置。每当需要调整飞船的位置时,我们都调用这个方法。

修改后的Ship类:

ship.py

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   #初始值设置为False,前述标识为True时向右移动

    def update(self):
        """根据移动标识调整飞船的位置"""
        if self.moving_right:      #前述标识为True时向右移动飞船,
            self.rect.centerx += 1

    def blitme(self):
        ---snip----

game_funcrions.py

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:
                """不直接调整飞船位置而将右移标识moving_right设置为True"""
                ship.moving_right =True
	"""玩家按下右箭头键时将moving_right设为True,玩家松开时将moving_right设为False"""
        elif event.type == pygame.KEYUP:
            if event.key == pygame.K_RIGHT:
                ship.moving_right = False   

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

alien_invasion.py

while True:
    #监视键盘和鼠标事件
    gf.check_events(ship)
    ship.update()    #飞船位置更新
    gf.update_screen(ai_setting, screen, ship)

左右移动:

向左移动和向右移动的原理是一样的,添加向右移动后的代码如下:

ship.py

class Ship():
---snip---

        #移动标志
        self.moving_right = False
        self.moving_left = False

    def update(self):
        """根据移动标识调整飞船的位置"""
        if self.moving_right:      #前述标识为True时向右移动飞船
            self.rect.centerx += 1
        if self.moving_left:
            self.rect.centerx -= 1

    def blitme(self):
        ---snip---

game_functions.py

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:
                """不直接调整飞船位置而将右移标识moving_right设置为True"""
                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

下面调整飞船的移动速度;限制飞船的移动距离,以免移到屏幕外面。

在sttings.py中添加新属性:

settings.py

class Settings():
    """存储《外星人入侵》的所有设置的类"""

    def __init__(self):
        ---snip---
        #飞船的设置
        self.ship_speed_factor = 1.5  #飞船的初始移动速度为1.5像素

ship.py

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):
        """根据移动标识调整飞船的位置"""
        if self.moving_right:      #前述标识为True时向右移动飞船
            self.center += self.ai_settings.ship_speed_factor
        if self.moving_left:
            self.center -= self.ai_settings.ship_speed_factor
        #根据selfcenter更新rect对象
        self.rect.centerx = self.center

    def blitme(self):
        ---snip---

alien_invasion.py

---snip---
def run_game():
    ---snip---
    #创建一艘飞船
    ship = Ship(ai_settings, screen)
    ---snip---

限制飞船的活动范围:

当前,如果玩家按住箭头的事件足够长,飞船将飞到屏幕外,消失的无影无踪。下面修复这种问题,让飞船到达屏幕边缘后停止移动。修改Ship类的update()方法:

def update(self):
    """根据移动标识调整飞船的位置"""
    if self.moving_right and self.rect.right < self.screen_rect.right:  #self.rect.right返回飞船的外接矩形的右边缘的x坐标,此值小于屏幕右边缘的值,说明未触及屏幕右边缘
        self.center += self.ai_settings.ship_speed_factor
    if self.moving_left and self.rect.left > 0:  #rect左边缘的坐标x大于0,说明未触及屏幕左边缘
        self.center -= self.ai_settings.ship_speed_factor
    #根据selfcenter更新rect对象
    self.rect.centerx = self.center

重构check_events():

随着游戏开发的运行,函数check_events()将越来越长,外贸将其部分代码放在两个函数中:一个处理KEYDOWN,事件,另一个处理KEYUP事件。

game_function.py

def check_keydown_events(event, ship):
    """响应按键"""
    if event.key == pygame.K_RIGHT:
        """不直接调整飞船位置而将右移标识moving_right设置为True"""
        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)

射击

添加子弹设置:更新settings.py,在其方法__init__()末尾存储新类Bullet所需的值:

settings.py

class Settings():
    """存储《外星人入侵》的所有设置的类"""

    def __init__(self):
        ---snip---
        #子弹设置:创建宽3像素、高5像素的深灰色子弹。子弹的速度比飞船稍低。
        self.bullet_speed_factor = 1  #子弹的运动速度为1个像素,稍低于飞船的速度
        self.bullet_width = 3       #子弹的宽为3像素
        self.bullet_height = 15     #子弹的高为15像素
        self.bullet_color = 60, 60, 60   #子弹设置为深灰色

创建Bullet类:

bullet.py

import pygame
from pygame.sprite import Sprite  #导入Sprite类


class Bullet(Sprite):      #继承类Sprite
    """一个对飞船发射的子弹进行管理的类"""

    def __init__(self, ai_settings, screen, ship):
        """在飞船的位置创建一个子弹对象"""
        super().__init__()   #继承Sprite类python2.7中使用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)   #子弹的y坐标存储为小数值,以便能够微调子弹的速度

        self.color = ai_settings.bullet_color
        self.speed_factor = ai_settings.bullet_speed_factor

        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)

Bullet类继承了从模块pygame.sprite中导入的Sprite类。通过使用精灵,可将游戏中相关的元素编组,进而提示操作编组中的元素。子弹的y坐标存储为小数值,以便能够微调子弹的速度 。

方法update()管理子弹的位置。发射出去后,子弹在屏幕中向上移动,这意味着y坐标将不断减小,因此为更新子弹的位置,外贸从self.y中减去self.speed_factor的值。接下来将self.rect.y的值设置为self.y的值。属性speed_factor让我们能够随着游戏的进行或根据需要调整子弹的速度,以调整游戏的行为。子弹发射后,其x坐标始终保持不变,因此子弹将沿直线垂直向上穿行。

调用draw_bullet()绘制子弹。函数draw_bullet()使用存储在self.color中的颜色填充表示子弹的rect占据的屏幕的部分。

将子弹存储到编组中:

定义Bullet类和必要的设置后,就可以编写代码了,在玩家每次按空格键时都射出一发子弹。首先,在alien_invasion.py中创建一个编组(group),用于存储所有有效的子弹,以便能够管理发射出去的子弹。这个编组将是pygame.sprite,Group类的一个实例;pygame.sprite.Group类类似于列表,但提供了有助于开发游戏的额外功能。在主循环中,我们将使用这个编组在屏幕上绘制子弹,以及更新每颗子弹的位置:

alien_invasion.py

import pygame   #模块pygame包含开发游戏所需的功能
from pygame.sprite import Group
---snip---

def run_game():
   ---snip---
    #创建一艘飞船
    ship = Ship(ai_settings, screen)
    bullets = Group()   #循环外部创建编组实例Group

    #开始循环游戏
    while True:
        #监视键盘和鼠标事件
        gf.check_events(ai_settings, screen, ship, bullets)
        ship.update()
        bullets.update()
        gf.update_screen(ai_settings, screen, ship,bullets)

如果在循环内部创建这样的编组,游戏运行时将创建数千个子弹编组,导致游戏慢的像蜗牛。如果游戏停滞不前,请仔细查看主while循环中发生的情况。

我们将bullets传递给check_events()和update_screen()。在check_events()中,需要玩家按空格键时处理bullets;而在update_screen()中,需要更新要绘制到屏幕上的bullets。

当你对编组调用update()时,编组将自动对其中的每个精灵调用update(),因此代码行bullets.update()将为编组bullets中的每颗子弹调用bullet.update()。

开火

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

game_functions.py

---snip---
from bullet import Bullet

def check_keydown_events(event, ai_settings, screen, ship, bullets):
    ---snip---
    elif event.key == pygame.K_SPACE:    #玩家按下空格键,创建一颗新子弹
        #创建一颗子弹,并将其加入到编组bullets中
        new_bullet = Bullet(ai_settings, screen, ship)    #新子弹
        bullets.add(new_bullet)   #将新子弹存储到bullets编组中
    ---snip---
def check_events(ai_settings, screen, ship, bullets):
    """响应按键和鼠标事件"""
    for event in pygame.event.get():
        ---snip---
        elif event.type == pygame.KEYDOWN:
            check_keydown_events(event, ai_settings, screen, ship, bullets)
     ---snip---

def update_screen(ai_settings, screen, ship, bullets):
    ---snip---
    #在飞船和外星人后面重绘所有子弹
    for bullet in bullets.sprites():
        bullet.draw_bullet()
    ship.blitme()
    ---snip---

删除已经消失的子弹:

当前子弹到达屏幕顶端后消失,这仅仅是因为Pygame无法在屏幕外绘制它们。这些子弹依然存在,它们的坐标为负数,且越来越小。它们将继续消耗内存和处理能力。

删除已消失的子弹:子弹的rect的bottom属性为0,它表明子弹已经穿过屏幕顶端:

alien_invasion.py

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)

在for循环中,不应从列表或者编组中删除条目,因此必须遍历编组的副本。我们使用了方法copy()来设置for循环,这让我们能够在循环中修改bullets。如果已从屏幕消失,则删除该子弹。

如果这些代码没有问题,我们发射子弹后查看终端窗口时,将发现随着子弹一颗颗地落在屏幕顶端消失,子弹数将逐渐降为0.运行这个游戏确认子弹已经被删除后,将这条print语句删除。留下这条语句,游戏地速度将大大降低,因为输出写入到终端花费地时间比将图像绘制到游戏窗口花费地时间还多。

限制子弹数量:

很多射击游戏都可对出现在屏幕上的子弹数量进行限制,以鼓励大家有目标的射击。

首先,在settings中存储所允许的最大子弹数:

settings.py

#子弹设置:创建宽3像素、高5像素的深灰色子弹。子弹的速度比飞船稍低。
self.bullet_speed_factor = 1
self.bullet_width = 3
self.bullet_height = 15
self.bullet_color = 60, 60, 60
self.bullets_allowed = 3   #将未消失的子弹数设置为3颗

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

game_functions.py

def check_keydown_events(event, ai_settings, screen, ship, bullets):
---snip----
    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)

玩家按空格键时,我们检查bullets的长度。如果len(bullets)小于3,就船舰一个新子弹;如果已经有3颗未消失的子弹,则玩家按空格键时什么都不会发生。

创建update_bullets():

将update_bullets()函数添加到game_functions.py的末尾:

game_functions.py

def update_bullets(bullets):
    """更新子弹的位置,并删除已经消失的子弹"""
    #更新子弹的位置
    bullets.update()
    #删除已经消失的子弹
    for bullet in bullets.copy():
        if bullet.rect.bottom <= 0:
            bullets.remove(bullet)

update_bullets()的代码从alien.invasion剪切粘贴而来,只需要一个参数:编组bullets。

alien_invasion的循环变得更加简单:

alien.invasion.py

while True:        #开始循环游戏
    #监视键盘和鼠标事件
    gf.check_events(ai_settings, screen, ship, bullets)
    ship.update()
    gf.update_bullets(bullets)
    gf.update_screen(ai_settings, screen, ship,bullets)

让主循环包含尽可能少的代码,这样只要看函数名就能迅速知道游戏中发生的情况。主循环一次检查玩家输入,然后更新飞船位置和所有消失的子弹的位置。最后,使用更新后的位置来绘制新屏幕。

创建函数fire_bullet()

game_functions.py

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)


def check_keydown_events(event, ai_settings, screen, ship, bullets):
    ----snip----
    elif event.key == pygame.K_SPACE:
        #创建一颗子弹,并将其加入到编组bullets中
        fire_bullet(ai_settings, screen, ship, bullets)

函数fire_bullet()只包含玩家按空格键时用于发射子弹的代码;在check_keydown_events()中,我们在玩家按空格键时调用fire_bullet()。

猜你喜欢

转载自blog.csdn.net/qq_33690342/article/details/82761539
今日推荐