本章最重要的是了解此游戏设计的逻辑。Pygame模块倒是其次,而且在本章中的应用也不难。

规划你的项目

键盘操纵的打飞机游戏,总共有三条命。

安装Pygame

Pygame的文档参见这里
需要用pip安装Pygame模块。pip是安装Python模块的程序,从3.4版本起包含在Python安装中。
--user表示?

## pip的--user参数表示只为当前用户安装,而不是系统所有用户
$ python3 -m pip install --user pygame
Collecting pygame
  Downloading https://files.pythonhosted.org/packages/8e/24/ede6428359f913ed9cd1643dd5533aefeb5a2699cc95bea089de50ead586/pygame-1.9.6-cp36-cp36m-manylinux1_x86_64.whl (11.4MB)
    100% |████████████████████████████████| 11.4MB 110kB/s
Installing collected packages: pygame
Successfully installed pygame-1.9.6

Pygame需要图形界面,如无,报错如下:

$ python3 alien_invasion.py
pygame 1.9.6
Hello from the pygame community. https://www.pygame.org/contribute.html
Traceback (most recent call last):
  File "alien_invasion.py", line 36, in <module>
    ai = AlienInvasion()
  File "alien_invasion.py", line 16, in __init__
    (self.settings.screen_width, self.settings.screen_height))
pygame.error: No available video device

开始游戏项目

看一下主程序alien_invasion.py,了解一下固定模式。设置在另一文件settings.py中:

import sys

import pygame

from settings import Settings

class AlienInvasion:
    """Overall class to manage game assets and behavior."""

    def __init__(self):
        """Initialize the game, and create game resources."""
        pygame.init() # 初始化
        self.settings = Settings()
		
		# 设置屏幕尺寸
        self.screen = pygame.display.set_mode(
            (self.settings.screen_width, self.settings.screen_height))
        pygame.display.set_caption("Alien Invasion")


    def run_game(self):
        """Start the main loop for the game."""
        while True:
            # Watch for keyboard and mouse events.
            for event in pygame.event.get():
                if event.type == pygame.QUIT:
                    sys.exit()

            # Redraw the screen during each pass through the loop.
            self.screen.fill(self.settings.bg_color)

            # Make the most recently drawn screen visible.
            pygame.display.flip()

if __name__ == '__main__':
    # Make a game instance, and run the game.
    ai = AlienInvasion()
    ai.run_game()

添加飞船图片

免费图片是从Pixabay获取的。Pygame默认的图片格式为bmp,图片最好是透明背景,否则需要将屏幕背景设置与图片背景一致。
无论图片形状如何,Pygame都将其视为长方形,包括屏幕,这样可以很方便的判断两个物体是否相撞。
坐标系中, (0,0)是左上角,因此右下角就是(1200,800)
为飞船构建的类ship.py如下:

import pygame
 
class Ship:
    """A class to manage the ship."""
 
    def __init__(self, ai_game):
        """Initialize the ship and set its starting position."""
        # self是飞船本身,ai_game是类alien_invasion,带屏幕属性
        self.screen = ai_game.screen
        self.screen_rect = ai_game.screen.get_rect()

        # Load the ship image and get its rect.
        self.image = pygame.image.load('images/ship.bmp')
        self.rect = self.image.get_rect()

        # Start each new ship at the bottom center of the screen.
        self.rect.midbottom = self.screen_rect.midbottom

    def blitme(self):
        """Draw the ship at its current location."""
        self.screen.blit(self.image, self.rect)

重构: _check_events() 和 _update_screen() 方法

这两个方法都是helper method,在Python中的惯例是以下划线(_)开头的。大致框架如下:

...
    def run_game(self):
        """Start the main loop for the game."""
        while True:
            self._check_events()
            self._update_screen()

    def _check_events(self):
        """Respond to keypresses and mouse events."""
        for event in pygame.event.get():
            if event.type == pygame.QUIT:
                sys.exit()

    def _update_screen(self):
        """Update images on the screen, and flip to the new screen."""
        self.screen.fill(self.settings.bg_color)
        self.ship.blitme()

        pygame.display.flip()
...

重构是对代码的优化,在本节中并未涉及程序行为的改变。

驾驶飞船

本节将让飞船响应键盘事件(KEYDOWN)。
大致思路是这样的,以右键为例,设置一个布尔变量moving_right初始值为False,如果检测到右键按下将其置为True,同时更新飞船位置;右键抬起则重置为False。左键同样处理。
此外还设置了全屏模式,按q键退出,设置右键速度(默认是1,可以设为>1的浮点数如1.5),以及一系列help method。

快速回顾

目前为止定义了3个Class:

  1. alien_invasion.py - 主程序,处理事件
  2. settings.py - 全局设置
  3. ship.py - 画飞船,更新位置

发射子弹

子弹没有使用图片,而是程序画的长方形,子弹的全局设置如尺寸,颜色和速度存放在文件settings.py中。子弹是通过空格键发射的。
要点1:
我们定义的类Bullet只是一颗子弹,但由于子弹可以有多发,因此在主程序中添加的属性bullets类型为sprite.Group(),这类似于一个列表,可以增删子弹,但最重要的是可以做批量操作,例如同时更新所有的子弹,因此Bullet类继承了类Sprite
要点2:
按下空格键会新增Bullet对象,删除对象则是在子弹到达顶部时,这时坐标系的有值会<0。
同时我们现在了子弹的个数,也就是只允许bullets中存3个Bullet对象。