(python)项目《外星人入侵》【上】

安装pygame

我是windows系统,所以直接打开终端窗口,输入了一串代码,然后它就跳出来一大堆代码,然后弄好了。

开始游戏项目

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

首先,跟着书上给的代码敲

import sys
import pygame
def run_game():
    #初始化游戏并创建一个屏幕对象
    pygame.init()
    screen =pygame.display.set_mode((1200,800))
    pygame.display.set_caption("Alien lnvasion")
    #开始游戏的主循环
    while True:
        #监视键盘和鼠标事件
        for event in pygame.event.get():
            if event.type == pygame.QUIT:
                sys.exit()
        #让最近绘制的屏幕可见
        pygame.display.flip()
run_game()

说实话,看到这个感觉自己前面十章白学了。。。
算了,老老实实看解释吧:

  • sys和pygame是模块,被导入进来了。(pygame包含开发游戏所需的功能。sys退出游戏)
  • pygame.init()初始化背景设置
  • pygame.display.set_mode()创建一个名为screen的显示窗口,这个游戏的所有图形元素都将在其中绘制。(两个括号啊)
  • 实参(1200, 800)是一个元组,指定了游戏窗口的尺寸。我们创建了一个宽1200像素、高800像素的游戏窗口(我以后都用这么大的窗口吧)
  • 对象screen是一个surface。在Pygame中,surface是屏幕的一部分,用于显示游戏元素。在这个游戏中,每个元素(如外星人或飞船)都是一个surface。display.set_mode()返回的surface表示整个游戏窗口。我们激活游戏的动画循环后,每经过一次循环都将自动重绘这个surface。
  • 这个游戏由一个while循环控制,其中包含一个事件循环以及管理屏幕更新的代码。
  • 事件是用户玩游戏时执行的操作,如按键或移动鼠标。
  • 为让程序响应事件,我们编写一个事件循环,以侦听事件,并根据发生的事件执行相应的任务。上面的for循环就是一个事件循环。
  • 访问Pygame检测到的事件:方法pygame.event.get()。
  • 所有键盘和鼠标事件都将促使for循环运行。
  • 在这个循环中,我们将编写一系列的if语句来检测并响应特定的事件。
  • 玩家单击游戏窗口的关闭按钮时,将检测到pygame.QUIT事件,然后调用sys.exit()来退出游戏。
  • 调用pygame.display.flip(),命令Pygame让最近绘制的屏幕可见。它在每次执行while循环时都绘制一个空屏幕,并擦去旧屏幕,使得只有新屏幕可见。(就是一个动作结束之后,从新开始)
  • 在我们移动游戏元素时,pygame.display.flip()将不断更新屏幕,以显示元素的新位置,并在原来的位置隐藏元素,(营造出了平滑移动的效果)
  • 最后一行调用run_game(),这将初始化游戏并开始主循环。
  • 如果此时运行这些代码,你将看到一个空的Pygame窗口。(的确,黑漆马虎的)

设置背景色

下面来将背景设置为另一种颜色:(黑色太没意思了,同意作者!!!)

import sys
import pygame
def run_game():
    #初始化游戏并创建一个屏幕对象
    pygame.init()
    screen =pygame.display.set_mode((1200,800))
    pygame.display.set_caption("Alien lnvasion")//屏幕标题
    #设置背景色
    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()

  • 创建一种背景色,并将其存储在bg_color中。
    (该颜色只需指定一次,因此我们在进入主while循环前定义它)
  • 科普:在Pygame中,颜色是以RGB值指定的。这种颜色由红色、绿色和蓝色值组成,其中每个值的可能取值范围都为0~255。颜色值(255, 0, 0)表示红色,(0, 255, 0)表示绿色,而(0, 0, 255)表示蓝色。在颜色值(230, 230, 230)中,红色、蓝色和绿色量相同,它将背景设置为一种浅灰色。
  • 我们调用方法screen.fill(),用背景色填充屏幕;这个方法只接受一个实参:一种颜色。

创建设置类

编写一个名为settings的模块,其中包含一个名为Settings的类,用于将所有设置存储在一个地方。要修改游戏,只需修改settings.py中的一些值,而无需查找散布在文件中的不同设置。
settings.py

