第十二章 武装飞船

12.1 规划项目

开发大型项目时,制定好计划后再动手编写代码很重要,本项目要实现的功能有:

  1. 玩家可以控制出现在屏幕底部的飞船,飞船可以左右移动和射击
  2. 一群外星人出现在屏幕上部,并向下方移动
  3. 玩家的任务是射击消灭外星人
  4. 一波外星人消灭后,下一波外星人向下移动的速度会更快
  5. 外星人撞到飞船或到达屏幕底部,玩家失败
  6. 玩家的飞船有三条命

12.2 安装 Pygame

使用 pycharm 开发游戏

  1. 创建一个 alien_invasion 的项目
    Alt

  2. 安装 pygame ,文件 -> 设置 -> 项目:alein_invasion -> python 解释器 -> ‘+’
    Alt

  3. 勾选对勾,点安装包
    Alt

  4. 显示安装成功
    Alt

  5. 使用代码验证安装成功(代码在运行图片后,直接复制即可)
    Alt

import pygame, sys
from pygame.locals import *

white = 255,255,255
black = 0,0,0

pygame.init()

screen = pygame.display.set_mode((600, 500))
myfront = pygame.font.Font(None,60)
textImage = myfront.render("Hello Pygame", True, black)

while True:
    for event in pygame.event.get():
        if event.type in (QUIT, KEYDOWN):
            sys.exit()
    screen.fill(white)
    screen.blit(textImage, (100, 100))
    pygame.display.update()

12.3 开始游戏项目

首先创建一个游戏窗口,供之后来绘制游戏元素

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

新建一个 alien_invasion.py 文件,录入如下代码

 # alien_invasion.py
import sys
import pygame

class AlienInvasion:
    '''管理游戏资源和行为的类'''

    def __init__(self):
        '''初始化游戏并创建游戏资源'''
        pygame.init()  # 初始化背景设置

        self.screen = pygame.display.set_mode((1200, 800)) # 创建一个显示窗口,并赋值给 self.screen
        pygame.display.set_caption("Alien Invasion") #窗口显示标题

    def run_game(self):
        '''开始游戏的主循环'''
        while True:
            # 监控键盘和鼠标事件
            for event in pygame.event.get():
                if event.type == pygame.QUIT:
                    sys.exit()

            # 显示最近绘制的屏幕
            pygame.display.flip()

if __name__ == '__main__':
    # 创建游戏实例,并运行游戏
    ai = AlienInvasion()
    ai.run_game()

运行结果如下:(红色的1200和800是我写上去的,不是运行效果)
Alt

12.3.2 设置背景色

在 _ _ init _ _ (self) 方法末尾设置背景色
在pygame中,颜色是以RGB值指定的,三原色分别是红、绿、蓝,每个的取值是0-255
我们选取(230,230,230),并赋值给 bg_color
调用方法fill() 让背景色填充屏幕,它只有一个参数,即只接受一种颜色

 # alien_invasion.py
import sys
import pygame

class AlienInvasion:
    '''管理游戏资源和行为的类'''

    def __init__(self):
        '''初始化游戏并创建游戏资源'''
        pygame.init()  # 初始化背景设置

        self.screen = pygame.display.set_mode((1200, 800)) # 创建一个显示窗口,并赋值给 self.screen
        pygame.display.set_caption("Alien Invasion") #窗口显示标题
        self.bg_color = (230, 230, 230) #设置背景颜色

    def run_game(self):
        '''开始游戏的主循环'''
        while True:
            # 监控键盘和鼠标事件
            for event in pygame.event.get():
                if event.type == pygame.QUIT:
                    sys.exit()

            # 每次循环都重绘屏幕
            self.screen.fill(self.bg_color)

            # 显示最近绘制的屏幕
            pygame.display.flip()

if __name__ == '__main__':
    # 创建游戏实例,并运行游戏
    ai = AlienInvasion()
    ai.run_game()

运行结果如下,发现背景颜色变成灰色了
Alt

12.3.3 创建设置类

随着引入的功能越来越多,我们要设置的东西也越多,比如上文中的屏幕尺寸、背景色等,所以我们要单独编写一个 settings.py 的模块,用来存储游戏的所有设置,方便后续修改
创建一个settings.py的文件

# settings.py
class Settings:
    '''存储游戏中所有设置的类'''

    def __init__(self):
        '''初始化游戏设置'''
        #设置屏幕大小和背景色
        self.screen_width = 1200
        self.screen_height = 800
        self.bg_color = (230, 230, 230)

修改 alien_invaison.py中的代码

  1. 增加 settings 模块中的 Settings 类
  2. 创建一个settings 实例
  3. 将屏幕宽、高移入设置中
  4. 将背景颜色移入设置中
# alien_invaison.py
import sys
import pygame
from settings import Settings

class AlienInvasion:
    '''管理游戏资源和行为的类'''

    def __init__(self):
        '''初始化游戏并创建游戏资源'''
        pygame.init()  # 初始化背景设置
        self.settings = Settings() # 创建一个settings 实例

        self.screen = pygame.display.set_mode((self.settings.screen_width, self.settings.screen_height)) # 创建一个显示窗口,并赋值给 self.screen
        pygame.display.set_caption("Alien Invasion") #窗口显示标题

    def run_game(self):
        '''开始游戏的主循环'''
        while True:
            # 监控键盘和鼠标事件
            for event in pygame.event.get():
                if event.type == pygame.QUIT:
                    sys.exit()

            # 每次循环都重绘屏幕
            self.screen.fill(self.settings.bg_color)

            # 显示最近绘制的屏幕
            pygame.display.flip()

if __name__ == '__main__':
    # 创建游戏实例,并运行游戏
    ai = AlienInvasion()
    ai.run_game()

