Pytho练手项目一《外星人入侵1》

版权声明:敲一敲看一看,记得评论告诉我错误 https://blog.csdn.net/idealhunting/article/details/88378590

一、游戏简介

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

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

二、项目环境准备

        本机环境Ubuntu16.04+python3.6.5+PyCharm+Anaconda

2.1安装Pygame

        命令形式

sudo apt-get install python-pygame

        PyCharm图形操作形式

通过File 进入Setting 选择 Project 下的interpreter选项 选择添加 其他库添加方法类似,注意有时需要先更新pip

 三、开始项目

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

        首先,我们创建一个空的Pygame窗口。使用Pygame编写的游戏的基本结构如下:

#alien_invasion.py
import sys
import pygame

def run_game():
    #初始化游戏并创建一个屏幕对象
    pygame.init() #1
    screen = pygame.display.set_mode((1200,800)) #2
    pygame.display.set_caption("Alien Invasion")
    #设置背景色
    bg_color = (230,230,230)

    #开始游戏的主循环
    while True:                             #3

        #监视键盘和鼠标事件
        for event in pygame.event.get():     #4
            if event.type == pygame.QUIT:    #5
                sys.exit()
        #每次循环时都重绘屏幕
        screen.fill(bg_color)
        #让最近绘制的屏幕可见
        pygame.display.flip()                #6

run_game()

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

3.2设置背景色

......
pygame.display.set_caption("Alien Invasion")
    #设置背景色 1
    bg_color = (230,230,230)
......

  #每次循环时都重绘屏幕   2
        screen.fill(bg_color)
        #让最近绘制的屏幕可见
        pygame.display.flip()                
......

         ......表示省略,参加上边代码

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

3.3创建设置类

        每次给游戏添加新功能时,通常也将引入一些新设置。下面来编写一个名为 settings 的模块,其中包含一个名为 Settings 的类,用于将所有设置存储在一个地方,以免在代码中到处添加设置。这样,我们就能传递一个设置对象,而不是众多不同的设置。另外,这让函数调用更简单,且在项目增大时修改游戏的外观更容易:要修改游戏,只需修改settings.py中的一些值,而无需查找散布在文件中的不同设置。下面是最初的 Settings 类:

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

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

首先导入Settins :from settings import Settings

添加修改代码 :#1 #2 #3

#alien_invasion.py
import sys
import pygame

from settings import  Settings