class Settings():
    """存储《外星人入侵》的所有设置的类"""
    def __init__(self):
        """初始化游戏的设置"""
        #屏幕设置
        self.screen_width=1200
        self.screen_height=800
        self.bg_color=(230,230,230)

(def和__init__之间有空格啊千万记住!!!)
为创建Settings实例并使用它来访问设置,将alien_invasion.py修改成下面这样:

import sys
import pygame
from settings import Settings
def run_game():
    #初始化游戏并创建一个屏幕对象
    pygame.init()
    ai_settings=Settings()
    screen =pygame.display.set_mode((ai_settings.screen_width,ai_settings.screen_height))
    pygame.display.set_caption("Alien lnvasion")
    #开始游戏的主循环
    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来访问背景色。
    【今天先回顾到这里,做个记号】

添加飞船图像

(这个搞了我好久啊,要去官网把所有数据下载,然后从里面找到图片,再在python里面按照书上要求的创建一个文件,然后把图片导进去)

创建Ship类

Ship.py

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。
  • Ship的方法__init__()接受两个参数:引用self和screen,其中后者指定了要将飞船绘制到什么地方。
  • 为加载图像,我们调用了pygame.image.load()。这个函数返回一个表示飞船的surface,而我们将这个surface存储到了self.image中。
  • 加载图像后,我们使用get_rect()获取相应surface的属性rect。
  • Pygame的效率之所以如此高,一个原因是它让你能够像处理矩形(rect对象)一样处理游戏元素,即便它们的形状并非矩形。
  • 像处理矩形一样处理游戏元素之所以高效,是因为矩形是简单的几何形状。
  • 这种做法的效果通常很好,游戏玩家几乎注意不到我们处理的不是游戏元素的实际形状。
  • 处理rect对象时,可使用矩形四角和中心的x和y坐标。可通过设置这些值来指定矩形的位置。
  • 要将游戏元素居中,可设置相应rect对象的属性center、centerx或centery。
  • 要让游戏元素与屏幕边缘对齐,可使用属性top、bottom、left或right;
  • 要调整游戏元素的水平或垂直位置,可使用属性x和y,它们分别是相应矩形左上角的x和y坐标。
  • 这些属性让你无需去做游戏开发人员原本需要手工完成的计算,你经常会用到这些属性。
  • 在Pygame中,原点(0, 0)位于屏幕左上角,向右下方移动时,坐标值将增大。在1200×800的屏幕上,原点位于左上角,而右下角的坐标为(1200, 800)。
  • 我们将把飞船放在屏幕底部中央。为此,首先将表示屏幕的矩形存储在self.screen_rect中,
  • 再将self.rect.centerx(飞船中心的x坐标)设置为表示屏幕的矩形的属性centerx,
  • 并将self.rect.bottom(飞船下边缘的y坐标)设置为表示屏幕的矩形的属性bottom。
  • Pygame将使用这些rect属性来放置飞船图像,使其与屏幕下边缘对齐并水平居中。
  • 我们定义了方法blitme(),它根据self.rect指定的位置将图像绘制到屏幕上。
在屏幕上绘制飞船

alien_invasion.py

import sys
import pygame
from settings import Settings
from ship import Ship
def run_game():
    #初始化游戏并创建一个屏幕对象
    pygame.init()
    ai_settings=Settings()
    screen =pygame.display.set_mode((ai_settings.screen_width,ai_settings.screen_height))
    pygame.display.set_caption("Alien lnvasion")
    #创建一艘飞船
    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循环前面创建该实例,以免每次循环时都创建一艘飞船。
  • 填充背景后,我们调用ship.blitme()将飞船绘制到屏幕上,确保它出现在背景前面。
  • 现在如果运行alien_invasion.py,将看到飞船位于空游戏屏幕底部中央

重构:模块game_functions

在大型项目中,经常需要在添加新代码前重构既有代码。重构旨在简化既有代码的结构,使其更容易扩展。在本节中,我们将创建一个名为game_functions的新模块,它将存储大量让游戏《外星人入侵》运行的函数。通过创建模块game_functions,可避免alien_invasion.py太长,并使其逻辑更容易理解。

函数check_events()

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

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.py的事件循环。
  • 下面来修改alien_invasion.py,使其导入模块game_functions,并将事件循环替换为对函数check_events()的调用:

alien_invasion.py