修改后,效果不变,但代码更有条理性,执行效果如下:
Alt

12.4 添加飞船图像

在项目文件夹(alien_invasion)中创建一个新文件,命名为 images ,并将下载的飞船图片放入其中,图片命名为:ship.bmp
Alt
注:使用 .png 图片可能会报错,建议使用 .bmp 图片

12.4.1 创建 Ship 类

有了飞船图片后,我们要将其显示到屏幕上
创建 ship.py 文件,在其中创建 Ship 类,负责管理飞船的大部分行为
ship.py文件

# ship.py
import pygame

class Ship:
    '''管理飞船的类'''

    def __init__(self, ai_game):
        '''初始化飞船,并设置其初始位置'''
        self.screen = ai_game.screen
        self.screen_rect = ai_game.screen.get_rect()

        # 加载飞船图像并获取其外接矩形
        self.image = pygame.image.load(r"images/ship.bmp")
        self.rect = self.image.get_rect()

        # 对于每艘新飞船,将其放在屏幕底部的中央
        self.rect.midbottom = self.screen_rect.midbottom

    def blitme(self):
        '''在指定位置绘制飞船'''
        self.screen.blit(self.image, self.rect)

12.4.2在屏幕上绘制飞船

调用ship.blitme()方法,将飞船显示到屏幕上
修改后的alien_invasion.py文件如下

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

class AlienInvasion:
    '''管理游戏资源和行为的类'''

    def __init__(self):
        '''初始化游戏并创建游戏资源'''
        pygame.init()  # 初始化背景设置
        self.settings = Settings() # 创建一个settings 实例

        self.screen = pygame.display.set_mode((self.settings.screen_width, self.settings.screen_height)) # 创建一个显示窗口,并赋值给 self.screen
        pygame.display.set_caption("Alien Invasion") #窗口显示标题
        self.ship = Ship(self)


    def run_game(self):
        '''开始游戏的主循环'''
        while True:
            # 监控键盘和鼠标事件
            for event in pygame.event.get():
                if event.type == pygame.QUIT:
                    sys.exit()

            # 每次循环都重绘屏幕
            self.screen.fill(self.settings.bg_color)
            self.ship.blitme()
            
            # 显示最近绘制的屏幕
            pygame.display.flip()

if __name__ == '__main__':
    # 创建游戏实例,并运行游戏
    ai = AlienInvasion()
    ai.run_game()

执行效果如下:
Alt

12.5 重构

在大型项目中,在添加新代码前要重构既有代码,旨在简化既有代码的结构,使其更容易扩展
run_game()的方法越来越长,可以拆分成两个辅助方法: _check_event() 和 _update_screen() ;辅助方法在类中执行任务,但并不是通过实例调用的,辅助方法的名称以单下划线开头

12.5.1 方法 _check_events()

将管理事件的代码移到 _check_events() 中

12.5.2方法 _update_screen()

将检查是否单击了关闭窗口按钮的代码移到 _update_screen() 中

12.5.1和12.5.2修改后的代码如下:

# alien_invasion.py
import sys
import pygame

from settings import Settings
from ship import Ship

class AlienInvasion:
    '''管理游戏资源和行为的类'''

    def __init__(self):
        '''初始化游戏并创建游戏资源'''
        pygame.init()  # 初始化背景设置
        self.settings = Settings() # 创建一个settings 实例

        self.screen = pygame.display.set_mode((self.settings.screen_width, self.settings.screen_height)) # 创建一个显示窗口,并赋值给 self.screen
        pygame.display.set_caption("Alien Invasion") #窗口显示标题
        self.ship = Ship(self)

    def run_game(self):
        '''开始游戏的主循环'''
        while True:
            self._check_event()  # 监控键盘和鼠标事件

            self._update_screen()  # 更新屏幕上的图像,并更新到新屏幕

            # 显示最近绘制的屏幕
            pygame.display.flip()
    def _check_event(self):
        # 监控键盘和鼠标事件
        for event in pygame.event.get():
            if event.type == pygame.QUIT:
                sys.exit()

    def _update_screen(self):
        '''更新屏幕上的图像,并更新到新屏幕'''
        self.screen.fill(self.settings.bg_color)
        self.ship.blitme()

if __name__ == '__main__':
    # 创建游戏实例,并运行游戏
    ai = AlienInvasion()
    ai.run_game()

运行程序后,显示效果与12.4.2相同

12.6 驾驶飞船

玩家可以通过左右键移动飞船

12.6.1 响应按键

各种事件可以通过方法 pygame.event.get()获取,按键可以被注册为 KEYDOWN 事件
在 alien_invasion.py 文件中,增加“按右键,飞船向右移动功能”,移动速度为1个像素
在_check_event()中添加 elif 代码块

# alien_invasion.py
import sys
import pygame

from settings import Settings
from ship import Ship

class AlienInvasion:
    '''管理游戏资源和行为的类'''

    def __init__(self):
        '''初始化游戏并创建游戏资源'''
        pygame.init()  # 初始化背景设置
        self.settings = Settings() # 创建一个settings 实例

        self.screen = pygame.display.set_mode((self.settings.screen_width, self.settings.screen_height)) # 创建一个显示窗口,并赋值给 self.screen
        pygame.display.set_caption("Alien Invasion") #窗口显示标题
        self.ship = Ship(self)

    def run_game(self):
        '''开始游戏的主循环'''
        while True:
            self._check_event()  # 监控键盘和鼠标事件
            self._update_screen()  # 更新屏幕上的图像,并更新到新屏幕

            # 显示最近绘制的屏幕
            pygame.display.flip()
    def _check_event(self):
        # 监控键盘和鼠标事件
        for event in pygame.event.get():
            if event.type == pygame.QUIT:
                sys.exit()
            elif event.type == pygame.KEYDOWN:
                if event.key == pygame.K_RIGHT:
                    self.ship.rect.x += 1 #检测到按下右键,飞船右移一个像素

    def _update_screen(self):
        '''更新屏幕上的图像,并更新到新屏幕'''
        self.screen.fill(self.settings.bg_color)
        self.ship.blitme()