def run_game():
    #初始化pygame、设置和屏幕对象
    pygame.init() #1
    ai_settings = Settings()                  #1
    screen = pygame.display.set_mode(         #2
        (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)     #3
        #让最近绘制的屏幕可见
        pygame.display.flip()

run_game()

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

3.4 添加飞船图像

        下面将飞船加入到游戏中。为了在屏幕上绘制玩家的飞船,我们将加载一幅图像,再使用Pygame方法 blit() 绘制它。

        为游戏选择素材时,务必要注意许可。最安全、最不费钱的方式是使用http://pixabay.com/等网站提供的图形,这些图形无需许可,你可以对进行修改。

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

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

。这个文件的背景色与这个项目使用的设置相同。请在主项目文件夹(alien_invasion)中新建一个文件夹,将其命名为images,并将文件ship.bmp保存到这个文件夹中。

3.4.1创建 Ship 类

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

import pygame
class Ship():
    def __init__(self, screen):
        """初始化飞船并设置其初始位置"""
        self.screen = screen

        # 加载飞船图像并获取其外接矩形 
        self.image = pygame.image.load('images/ship.bmp')  #1
        self.rect = self.image.get_rect()                  #2
        self.screen_rect = screen.get_rect()               #3

        # 将每艘新飞船放在屏幕底部中央 
        self.rect.centerx = self.screen_rect.centerx       #4
        self.rect.bottom = self.screen_rect.bottom
    def blitme(self):                                     #5
        """在指定位置绘制飞船"""
        self.screen.blit(self.image, self.rect)

        首先,我们导入了模块 pygame 。 Ship 的方法 __init__() 接受两个参数:引用 self 和 screen ,其中后者指定了要将飞船绘制到什么地方。为加载图像,我们调用了 pygame.image.load() (见1)。这个函数返回一个表示飞船的surface,而我们将这个surface存储到了 self.image 中。

        加载图像后,我们使用 get_rect() 获取相应surface的属性 rect (见2)。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 中(见3),再将 self.rect.centerx (飞船中心的x坐标)设置为表示屏幕的矩形的属性 centerx (见4),并将 self.rect.bottom (飞船下边缘的y坐标)设置为表示屏幕的矩形的属性 bottom 。 Pygame将使用这些 rect 属性来放置飞船图像,使其与屏幕下边缘对齐并水平居中。在5处,我们定义了方法 blitme() ,它根据 self.rect 指定的位置将图像绘制到屏幕上。

4.2在屏幕上绘制飞船

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

首先添加  Ship  :from ship import  Ship

...... 表省略 参加前面文件

#alien_invasion.py
......
from settings import  Settings
from ship import Ship

......
    #创建一艘飞船
    ship = Ship(screen)              #1
......   
        ship.blitme()
        #让最近绘制的屏幕可见
        pygame.display.flip()         #2

run_game()

         我们导入 Ship 类,并在创建屏幕后创建一个名为 ship 的 Ship 实例。必须在主 while 循环前面创建该实例(见1),以免每次循环时都创建一艘飞船。填充背景后,我们调用 ship.blitme() 将飞船绘制到屏幕上,确保它出现在背景前面(见2)。现在如果运行alien_invasion.py,将看到飞船位于空游戏屏幕底部中央,如图所示。

5 重构:模块game——function

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

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

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

          下面来修改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():
    -- snip --
    # 开始游戏主循环
    while True:
        gf.check_events()
        # 让最近绘制的屏幕可见
        -- snip --

         这个模块中导入了事件检查循环要使用的 sys 和 pygame 。当前,函数 check_events() 不需要任何形参,其函数体复制了alien_invasion.py的事件循环。下面来修改alien_invasion.py,使其导入模块 game_functions ,并将事件循环替换为对函数
check_events() 的调用:

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

#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
- snip --
    # 开始游戏主循环
    while True:
        gf.check_events()
        gf.update_screen(ai_settings, screen, ship)
run_game()

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

6.驾驶飞船

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

6.1 响应按键

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

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

#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:   #1
             if event.key == pygame.K_RIGHT: #2
                #向右移动飞船 
                ship.rect.centerx += 1      #3

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 事件时作出响应(见1)。我们读取属性 event.key ,以检查按下的是否是右箭头键( pygame.K_RIGHT )(见2)。如果按下的是右箭头键,就将 ship.rect.centerx 的值加1,从而将飞船向右移动(见3)。在 alien_invasion.py 中,我们需要更新调用的 check_events() 代码,将 ship 作为实参传递给它:

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

# 开始游戏主循环
while True:
    gf.check_events(ship)
    gf.update_screen(ai_settings, screen, ship)

6.2 允许不断移动

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

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

.......        
        self.rect.bottom = self.screen_rect.bottom

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

    def update(self):             #2
        """根据移动标志调整飞船的位置"""
        if self.moving_right:
            self.rect.centerx += 1

    def blitme(self):                                 
.......

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

#game_functions.py
def check_events(ship):
    """响应按键和鼠标事件"""
    for event in pygame.event.get():
        -- snip --
        elif event.type == pygame.KEYDOWN:
            if event.key == pygame.K_RIGHT:
                ship.moving_right = True  #1

        elif event.type == pygame.KEYUP: #2
            if event.key == pygame.K_RIGHT:
                ship.moving_right = False

        在1处,我们修改了游戏在玩家按下右箭头键时响应的方式:不直接调整飞船的位置,而只
是将 moving_right 设置为 True 。在处,我们添加了一个新的 elif 代码块,用于响应 KEYUP 事件:玩家松开右箭头键( K_RIGHT )时,我们将 moving_right 设置为 False 。]

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

# 开始游戏主循环
while True:
gf.check_events(ship)
ship.update()
gf.update_screen(ai_settings, screen, ship)

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

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

6.3左右移动

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

ship.py

def __init__(self, screen):
    -- snip --
    # 移动标志
    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

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

game_functions.py

        elif event.type == pygame.KEYDOWN:   #1
             if event.key == pygame.K_RIGHT: #2
                 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

        如果因玩家按下 K_LEFT 键而触发了 KEYDOWN 事件,我们就将 moving_left 设置为 True ;如果因玩家松开 K_LEFT 而触发了 KEYUP 事件,我们就将 moving_left 设置为 False 。这里之所以可以使用elif 代码块,是因为每个事件都只与一个键相关联;如果玩家同时按下了左右箭头键,将检测到两个不同的事件。
        如果此时运行alien_invasion.py,将能够不断地左右移动飞船;如果你同时按左右箭头键,飞船将纹丝不动。

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