import pygame
from settings import Settings
from ship import Ship
import game_functions as gf
def run_game():
    #初始化游戏并创建一个屏幕对象
    pygame.init()
    ai_settings=Settings()
    screen =pygame.display.set_mode((ai_settings.screen_width,ai_settings.screen_height))
    pygame.display.set_caption("Alien lnvasion")
    #创建一艘飞船
    ship = Ship(screen)
    #开始游戏的主循环
    while True:
        gf.check_events()
        #每次循环时都重绘屏幕
        screen.fill(ai_settings.bg_color)
        ship.blitme()
        #让最近绘制的屏幕可见
        pygame.display.flip()
run_game()

在主程序文件中,不再需要直接导入sys,因为当前只在模块game_functions中使用了它。出于简化的目的,我们给导入的模块game_functions指定了别名gf。

函数update_screen()

为进一步简化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()

新函数update_screen()包含三个形参:ai_settings、screen和ship。现在需要将alien_invasion.py的while循环中更新屏幕的代码替换为对函数update_screen()的调用:

alien_invasion.py

import pygame
from settings import Settings
from ship import Ship
import game_functions as gf
def run_game():
    #初始化游戏并创建一个屏幕对象
    pygame.init()
    ai_settings=Settings()
    screen =pygame.display.set_mode((ai_settings.screen_width,ai_settings.screen_height))
    pygame.display.set_caption("Alien lnvasion")
    #创建一艘飞船
    ship = Ship(screen)
    #开始游戏的主循环
    while True:
        gf.check_events()
        gf.update_screen(ai_settings,screen,ship)
run_game()

这两个函数让while循环更简单,并让后续开发更容易:在模块game_functions而不是run_game()中完成大部分工作。鉴于我们一开始只想使用一个文件,因此没有立刻引入模块game_functions。这让你能够了解实际的开发过程:一开始将代码编写得尽可能简单,并在项目越来越复杂时进行重构。

对代码进行重构使其更容易扩展后,可以开始处理游戏的动态方面了!

(终于要动了!!!!)

驾驶飞船

下面来让玩家能够左右移动飞船。为此,我们将编写代码,在用户按左或右箭头键时作出响应。我们将首先专注于向右移动,再使用同样的原理来控制向左移动。通过这样做,你将学会如何控制屏幕图像的移动。

响应按键

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

game_functions.py

import sys
import pygame
def check_events():
    """相应按键和鼠标事件"""
    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()

我们在函数check_events()中包含形参ship,因为玩家按右箭头键时,需要将飞船向右移动。在函数check_events()内部,我们在事件循环中添加了一个elif代码块,以便在Pygame检测到KEYDOWN事件时作出响应。
我们读取属性event.key,以检查按下的是否是右箭头键(pygame.K_RIGHT)。
如果按下的是右箭头键,就将ship.rect.centerx的值加1,从而将飞船向右移动。
在alien_invasion.py中,我们需要更新调用的check_events()代码,将ship作为实参传递给它:

alien_invasion.py

import pygame
from settings import Settings
from ship import Ship
import game_functions as gf
def run_game():
    #初始化游戏并创建一个屏幕对象
    pygame.init()
    ai_settings=Settings()
    screen =pygame.display.set_mode((ai_settings.screen_width,ai_settings.screen_height))
    pygame.display.set_caption("Alien lnvasion")
    #创建一艘飞船
    ship = Ship(screen)
    #开始游戏的主循环
    while True:
        gf.check_events(ship)
        gf.update_screen(ai_settings,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

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)

在方法__init__()中,我们添加了属性self.moving_right,并将其初始值设置为False。
接下来,我们添加了方法update(),它在前述标志为True时向右移动飞船。下面来修改check_events(),使其在玩家按下右箭头键时将moving_right设置为True,并在玩家松开时将moving_right设置为False:

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.moving_right=True
        elif event.type == pygame.KEYUP:
            if event.key == pygame.K_RIGHT:
                ship.moving_right=False
def update_screen(ai_settings,screen,ship):
    """更新屏幕上的图像,并切换到新屏幕"""
    #每次循环时都重绘屏幕
    screen.fill(ai_settings.bg_color)
    ship.blitme()
    #让最近绘制的屏幕可见
    pygame.display.flip()