if __name__ == '__main__':
    # 创建游戏实例,并运行游戏
    ai = AlienInvasion()
    ai.run_game()

按下一次右键,飞船移动一个像素,执行效果见下图
Alt

12.6.2 允许持续移动

我们希望按住右键不放时,飞船不断向右移动,直到松开右键为止
在飞船的属性中增加标志位 moving_right == False ,在飞创的方法中增加 update() ,用来检查moveing_right 的状态,如果为True,就调整飞船位置
对 ship.py 进行修改

# ship.py
import pygame

class Ship:
    '''管理飞船的类'''

    def __init__(self, ai_game):
        '''初始化飞船,并设置其初始位置'''
        self.screen = ai_game.screen
        self.screen_rect = ai_game.screen.get_rect()

        # 加载飞船图像并获取其外接矩形
        self.image = pygame.image.load(r"images/ship.bmp")
        self.rect = self.image.get_rect()

        # 对于每艘新飞船,将其放在屏幕底部的中央
        self.rect.midbottom = self.screen_rect.midbottom

        # 增加移动标记
        self.moving_right == False

    def update(self):
        '''根据移动标记状态调整调整飞船位置'''
        if self.moving_right:
            self.rect.x += 1


    def blitme(self):
        '''在指定位置绘制飞船'''
        self.screen.blit(self.image, self.rect)


修改alien_invasion.py 文件中的 _check_events(),使玩家按下右键时 moving_right 为True,松开为False,最后在 run_game(self) 函数中调用 self.ship.update()函数

# alien_invasion.py
import sys
import pygame

from settings import Settings
from ship import Ship

class AlienInvasion:
    '''管理游戏资源和行为的类'''

    def __init__(self):
        '''初始化游戏并创建游戏资源'''
        pygame.init()  # 初始化背景设置
        self.settings = Settings() # 创建一个settings 实例

        self.screen = pygame.display.set_mode((self.settings.screen_width, self.settings.screen_height)) # 创建一个显示窗口,并赋值给 self.screen
        pygame.display.set_caption("Alien Invasion") #窗口显示标题
        self.ship = Ship(self)

    def run_game(self):
        '''开始游戏的主循环'''
        while True:
            self._check_event()  # 监控键盘和鼠标事件
            self.ship.update() # 飞船的位置信息更新
            self._update_screen()  # 更新屏幕上的图像,并更新到新屏幕

            # 显示最近绘制的屏幕
            pygame.display.flip()
    def _check_event(self):
        # 监控键盘和鼠标事件
        for event in pygame.event.get():
            if event.type == pygame.QUIT:
                sys.exit()
            elif event.type == pygame.KEYDOWN:
                if event.key == pygame.K_RIGHT:
                    self.ship.moving_right = True
            elif event.type == pygame.KEYUP:
                if event.key == pygame.K_RIGHT:
                    self.ship.moving_right = False

    def _update_screen(self):
        '''更新屏幕上的图像,并更新到新屏幕'''
        self.screen.fill(self.settings.bg_color)
        self.ship.blitme()

if __name__ == '__main__':
    # 创建游戏实例,并运行游戏
    ai = AlienInvasion()
    ai.run_game()

按住右键飞船持续向右移动,松开停止移动,可以移除右侧界面,执行结果如下图:
Alt

12.6.3 左右移动

同样的方法,增加向左移动功能
对 ship.py 文件的 _ _ init_ _ () 和 update() 方法进行修改

# ship.py
import pygame

class Ship:
    '''管理飞船的类'''

    def __init__(self, ai_game):
        '''初始化飞船,并设置其初始位置'''
        self.screen = ai_game.screen
        self.screen_rect = ai_game.screen.get_rect()

        # 加载飞船图像并获取其外接矩形
        self.image = pygame.image.load(r"images/ship.bmp")
        self.rect = self.image.get_rect()

        # 对于每艘新飞船,将其放在屏幕底部的中央
        self.rect.midbottom = self.screen_rect.midbottom

        # 增加移动标记
        self.moving_right = False
        self.moving_left = False

    def update(self):
        '''根据移动标记状态调整调整飞船位置'''
        if self.moving_right:
            self.rect.x += 1
        if self.moving_left:
            self.rect.x -= 1


    def blitme(self):
        '''在指定位置绘制飞船'''
        self.screen.blit(self.image, self.rect)

对 文件中的 _chek_events(self)方法进行修改

# alien_invasion.py
import sys
import pygame

from settings import Settings
from ship import Ship

class AlienInvasion:
    '''管理游戏资源和行为的类'''

    def __init__(self):
        '''初始化游戏并创建游戏资源'''
        pygame.init()  # 初始化背景设置
        self.settings = Settings() # 创建一个settings 实例

        self.screen = pygame.display.set_mode((self.settings.screen_width, self.settings.screen_height)) # 创建一个显示窗口,并赋值给 self.screen
        pygame.display.set_caption("Alien Invasion") #窗口显示标题
        self.ship = Ship(self)

    def run_game(self):
        '''开始游戏的主循环'''
        while True:
            self._check_event()  # 监控键盘和鼠标事件
            self.ship.update() # 飞船的位置信息更新
            self._update_screen()  # 更新屏幕上的图像,并更新到新屏幕

            # 显示最近绘制的屏幕
            pygame.display.flip()
    def _check_event(self):
        # 监控键盘和鼠标事件
        for event in pygame.event.get():
            if event.type == pygame.QUIT:
                sys.exit()
            elif event.type == pygame.KEYDOWN:
                if event.key == pygame.K_RIGHT:
                    self.ship.moving_right = True
                elif event.key == pygame.K_LEFT:
                    self.ship.moving_left = True

            elif event.type == pygame.KEYUP:
                if event.key == pygame.K_RIGHT:
                    self.ship.moving_right = False
                elif event.key == pygame.K_LEFT:
                    self.ship.moving_left = False

    def _update_screen(self):
        '''更新屏幕上的图像,并更新到新屏幕'''
        self.screen.fill(self.settings.bg_color)
        self.ship.blitme()