6.4 调整飞船的速度

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

class Settings():
    """一个存储游戏《外星人入侵》的所有设置的类"""
    def __init__(self):
        --snip --
        # 飞船的设置
        self.ship_speed_factor = 1.5

         我们将 ship_speed_factor 的初始值设置成了 1.5 。需要移动飞船时,我们将移动1.5像素而不是1像素。
        通过将速度设置指定为小数值,可在后面加快游戏的节奏时更细致地控制飞船的速度。然而,rect 的 centerx 等属性只能存储整数值,因此我们需要对 Ship 类做些修改:

class Ship():

    def __init__(self, ai_settings, screen):   #1
        """初始化飞船并设置其初始位置"""
        self.screen = screen
        self.ai_settings = ai_settings  #2
        -- snip --

        # 将每艘新飞船放在屏幕底部中央
        -- snip --

        # 在飞船的属性center中存储小数值
        self.center = float(self.rect.centerx)  #3
        # 移动标志
        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 #4
        if self.moving_left:
            self.center -= self.ai_settings.ship_speed_factor
        
        # 根据self.center更新rect对象
        self.rect.centerx = self.center #5
    def blitme(self):
            -- snip --

        在1处,我们在 __init__() 的形参列表中添加了 ai_settings ,让飞船能够获取其速度设置。接下来,我们将形参 ai_settings 的值存储在一个属性中,以便能够在 update() 中使用它(见2)。鉴于现在调整飞船的位置时,将增加或减去一个单位为像素的小数值,因此需要将位置存储在一
个能够存储小数值的变量中。可以使用小数来设置 rect 的属性,但 rect 将只存储这个值的整数部分。为准确地存储飞船的位置,我们定义了一个可存储小数值的新属性 self.center (见3)。我们使用函数 float() 将 self.rect.centerx 的值转换为小数,并将结果存储到s elf.center 中。
现在在 update() 中调整飞船的位置时,将 self.center 的值增加或减去 ai_settings.ship_speed_factor 的 值 ( 见4 )。 更 新 self.center 后 , 我 们 再 根 据 它 来 更 新 控 制 飞 船 位 置 的self.rect.centerx (见5)。 self.rect.centerx 将只存储 self.center 的整数部分,但对显示飞船而言,这问题不大。在 alien_invasion.py 中创建 Ship 实例时,需要传入实参 ai_settings :
alien_invasion.py

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

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

6.5 限制飞船的活动范围

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

 def update(self):
        """根据移动标志调整飞船的位置"""
        # 更新飞船的center值,而不是rect
        if self.moving_right and self.rect.right < self.screen_rect.right: #1
            self.center += self.ai_settings.ship_speed_factor
        if self.moving_left and self.rect.left > 0:              #2
            self.center -= self.ai_settings.ship_speed_factor

        # 根据self.center更新rect对象
        self.rect.centerx = self.center

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

6.6 重构

        随着游戏开发的进行,函数 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() 更简单,代码结构更清晰。这样,在其中响应其他玩家输入时将更容易。

7. 简单回顾

        下一节将添加射击功能,这需要新增一个名为bullet.py的文件,并对一些既有文件进行修改。当前,我们有四个文件,其中包含很多类、函数和方法。添加其他功能之前,为让你清楚这个项目的组织结构,先来回顾一下这些文件。

7.1     alien_invasion.py

        主文件alien_invasion.py创建一系列整个游戏都要用到的对象:存储在 ai_settings 中的设置、存储在 screen 中的主显示surface以及一个飞船实例。文件 alien_invasion.py 还包含游戏的主循环,这是一个调用 check_events() 、 ship.update() 和 update_screen() 的 while 循环。要 玩 游 戏 《 外 星 人 入 侵 》, 只 需 运 行 文 件 alien_invasion.py 。 其 他 文 件 ( settings.py 、game_functions.py、ship.py)包含的代码被直接或间接地导入到这个文件中。

7.2    settings.py

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

7.3    game_functions.py

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

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

补充:内容来源 《python编程:从入门到实践》 

本文只做学习记录使用,其中部分代码出现纰漏,可参照原文或源代码 

这里附上相关资源(包括原文,原代码,及本人最后修订代码):https://download.csdn.net/download/idealhunting/11012497

猜你喜欢

转载自blog.csdn.net/idealhunting/article/details/88378590
今日推荐