在❶处,我们修改了游戏在玩家按下右箭头键时响应的方式:不直接调整飞船的位置,而只是将moving_right设置为True。在❷处,我们添加了一个新的elif代码块,用于响应KEYUP事件:玩家松开右箭头键(K_RIGHT)时,我们将moving_right设置为False。最后,我们需要修改alien_invasion.py中的while循环,以便每次执行循环时都调用飞船的方法update():

alien_invasion.py

import pygame
from settings import Settings
from ship import Ship
import game_functions as gf
def run_game():
    #初始化游戏并创建一个屏幕对象
    pygame.init()
    ai_settings=Settings()
    screen =pygame.display.set_mode((ai_settings.screen_width,ai_settings.screen_height))
    pygame.display.set_caption("Alien lnvasion")
    #创建一艘飞船
    ship = Ship(screen)
    #开始游戏的主循环
    while True:
        gf.check_events(ship)
        ship.update()
        gf.update_screen(ai_settings,screen,ship)
run_game()

飞船的位置将在检测到键盘事件后(但在更新屏幕前)更新。这样,玩家输入时,飞船的位置将更新,从而确保使用更新后的位置将飞船绘制到屏幕上。如果你现在运行alien_invasion.py并按住右箭头键,飞船将不断地向右移动,直到你松开为止。

左右移动

飞船能够不断地向右移动后,添加向左移动的逻辑很容易。我们将再次修改Ship类和函数check_events()。下面显示了对Ship类的方法__init__()和update()所做的相关修改:

ship.py

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
        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)

在方法__init__()中,我们添加了标志self.moving_left;在方法update()中,我们添加了一个if代码块而不是elif代码块,这样如果玩家同时按下了左右箭头键,将先增大飞船的rect.centerx值,再降低这个值,即飞船的位置保持不变。如果使用一个elif代码块来处理向左移动的情况,右箭头键将始终处于优先地位。从向左移动切换到向右移动时,玩家可能同时按住左右箭头键,在这种情况下,前面的做法让移动更准确。我们还需对check_events()作两方面的调整:

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.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
def update_screen(ai_settings,screen,ship):
    """更新屏幕上的图像,并切换到新屏幕"""
    #每次循环时都重绘屏幕
    screen.fill(ai_settings.bg_color)
    ship.blitme()
    #让最近绘制的屏幕可见
    pygame.display.flip()

如果因玩家按下K_LEFT键而触发了KEYDOWN事件,我们就将moving_left设置为True;如果因玩家松开K_LEFT而触发了KEYUP事件,我们就将moving_left设置为False。这里之所以可以使用elif代码块,是因为每个事件都只与一个键相关联;如果玩家同时按下了左右箭头键,将检测到两个不同的事件。如果此时运行alien_invasion.py,将能够不断地左右移动飞船;如果你同时按左右箭头键,飞船将纹丝不动。下面来进一步优化飞船的移动方式:调整飞船的速度;限制飞船的移动距离,以免它移到屏幕外面去。

调整飞船的速度

当前,每次执行while循环时,飞船最多移动1像素,但我们可以在Settings类中添加属性ship_speed_factor,用于控制飞船的速度。我们将根据这个属性决定飞船在每次循环时最多移动多少距离。下面演示了如何在settings.py中添加这个新属性:
settings.py

class Settings():
    """存储《外星人入侵》的所有设置的类"""
    def __init__(self):
        """初始化游戏的设置"""
        #屏幕设置
        self.screen_width=1200
        self.screen_height=800
        self.bg_color=(230,230,230)
        #飞船的设置
        self.ship_speed_factor=1.5

我们将ship_speed_factor的初始值设置成了1.5。需要移动飞船时,我们将移动1.5像素而不是1像素。通过将速度设置指定为小数值,可在后面加快游戏的节奏时更细致地控制飞船的速度。然而,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.rect.center+=self.ai_settings.ship_speed_factor
        if self.moving_left:
            self.rect.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)
  • 我们在__init__()的形参列表中添加了ai_settings,让飞船能够获取其速度设置。
  • 接下来,我们将形参ai_settings的值存储在一个属性中,以便能够在update()中使用它。
  • 鉴于现在调整飞船的位置时,将增加或减去一个单位为像素的小数值,因此需要将位置存储在一个能够存储小数值的变量中。
  • 可以使用小数来设置rect的属性,但rect将只存储这个值的整数部分。
  • 为准确地存储飞船的位置,我们定义了一个可存储小数值的新属性self.center。
  • 我们使用函数float()将self.rect.centerx的值转换为小数,并将结果存储到self.center中。
  • 现在在update()中调整飞船的位置时,将self.center的值增加或减去ai_settings.ship_speed_factor的值。
  • 更新self.center后,我们再根据它来更新控制飞船位置的self.rect.centerx。
  • self.rect.centerx将只存储self.center的整数部分,但对显示飞船而言,这问题不大。
  • 在alien_invasion.py中创建Ship实例时,需要传入实参ai_settings:

alien_invasion.py

import pygame
from settings import Settings
from ship import Ship
import game_functions as gf
def run_game():
    #初始化游戏并创建一个屏幕对象
    pygame.init()
    ai_settings=Settings()
    screen =pygame.display.set_mode((ai_settings.screen_width,ai_settings.screen_height))
    pygame.display.set_caption("Alien lnvasion")
    #创建一艘飞船
    ship = Ship(ai_settings,screen)
    #开始游戏的主循环
    while True:
        gf.check_events(ship)
        ship.update()
        gf.update_screen(ai_settings,screen,ship)
run_game()

(靠,又有好多出错。。)
现在,只要ship_speed_factor的值大于1,飞船的移动速度就会比以前更快。这有助于让飞船的反应速度足够快,能够将外星人射下来,还让我们能够随着游戏的进行加快游戏的节奏。

限制飞船的活动范围

当前,如果玩家按住箭头键的时间足够长,飞船将移到屏幕外面,消失得无影无踪。下面来修复这种问题,让飞船到达屏幕边缘后停止移动。为此,我们将修改Ship类的方法update():
ship.py

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 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
    def blitme(self):
        """在指定位置绘制飞船"""
        self.screen.blit(self.image,self.rect)

上述代码在修改self.center的值之前检查飞船的位置。self.rect.right返回飞船外接矩形的右边缘的x坐标,如果这个值小于self.screen_rect.right的值,就说明飞船未触及屏幕右边缘(见❶)。左边缘的情况与此类似:如果rect的左边缘的x坐标大于零,就说明飞船未触及屏幕左边缘(见❷)。这确保仅当飞船在屏幕内时,才调整self.center的值。如果此时运行alien_invasion.py,飞船将在触及屏幕左边缘或右边缘后停止移动。

重构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()

我们创建了两个新函数:check_keydown_events()和check_keyup_events(),它们都包含形参event和ship。这两个函数的代码是从check_events()中复制而来的,因此我们将函数check_events中相应的代码替换成了对这两个函数的调用。现在,函数check_events()更简单,代码结构更清晰。这样,在其中响应其他玩家输入时将更容易。

回顾

主文件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)包含的代码被直接或间接地导入到这个文件中。

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

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

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

射击

下面来添加射击功能。我们将编写玩家按空格键时发射子弹(小矩形)的代码。子弹将在屏幕中向上穿行,抵达屏幕上边缘后消失。

添加子弹设置

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

class Settings():
    """存储《外星人入侵》的所有设置的类"""
    def __init__(self):
        """初始化游戏的设置"""
        #屏幕设置
        self.screen_width=1200
        self.screen_height=800
        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像素的深灰色子弹。子弹的速度比飞船稍低。

创建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

Bullet类继承了我们从模块pygame.sprite中导入的Sprite类。通过使用精灵,可将游戏中相关的元素编组,进而同时操作编组中的所有元素。为创建子弹实例,需要向__init__()传递ai_settings、screen和ship实例,还调用了super()来继承Sprite。注意 代码super(Bullet, self).init()使用了Python 2.7语法。这种语法也适用于Python 3,但你也可以将这行代码简写为super().init()。在❶处,我们创建了子弹的属性rect。子弹并非基于图像的,因此我们必须使用pygame.Rect()类从空白开始创建一个矩形。创建这个类的实例时,必须提供矩形左上角的x坐标和y坐标,还有矩形的宽度和高度。我们在(0, 0)处创建这个矩形,但接下来的两行代码将其移到了正确的位置,因为子弹的初始位置取决于飞船当前的位置。子弹的宽度和高度是从ai_settings中获取的。在❷处,我们将子弹的centerx设置为飞船的rect.centerx。子弹应从飞船顶部射出,因此我们将表示子弹的rect的top属性设置为飞船的rect的top属性,让子弹看起来像是从飞船中射出的(见❸)。我们将子弹的y坐标存储为小数值,以便能够微调子弹的速度(见❹)。在❺处,我们将子弹的颜色和速度设置分别存储到self.color和self.speed_factor中。下面是bullet.py的第二部分——方法update()和draw_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
    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坐标将不断减小,因此为更新子弹的位置,我们从self.y中减去self.speed_factor的值(见❶)。接下来,我们将self.rect.y设置为self.y的值(见❷)。属性speed_factor让我们能够随着游戏的进行或根据需要提高子弹的速度,以调整游戏的行为。子弹发射后,其x坐标始终不变,因此子弹将沿直线垂直地往上穿行。需要绘制子弹时,我们调用draw_bullet()。函数draw.rect()使用存储在self.color中的颜色填充表示子弹的rect占据的屏幕部分(见❸)。