if __name__ == '__main__':
    # 创建游戏实例,并运行游戏
    ai = AlienInvasion()
    ai.run_game()

执行代码发现,飞船可以左右持续移动了,但会超出屏幕界面,执行图如下:
Alt

Alt

12.6.4 调整飞船的速度

在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 = 2

在 ship.py 中,给Ship类添加属性settings,增加self.x属性,更新update()方法

# ship.py
import pygame

class Ship:
    '''管理飞船的类'''

    def __init__(self, ai_game):
        '''初始化飞船,并设置其初始位置'''
        self.screen = ai_game.screen
        self.screen_rect = ai_game.screen.get_rect()
        self.settings = ai_game.settings

        # 加载飞船图像并获取其外接矩形
        self.image = pygame.image.load(r"images/ship.bmp")
        self.rect = self.image.get_rect()

        # 对于每艘新飞船,将其放在屏幕底部的中央
        self.rect.midbottom = self.screen_rect.midbottom

        #飞船属性中存储x的值
        self.x = float(self.rect.x)

        # 增加移动标记
        self.moving_right = False
        self.moving_left = False

    def update(self):
        '''根据移动标记状态调整调整飞船位置'''
        if self.moving_right:
            self.rect.x += self.settings.ship_speed
        if self.moving_left:
            self.rect.x -= self.settings.ship_speed


    def blitme(self):
        '''在指定位置绘制飞船'''
        self.screen.blit(self.image, self.rect)

执行后,按左右键时飞船的移动速度明显加快

12.6.5 限制飞船的活动范围

飞船会飞到屏幕之外,修改使飞船只能在屏幕内移动,修改 ship.py 中的update()方法即可

# ship.py
import pygame

class Ship:
    '''管理飞船的类'''

    def __init__(self, ai_game):
        '''初始化飞船,并设置其初始位置'''
        self.screen = ai_game.screen
        self.screen_rect = ai_game.screen.get_rect()
        self.settings = ai_game.settings

        # 加载飞船图像并获取其外接矩形
        self.image = pygame.image.load(r"images/ship.bmp")
        self.rect = self.image.get_rect()

        # 对于每艘新飞船,将其放在屏幕底部的中央
        self.rect.midbottom = self.screen_rect.midbottom

        #飞船属性中存储x的值
        self.x = float(self.rect.x)

        # 增加移动标记
        self.moving_right = False
        self.moving_left = False

    def update(self):
        '''根据移动标记状态调整调整飞船位置'''
        if self.moving_right and self.rect.right < self.screen_rect.right:
            self.rect.x += self.settings.ship_speed
        if self.moving_left and self.rect.left > 0:
            self.rect.x -= self.settings.ship_speed


    def blitme(self):
        '''在指定位置绘制飞船'''
        self.screen.blit(self.image, self.rect)

执行程序,发现飞船不会跳出屏幕外了,效果如下图:
Alt

Alt

12.6.6重构_check_events()

方法_check_events()的代码越来越长,将其分解成两个方法,一个处理KEYDOWN事件,另一个处理KEYUP事件
在alien_invasion.py 文件中创建两个新的辅助方法:_check_keydown_events() 和_check_keyup_events()

# alien_invasion.py
import sys
import pygame

from settings import Settings
from ship import Ship

class AlienInvasion:
    '''管理游戏资源和行为的类'''

    def __init__(self):
        '''初始化游戏并创建游戏资源'''
        pygame.init()  # 初始化背景设置
        self.settings = Settings() # 创建一个settings 实例

        self.screen = pygame.display.set_mode((self.settings.screen_width, self.settings.screen_height)) # 创建一个显示窗口,并赋值给 self.screen
        pygame.display.set_caption("Alien Invasion") #窗口显示标题
        self.ship = Ship(self)

    def run_game(self):
        '''开始游戏的主循环'''
        while True:
            self._check_event()  # 监控键盘和鼠标事件
            self.ship.update() # 飞船的位置信息更新
            self._update_screen()  # 更新屏幕上的图像,并更新到新屏幕

            # 显示最近绘制的屏幕
            pygame.display.flip()
    def _check_event(self):
        # 监控键盘和鼠标事件
        for event in pygame.event.get():
            if event.type == pygame.QUIT:
                sys.exit()
            elif event.type == pygame.KEYDOWN:
                self._check_keydown_events(event)
            elif event.type == pygame.KEYUP:
                self._check_keyup_events(event)

    # 响应按键
    def _check_keydown_events(self, event):
        if event.key == pygame.K_RIGHT:
            self.ship.moving_right = True
        elif event.key == pygame.K_LEFT:
            self.ship.moving_left = True

    # 响应松开按键
    def _check_keyup_events(self, event):
        if event.key == pygame.K_RIGHT:
            self.ship.moving_right = False
        elif event.key == pygame.K_LEFT:
            self.ship.moving_left = False

    def _update_screen(self):
        '''更新屏幕上的图像,并更新到新屏幕'''
        self.screen.fill(self.settings.bg_color)
        self.ship.blitme()