将子弹存储到编组中

定义Bullet类和必要的设置后,就可以编写代码了,在玩家每次按空格键时都射出一发子弹。首先,我们将在alien_invasion.py中创建一个编组(group),用于存储所有有效的子弹,以便能够管理发射出去的所有子弹。这个编组将是pygame.sprite.Group类的一个实例;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()
    screen =pygame.display.set_mode((ai_settings.screen_width,ai_settings.screen_height))
    pygame.display.set_caption("Alien lnvasion")
    #创建一艘飞船
    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()

我们导入了pygame.sprite中的Group类。在❶处,我们创建了一个Group实例,并将其命名为bullets。这个编组是在while循环外面创建的,这样就无需每次运行该循环时都创建一个新的子弹编组。注意 如果在循环内部创建这样的编组,游戏运行时将创建数千个子弹编组,导致游戏慢得像蜗牛。如果游戏停滞不前,请仔细查看主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所做的相关修改:

game_functions.py

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
    elif 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)
    ship.blitme()
    # 在飞船和外星人后面重绘所有子弹
    for bullet in bullets.sprites():
        bullet.draw_bullet()
    ship.blitme()
    # 让最近绘制的屏幕可见
    pygame.display.flip()

编组bulltes传递给了check_keydown_events()(见❶)。玩家按空格键时,创建一颗新子弹(一个名为new_bullet的Bullet实例),并使用方法add()将其加入到编组bullets中(见❷);代码bullets.add(new_bullet)将新子弹存储到编组bullets中。在check_events()的定义中,我们需要添加形参bullets(见❸);调用check_keydown_events()时,我们也需要将bullets作为实参传递给它。在❹处,我们给在屏幕上绘制子弹的update_screen()添加了形参bullets。方法bullets.sprites()返回一个列表,其中包含编组bullets中的所有精灵。为在屏幕上绘制发射的所有子弹,我们遍历编组bullets中的精灵,并对每个精灵都调用draw_bullet()(见❺)。如果此时运行alien_invasion.py,将能够左右移动飞船,并发射任意数量的子弹。子弹在屏幕上向上穿行,抵达屏幕顶部后消失,如图12-3所示。可在settings.py中修改子弹的尺寸、颜色和速度。

删除已消失的子弹

当前,子弹抵达屏幕顶端后消失,这仅仅是因为Pygame无法在屏幕外面绘制它们。这些子弹实际上依然存在,它们的y坐标为负数,且越来越小。这是个问题,因为它们将继续消耗内存和处理能力。我们需要将这些已消失的子弹删除,否则游戏所做的无谓工作将越来越多,进而变得越来越慢。为此,我们需要检测这样的条件,即表示子弹的rect的bottom属性为零,它表明子弹已穿过屏幕顶端:
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()
    screen =pygame.display.set_mode((ai_settings.screen_width,ai_settings.screen_height))
    pygame.display.set_caption("Alien lnvasion")
    #创建一艘飞船
    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循环中,不应从列表或编组中删除条目,因此必须遍历编组的副本。我们使用了方法copy()来设置for循环(见❶),这让我们能够在循环中修改bullets。我们检查每颗子弹,看看它是否已从屏幕顶端消失(见❷)。如果是这样,就将其从bullets中删除(见❸)。在❹处,我们使用了一条print语句,以显示当前还有多少颗子弹,从而核实已消失的子弹确实删除了。如果这些代码没有问题,我们发射子弹后查看终端窗口时,将发现随着子弹一颗颗地在屏幕顶端消失,子弹数将逐渐降为零。运行这个游戏并确认子弹已被删除后,将这条print语句删除。如果你留下这条语句,游戏的速度将大大降低,因为将输出写入到终端而花费的时间比将图形绘制到游戏窗口花费的时间还多。