if __name__ == '__main__':
    # 创建游戏实例,并运行游戏
    ai = AlienInvasion()
    ai.run_game()

修改后,执行效果与12.6.5相同

12.6.7 按Q键退出

添加一个快速结束游戏的快捷键:Q
在 文件的方法 _check_keydown_events(self, event) 中修改,修改后,按 'q‘ 立即退出游戏

# alien_invasion.py
import sys
import pygame

from settings import Settings
from ship import Ship

class AlienInvasion:
    '''管理游戏资源和行为的类'''

    def __init__(self):
        '''初始化游戏并创建游戏资源'''
        pygame.init()  # 初始化背景设置
        self.settings = Settings() # 创建一个settings 实例

        self.screen = pygame.display.set_mode((self.settings.screen_width, self.settings.screen_height)) # 创建一个显示窗口,并赋值给 self.screen
        pygame.display.set_caption("Alien Invasion") #窗口显示标题
        self.ship = Ship(self)

    def run_game(self):
        '''开始游戏的主循环'''
        while True:
            self._check_event()  # 监控键盘和鼠标事件
            self.ship.update() # 飞船的位置信息更新
            self._update_screen()  # 更新屏幕上的图像,并更新到新屏幕

            # 显示最近绘制的屏幕
            pygame.display.flip()
    def _check_event(self):
        # 监控键盘和鼠标事件
        for event in pygame.event.get():
            if event.type == pygame.QUIT:
                sys.exit()
            elif event.type == pygame.KEYDOWN:
                self._check_keydown_events(event)
            elif event.type == pygame.KEYUP:
                self._check_keyup_events(event)

    # 响应按键
    def _check_keydown_events(self, event):
        if event.key == pygame.K_RIGHT:
            self.ship.moving_right = True
        elif event.key == pygame.K_LEFT:
            self.ship.moving_left = True
        elif event.key == pygame.K_q:
            sys.exit()

    # 响应松开按键
    def _check_keyup_events(self, event):
        if event.key == pygame.K_RIGHT:
            self.ship.moving_right = False
        elif event.key == pygame.K_LEFT:
            self.ship.moving_left = False

    def _update_screen(self):
        '''更新屏幕上的图像,并更新到新屏幕'''
        self.screen.fill(self.settings.bg_color)
        self.ship.blitme()

if __name__ == '__main__':
    # 创建游戏实例,并运行游戏
    ai = AlienInvasion()
    ai.run_game()

12.6.8 在全屏模式下运行游戏

要实现全屏模式,可在 alien_invasion.py 文件的 _ _ init _ _ ()中修改
全屏显示,按 ”q“ 退出

# alien_invasion.py
import sys
import pygame

from settings import Settings
from ship import Ship

class AlienInvasion:
    '''管理游戏资源和行为的类'''

    def __init__(self):
        '''初始化游戏并创建游戏资源'''
        pygame.init()  # 初始化背景设置
        self.settings = Settings() # 创建一个settings 实例

        # 独立窗口模式
        #self.screen = pygame.display.set_mode((self.settings.screen_width, self.settings.screen_height)) # 创建一个显示窗口,并赋值给 self.screen

        # 全屏模式
        self.screen = pygame.display.set_mode((0,0), pygame.FULLSCREEN)
        self.settings.screen_width = self.screen.get_rect().width
        self.settings.screen_height = self.screen.get_rect().height

        pygame.display.set_caption("Alien Invasion") #窗口显示标题
        self.ship = Ship(self)

    def run_game(self):
        '''开始游戏的主循环'''
        while True:
            self._check_event()  # 监控键盘和鼠标事件
            self.ship.update() # 飞船的位置信息更新
            self._update_screen()  # 更新屏幕上的图像,并更新到新屏幕

            # 显示最近绘制的屏幕
            pygame.display.flip()
    def _check_event(self):
        # 监控键盘和鼠标事件
        for event in pygame.event.get():
            if event.type == pygame.QUIT:
                sys.exit()
            elif event.type == pygame.KEYDOWN:
                self._check_keydown_events(event)
            elif event.type == pygame.KEYUP:
                self._check_keyup_events(event)

    # 响应按键
    def _check_keydown_events(self, event):
        if event.key == pygame.K_RIGHT:
            self.ship.moving_right = True
        elif event.key == pygame.K_LEFT:
            self.ship.moving_left = True
        elif event.key == pygame.K_q:
            sys.exit()

    # 响应松开按键
    def _check_keyup_events(self, event):
        if event.key == pygame.K_RIGHT:
            self.ship.moving_right = False
        elif event.key == pygame.K_LEFT:
            self.ship.moving_left = False

    def _update_screen(self):
        '''更新屏幕上的图像,并更新到新屏幕'''
        self.screen.fill(self.settings.bg_color)
        self.ship.blitme()

if __name__ == '__main__':
    # 创建游戏实例,并运行游戏
    ai = AlienInvasion()
    ai.run_game()

12.7 简单回顾

12.7.1 alien_invasion.py

主程序

12.7.2 settings.py

各种设置

12.7.3 ship.py

管理飞船的属性和方法

12.8 射击

恢复独立窗口(不全屏)显示,添加射击功能,子弹在屏幕中向上飞行,达到屏幕上边缘后消失

12.8.1 添加子弹设置

在 settings.py 的 _ _ init _ _()方法中增加子弹设置

# settings.py
class Settings:
    '''存储游戏中所有设置的类'''

    def __init__(self):
        '''初始化游戏设置'''
        #设置屏幕大小和背景色
        self.screen_width = 1200
        self.screen_height = 800
        self.bg_color = (230, 230, 230)

        # 飞船设置
        self.ship_speed = 2

        # 子弹设置
        self.bullet_speed = 1.5
        self.bullet_width = 3
        self.bullet_height = 15
        self.bullet_color = (60, 60, 60)
    

12.8.2 创建Bullet类

新创建一个bullet.py文件

# bullet.py

import pygame
from pygame.sprite import Sprite

class Bullet(Sprite):
    '''管理飞船发射子弹的类'''
    def __init__(self, ai_game):
        '''在飞船当前位置创建一个子弹对象'''
        super().__init__()
        self.screen = ai_game.screen
        self.settings = ai_game.settings
        self.color = self.settings.bullet_color

        # 在(0,0)创建一个子弹矩形,再设置到正确位置
        self.rect = pygame.Rect(0,0,self.settings.bullet_width,self.settings.bullet_height)
        self.rect.midtop = ai_game.ship.rect.midtop

        # 存储用小数表示的子弹位置
        self.y = float(self.rect.y)

    def update(self):
        '''向上移动子弹'''
        self.y -= self.settings.bullet_speed
        self.rect.y = self.y  # 更新子弹rect的位置

    def draw_bullet(self):
        '''在屏幕上绘制子弹'''
        pygame.draw.rect(self.screen, self.color, self.rect)

12.8.3将子弹存储在编组中

在 文件的_ _ init _ _()创建一个实例,在 run_game() 中更新子弹的位置

# alien_invasion.py
import sys
import pygame

from settings import Settings
from ship import Ship

class AlienInvasion:
    '''管理游戏资源和行为的类'''

    def __init__(self):
        '''初始化游戏并创建游戏资源'''
        pygame.init()  # 初始化背景设置
        self.settings = Settings() # 创建一个settings 实例

        # 独立窗口模式
        self.screen = pygame.display.set_mode((self.settings.screen_width, self.settings.screen_height)) # 创建一个显示窗口,并赋值给 self.screen

        # 全屏模式
        '''
        self.screen = pygame.display.set_mode((0,0), pygame.FULLSCREEN)
        self.settings.screen_width = self.screen.get_rect().width
        self.settings.screen_height = self.screen.get_rect().height'''

        pygame.display.set_caption("Alien Invasion") #窗口显示标题
        self.ship = Ship(self)
        self.bullets = pygame.sprite.Group()

    def run_game(self):
        '''开始游戏的主循环'''
        while True:
            self._check_event()  # 监控键盘和鼠标事件
            self.ship.update() # 飞船的位置信息更新
            self.bullets.update() #子弹的位置更新
            self._update_screen()  # 更新屏幕上的图像,并更新到新屏幕

            # 显示最近绘制的屏幕
            pygame.display.flip()
    def _check_event(self):
        # 监控键盘和鼠标事件
        for event in pygame.event.get():
            if event.type == pygame.QUIT:
                sys.exit()
            elif event.type == pygame.KEYDOWN:
                self._check_keydown_events(event)
            elif event.type == pygame.KEYUP:
                self._check_keyup_events(event)

    # 响应按键
    def _check_keydown_events(self, event):
        if event.key == pygame.K_RIGHT:
            self.ship.moving_right = True
        elif event.key == pygame.K_LEFT:
            self.ship.moving_left = True
        elif event.key == pygame.K_q:
            sys.exit()

    # 响应松开按键
    def _check_keyup_events(self, event):
        if event.key == pygame.K_RIGHT:
            self.ship.moving_right = False
        elif event.key == pygame.K_LEFT:
            self.ship.moving_left = False

    def _update_screen(self):
        '''更新屏幕上的图像,并更新到新屏幕'''
        self.screen.fill(self.settings.bg_color)
        self.ship.blitme()

if __name__ == '__main__':
    # 创建游戏实例,并运行游戏
    ai = AlienInvasion()
    ai.run_game()

12.8.4 开火

为发射子弹,需要在 alien_invasion.py 中导入子弹类,并编写一个新方法_fire_bullet()
因为按下空格发射子弹,需要修改 _check_keydown_events()
确保子弹及时显示在屏幕上,需要修改_update_screen()

# alien_invasion.py
import sys
import pygame
from bullet import  Bullet

from settings import Settings
from ship import Ship

class AlienInvasion:
    '''管理游戏资源和行为的类'''

    def __init__(self):
        '''初始化游戏并创建游戏资源'''
        pygame.init()  # 初始化背景设置
        self.settings = Settings() # 创建一个settings 实例

        # 独立窗口模式
        self.screen = pygame.display.set_mode((self.settings.screen_width, self.settings.screen_height)) # 创建一个显示窗口,并赋值给 self.screen

        # 全屏模式
        '''
        self.screen = pygame.display.set_mode((0,0), pygame.FULLSCREEN)
        self.settings.screen_width = self.screen.get_rect().width
        self.settings.screen_height = self.screen.get_rect().height'''

        pygame.display.set_caption("Alien Invasion") #窗口显示标题
        self.ship = Ship(self)
        self.bullets = pygame.sprite.Group()

    def run_game(self):
        '''开始游戏的主循环'''
        while True:
            self._check_event()  # 监控键盘和鼠标事件
            self.ship.update() # 飞船的位置信息更新
            self.bullets.update() #子弹的位置更新
            self._update_screen()  # 更新屏幕上的图像,并更新到新屏幕

            # 显示最近绘制的屏幕
            pygame.display.flip()
    def _check_event(self):
        # 监控键盘和鼠标事件
        for event in pygame.event.get():
            if event.type == pygame.QUIT:
                sys.exit()
            elif event.type == pygame.KEYDOWN:
                self._check_keydown_events(event)
            elif event.type == pygame.KEYUP:
                self._check_keyup_events(event)

    # 响应按键
    def _check_keydown_events(self, event):
        if event.key == pygame.K_RIGHT:
            self.ship.moving_right = True
        elif event.key == pygame.K_LEFT:
            self.ship.moving_left = True
        elif event.key == pygame.K_q:
            sys.exit()
        elif event.key == pygame.K_SPACE:
            self._fire_bullet()

    # 响应松开按键
    def _check_keyup_events(self, event):
        if event.key == pygame.K_RIGHT:
            self.ship.moving_right = False
        elif event.key == pygame.K_LEFT:
            self.ship.moving_left = False

    def _fire_bullet(self):
        '''创建一颗子弹,并加入编组bullets中'''
        new_bullet = Bullet(self)
        self.bullets.add(new_bullet)

    def _update_screen(self):
        '''更新屏幕上的图像,并更新到新屏幕'''
        self.screen.fill(self.settings.bg_color)
        self.ship.blitme()
        for bullet in self.bullets.sprites():
            bullet.draw_bullet()