限制子弹数量

很多射击游戏都对可同时出现在屏幕上的子弹数量进行限制,以鼓励玩家有目标地射击。下面在游戏《外星人入侵》中作这样的限制。首先,在settings.py中存储所允许的最大子弹数:
settings.py

class Settings():
    """存储《外星人入侵》的所有设置的类"""
    def __init__(self):
        """初始化游戏的设置"""
        #屏幕设置
        self.screen_width=1200
        self.screen_height=800
        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
        self.bullet_allowed=3

(靠靠靠, self.bullet_speed_factor=1原来不用删掉啊)
这将未消失的子弹数限制为3颗。在game_functions.py的check_keydown_events()中,我们在创建新子弹前检查未消失的子弹数是否小于该设置:
game_functions.py

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中
        if len(bullets)<ai_settings.bullets_allowed:
            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
    elif 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)
    ship.blitme()
    # 在飞船和外星人后面重绘所有子弹
    for bullet in bullets.sprites():
        bullet.draw_bullet()
    ship.blitme()
    # 让最近绘制的屏幕可见
    pygame.display.flip()

玩家按空格键时,我们检查bullets的长度。如果len(bullets)小于3,我们就创建一个新子弹;但如果已有3颗未消失的子弹,则玩家按空格键时什么都不会发生。如果你现在运行这个游戏,屏幕上最多只能有3颗子弹。

创建函数update_bullets()

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

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中
        if len(bullets)<ai_settings.bullets_allowed:
            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
    elif 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)
    ship.blitme()
    # 在飞船和外星人后面重绘所有子弹
    for bullet in bullets.sprites():
        bullet.draw_bullet()
    ship.blitme()
    # 让最近绘制的屏幕可见
    pygame.display.flip()
def update_bullets(bullets):
    """更新子弹的位置,并删除已消失的子弹"""
    #更新子弹的位置
    bullets.update()
    #删除已消失的子弹
    for bullet in bullets.copy():
        if bullet.rect.bottom<=0:
            bullets.remove(bullet)

update_bullets()的代码是从alien_invasion.py剪切并粘贴而来的,它只需要一个参数,即编组bullets。alien_invasion.py中的while循环又变得很简单了:
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()
    screen =pygame.display.set_mode((ai_settings.screen_width,ai_settings.screen_height))
    pygame.display.set_caption("Alien lnvasion")
    #创建一艘飞船
    ship = Ship(ai_settings,screen)
    #创建一个用于存储子弹的编组
    bullets=Group()
    #开始游戏的主循环
    while True:
        gf.check_events(ai_settings,screen,ship,bullets)
        ship.update()
        gf.update_bullets(bullets)
        gf.update_screen(ai_settings,screen,ship,bullets)
run_game()

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

创建函数fire_bullet()

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

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.bullets_allowed:
        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
    elif 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)
    ship.blitme()
    # 在飞船和外星人后面重绘所有子弹
    for bullet in bullets.sprites():
        bullet.draw_bullet()
    ship.blitme()
    # 让最近绘制的屏幕可见
    pygame.display.flip()
def update_bullets(bullets):
    """更新子弹的位置,并删除已消失的子弹"""
    #更新子弹的位置
    bullets.update()
    #删除已消失的子弹
    for bullet in bullets.copy():
        if bullet.rect.bottom<=0:
            bullets.remove(bullet)

函数fire_bullet()只包含玩家按空格键时用于发射子弹的代码;在check_keydown_events()中,我们在玩家按空格键时调用fire_bullet()。请再次运行alien_invasion.py,确认发射子弹时依然没有错误。

我的感想

额,敲了三天,感觉云里雾里的,头好晕,根本没看懂啊,迷迷糊糊的,只是跟着敲而已,这该咋办啊,有人知道这该怎么学么??

  • 3
    点赞
  • 15
    收藏
    觉得还不错? 一键收藏
  • 5
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 5
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值