if __name__ == '__main__':
    # 创建游戏实例,并运行游戏
    ai = AlienInvasion()
    ai.run_game()

执行结果见下图
Alt

12.8.5 删除消失的子弹

我们需要删除消失的子弹(子弹移动到屏幕外,但依然存在),否则会消耗计算机内存
在 alien_invasion.py 文件的run_game()方法中,删除消失的子弹
注:for 循环在执行过程中,列表的长度不能变,所以需要创建一个副本

# alien_invasion.py
import sys
import pygame
from bullet import  Bullet

from settings import Settings
from ship import Ship

class AlienInvasion:
    '''管理游戏资源和行为的类'''

    def __init__(self):
        '''初始化游戏并创建游戏资源'''
        pygame.init()  # 初始化背景设置
        self.settings = Settings() # 创建一个settings 实例

        # 独立窗口模式
        self.screen = pygame.display.set_mode((self.settings.screen_width, self.settings.screen_height)) # 创建一个显示窗口,并赋值给 self.screen

        # 全屏模式
        '''
        self.screen = pygame.display.set_mode((0,0), pygame.FULLSCREEN)
        self.settings.screen_width = self.screen.get_rect().width
        self.settings.screen_height = self.screen.get_rect().height'''

        pygame.display.set_caption("Alien Invasion") #窗口显示标题
        self.ship = Ship(self)
        self.bullets = pygame.sprite.Group()

    def run_game(self):
        '''开始游戏的主循环'''
        while True:
            self._check_event()  # 监控键盘和鼠标事件
            self.ship.update() # 飞船的位置信息更新
            self.bullets.update() #子弹的位置更新
            # 删除消失的子弹
            for bullet in self.bullets.copy():
                if bullet.rect.bottom <= 0:
                    self.bullets.remove(bullet)
            # print(len(self.bullets)) 验证子弹消失代码,在终端窗口观察

            self._update_screen()  # 更新屏幕上的图像,并更新到新屏幕

            # 显示最近绘制的屏幕
            pygame.display.flip()
    def _check_event(self):
        # 监控键盘和鼠标事件
        for event in pygame.event.get():
            if event.type == pygame.QUIT:
                sys.exit()
            elif event.type == pygame.KEYDOWN:
                self._check_keydown_events(event)
            elif event.type == pygame.KEYUP:
                self._check_keyup_events(event)

    # 响应按键
    def _check_keydown_events(self, event):
        if event.key == pygame.K_RIGHT:
            self.ship.moving_right = True
        elif event.key == pygame.K_LEFT:
            self.ship.moving_left = True
        elif event.key == pygame.K_q:
            sys.exit()
        elif event.key == pygame.K_SPACE:
            self._fire_bullet()

    # 响应松开按键
    def _check_keyup_events(self, event):
        if event.key == pygame.K_RIGHT:
            self.ship.moving_right = False
        elif event.key == pygame.K_LEFT:
            self.ship.moving_left = False

    def _fire_bullet(self):
        '''创建一颗子弹,并加入编组bullets中'''
        new_bullet = Bullet(self)
        self.bullets.add(new_bullet)

    def _update_screen(self):
        '''更新屏幕上的图像,并更新到新屏幕'''
        self.screen.fill(self.settings.bg_color)
        self.ship.blitme()
        for bullet in self.bullets.sprites():
            bullet.draw_bullet()

if __name__ == '__main__':
    # 创建游戏实例,并运行游戏
    ai = AlienInvasion()
    ai.run_game()

12.8.6 限制子弹数量

需要限制子弹的数量,鼓励玩家有效射击
在 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 = 2

        # 子弹设置
        self.bullet_speed = 0.5
        self.bullet_width = 3
        self.bullet_height = 15
        self.bullet_color = (60, 60, 60)
        self.bullet_allowed = 3

在 alien_invasion.py 中检查子弹是否小于限定值,小于则产生新子弹,在_fire_bullet中修改

# alien_invasion.py
import sys
import pygame
from bullet import  Bullet

from settings import Settings
from ship import Ship

class AlienInvasion:
    '''管理游戏资源和行为的类'''

    def __init__(self):
        '''初始化游戏并创建游戏资源'''
        pygame.init()  # 初始化背景设置
        self.settings = Settings() # 创建一个settings 实例

        # 独立窗口模式
        self.screen = pygame.display.set_mode((self.settings.screen_width, self.settings.screen_height)) # 创建一个显示窗口,并赋值给 self.screen

        # 全屏模式
        '''
        self.screen = pygame.display.set_mode((0,0), pygame.FULLSCREEN)
        self.settings.screen_width = self.screen.get_rect().width
        self.settings.screen_height = self.screen.get_rect().height'''

        pygame.display.set_caption("Alien Invasion") #窗口显示标题
        self.ship = Ship(self)
        self.bullets = pygame.sprite.Group()

    def run_game(self):
        '''开始游戏的主循环'''
        while True:
            self._check_event()  # 监控键盘和鼠标事件
            self.ship.update() # 飞船的位置信息更新
            self.bullets.update() #子弹的位置更新
            # 删除消失的子弹
            for bullet in self.bullets.copy():
                if bullet.rect.bottom <= 0:
                    self.bullets.remove(bullet)
            # print(len(self.bullets)) 验证子弹消失代码,在终端窗口观察

            self._update_screen()  # 更新屏幕上的图像,并更新到新屏幕

            # 显示最近绘制的屏幕
            pygame.display.flip()
    def _check_event(self):
        # 监控键盘和鼠标事件
        for event in pygame.event.get():
            if event.type == pygame.QUIT:
                sys.exit()
            elif event.type == pygame.KEYDOWN:
                self._check_keydown_events(event)
            elif event.type == pygame.KEYUP:
                self._check_keyup_events(event)

    # 响应按键
    def _check_keydown_events(self, event):
        if event.key == pygame.K_RIGHT:
            self.ship.moving_right = True
        elif event.key == pygame.K_LEFT:
            self.ship.moving_left = True
        elif event.key == pygame.K_q:
            sys.exit()
        elif event.key == pygame.K_SPACE:
            self._fire_bullet()

    # 响应松开按键
    def _check_keyup_events(self, event):
        if event.key == pygame.K_RIGHT:
            self.ship.moving_right = False
        elif event.key == pygame.K_LEFT:
            self.ship.moving_left = False

    def _fire_bullet(self):
        '''创建一颗子弹,并加入编组bullets中'''
        if len(self.bullets) < self.settings.bullet_allowed:
            new_bullet = Bullet(self)
            self.bullets.add(new_bullet)

    def _update_screen(self):
        '''更新屏幕上的图像,并更新到新屏幕'''
        self.screen.fill(self.settings.bg_color)
        self.ship.blitme()
        for bullet in self.bullets.sprites():
            bullet.draw_bullet()

if __name__ == '__main__':
    # 创建游戏实例,并运行游戏
    ai = AlienInvasion()
    ai.run_game()

执行程序,发现只能发射3枚子弹,效果见下图:
Alt

12.8.7 创建方法 _update_bullets()

代码可以正常执行后,进行重构,将run_game()方法中与子弹相关代码放在一个新方法_update_bullets()中
在 alien_invasion.py 中创建_update_bullets()方法,并修改

# alien_invasion.py
import sys
import pygame
from bullet import  Bullet

from settings import Settings
from ship import Ship

class AlienInvasion:
    '''管理游戏资源和行为的类'''

    def __init__(self):
        '''初始化游戏并创建游戏资源'''
        pygame.init()  # 初始化背景设置
        self.settings = Settings() # 创建一个settings 实例

        # 独立窗口模式
        self.screen = pygame.display.set_mode((self.settings.screen_width, self.settings.screen_height)) # 创建一个显示窗口,并赋值给 self.screen

        # 全屏模式
        '''
        self.screen = pygame.display.set_mode((0,0), pygame.FULLSCREEN)
        self.settings.screen_width = self.screen.get_rect().width
        self.settings.screen_height = self.screen.get_rect().height'''

        pygame.display.set_caption("Alien Invasion") #窗口显示标题
        self.ship = Ship(self)
        self.bullets = pygame.sprite.Group()

    def run_game(self):
        '''开始游戏的主循环'''
        while True:
            self._check_event()  # 监控键盘和鼠标事件
            self.ship.update() # 飞船的位置信息更新
            self._update_bullets() # 更新子弹信息并删除消失的子弹
            self._update_screen()  # 更新屏幕上的图像,并更新到新屏幕

            # 显示最近绘制的屏幕
            pygame.display.flip()

    def _update_bullets(self):
        '''更新子弹的位置并删除消失的子弹'''
        self.bullets.update()  # 子弹的位置更新
        # 删除消失的子弹
        for bullet in self.bullets.copy():
            if bullet.rect.bottom <= 0:
                self.bullets.remove(bullet)
        # print(len(self.bullets)) 验证子弹消失代码,在终端窗口观察

    def _check_event(self):
        # 监控键盘和鼠标事件
        for event in pygame.event.get():
            if event.type == pygame.QUIT:
                sys.exit()
            elif event.type == pygame.KEYDOWN:
                self._check_keydown_events(event)
            elif event.type == pygame.KEYUP:
                self._check_keyup_events(event)

    # 响应按键
    def _check_keydown_events(self, event):
        if event.key == pygame.K_RIGHT:
            self.ship.moving_right = True
        elif event.key == pygame.K_LEFT:
            self.ship.moving_left = True
        elif event.key == pygame.K_q:
            sys.exit()
        elif event.key == pygame.K_SPACE:
            self._fire_bullet()

    # 响应松开按键
    def _check_keyup_events(self, event):
        if event.key == pygame.K_RIGHT:
            self.ship.moving_right = False
        elif event.key == pygame.K_LEFT:
            self.ship.moving_left = False

    def _fire_bullet(self):
        '''创建一颗子弹,并加入编组bullets中'''
        if len(self.bullets) < self.settings.bullet_allowed:
            new_bullet = Bullet(self)
            self.bullets.add(new_bullet)

    def _update_screen(self):
        '''更新屏幕上的图像,并更新到新屏幕'''
        self.screen.fill(self.settings.bg_color)
        self.ship.blitme()
        for bullet in self.bullets.sprites():
            bullet.draw_bullet()

if __name__ == '__main__':
    # 创建游戏实例,并运行游戏
    ai = AlienInvasion()
    ai.run_game()

运行程序,执行效果与12.8.6相同

12.9 小结

评论 4
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

张小勇zhangxy

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值