游戏框架

整体来看,飞机大战游戏只需要两个文件:

  • plane_main.py
    • 封装 主游戏类
    • 创建 游戏对象
    • 启动游戏
  • plane_sprites.py
    • 封装游戏中所有需要使用的 精灵子类
    • 提供精灵的 相关工具

主游戏类

飞机大战主游戏类命名为 PlaneGame,这个类应包含的函数为:

其中:

  • __init__:游戏初始化
    • __create_sprites(self):创建精灵和精灵组
  • start_game():游戏循环
    • __event_handler(self):事件监听
    • __check_collide(self):碰撞检测 —— 子弹销毁敌机、敌机撞毁英雄
    • __update_sprites(self):精灵组更新和绘制
    • __game_over():游戏结束
import pygame
from plane_sprites import *


class PlaneGame(object):
    """飞机大战主游戏"""

    def __init__(self):
        print("游戏初始化...")
        pass

    def start_game(self):
        print("开始游戏...")
        pass


if __name__ == '__main__':
    PlaneGame().start_game()

__init__(self)

SCREEN_RECT = pygame.Rect(0, 0, 480, 700)
def __init__(self):
    print("游戏初始化")
    
    self.screen = pygame.display.set_mode(SCREEN_RECT.size)
    self.clock = pygame.time.Clock()
    self.__create_sprites()

这里使用到了 常量,一般约定是全部字母大写。

start_game()

FPS = 60
def start_game(self):
    """游戏循环"""
    
    print("开始游戏...")
       
    while True:

        self.clock.tick(FPS)
        self.__event_handler()
        self.__check_collide()
        self.__update_sprites()
        pygame.display.update()

其中,事件监听和游戏结束的代码如下:

def __event_handler(self):
    """事件监听"""
    
    for event in pygame.event.get():
    
        if event.type == pygame.QUIT:
            PlaneGame.__game_over()
@staticmethod
def __game_over():
   """游戏结束"""

   print("游戏结束")
   pygame.quit()
   exit()

碰撞检测 和 更新精灵组 之后再细说。

准备精灵组

创建精灵组

def __create_sprites(self):
    """创建精灵组"""
    
    self.back_group = pygame.sprite.Group()
    self.enemy_group = pygame.sprite.Group()
    self.hero_group = pygame.sprite.Group()

更新精灵组

def __update_sprites(self):
    """更新精灵组"""
    
    for group in [self.back_group, self.enemy_group, self.hero_group]:
    
        group.update()
        group.draw(self.screen)

背景图像

背景图像应不断地向下方移动,这样在视觉上就是英雄的飞机不断向上方飞行。这是很多跑酷游戏常用的套路。

实现思路

两张背景图纵向拼接到一起,一起向下移动。当其中一张的 rect.y >= 屏幕的高度,则说明已经移动到了屏幕下方,此时将另一张图像设置回屏幕的上方 rect.y = -rect.height 即可。

之前已经设计了一个游戏精灵类 GameSpirte

class GameSprite(pygame.sprite.Sprite):
    """游戏精灵基类"""

    def __init__(self, image_name, speed=5):

        super().__init__()

        self.image = pygame.image.load(image_name)
        self.rect = self.image.get_rect()
        self.speed = speed

    def update(self, *args):

        self.rect.y += self.speed

因此,只需创建一个 背景类,继承父类 GameSpirte 的方法,适当重写即可。
plane_spirtes.py

class Background(GameSprite):
    """游戏背景精灵"""

    def update(self):

        super().update()

        if self.rect.y >= SCREEN_RECT.height:
            self.rect.y = -self.rect.height

plane_main.py

def __create_sprites(self):

    # 创建背景精灵和精灵组
    bg1 = Background("./images/background.png")
    bg2 = Background("./images/background.png")
    bg2.rect.y = -bg2.rect.height
    
    self.back_group = pygame.sprite.Group(bg1, bg2)
def __update_sprites(self):

    self.back_group.update()
    self.back_group.draw(self.screen)

简化

根据面向对象设计原则,应该将对象的职责封装到类内部,并且尽量简化程序调用。

因此可进行如下改进:

plane_sprites.pyBackground 的初始化方法:

def __init__(self, is_alt=False):

    super().__init__("./images/background.png")
       
    if is_alt:
        self.rect.y = -self.rect.height     

plane_main 中的 __create_sprites 方法:

bg1 = Background()
bg2 = Background(True)

self.back_group = pygame.sprite.Group(bg1, bg2)

敌机

设计 Enemy 类,然后使用 定时器 添加敌机。

定时器

使用 pygame.time.set_timer(eventid, milliseconds) 创建定时器。

  • eventid:事件代号,基于常量 pygame.USEREVENT 来指定
    • USEREVENT 是一个整数,再增加的事件可以使用 USEREVENT + 1 指定
  • milliseconds:事件触发间隔的毫秒值

创建定时器步骤:

  • 定义定时器常量 eventid
  • 在初始化方法中调用 set_timer 创建定时器事件
  • 在游戏循环中监听

plane_sprites.py

CREATE_ENEMY_EVENT = pygame.USEREVENT

plane_main.pyPlaneGame初始化方法 中创建事件:

pygame.time.set_timer(CREATE_ENEMY_EVENT, 1000)

plane_main.py__event_handler 的方法中进行监听:

def __event_handler(self):
    
    for event in pygame.event.get():
    
        if event.type == pygame.QUIT:
            PlaneGame.__game_over()

        elif event.type == CREATE_ENEMY_EVENT:
            print("敌机出场...")
            self.enemy_group.add(Enemy())

敌机类的设计

  • 每隔 1 秒会出现一架敌机
  • 敌机向屏幕下方飞行,速度各不相同,水平位置也不尽相同
  • 当敌机从屏幕下方飞出,销毁

plane_sprites.py

import random
  • 在导入模块时,建议按照以下顺序导入
    1. 官方标准模块导入
    2. 第三方模块导入
    3. 应用程序模块导入
class Enemy(GameSprite):
    """敌机精灵"""
    
    def __init__(self):
        
        super().__init__("./images/enemy1.png")
        self.speed = random.randint(1, 3)
        self.rect.bottom = 0
        max_x = SCREEN_RECT.width - self.rect.width
        self.rect.x = random.randint(0, max_x)
    
    def update(self):
        
        super().update()
        if self.rect.y >= SCREEN_RECT.height:
            print("敌机飞出屏幕...")   
            self.kill() 

敌机飞出屏幕之后,为节省内存,应将其从敌机组删除。
__del__ 内置方法会在对象被销毁前调用,可用于判断对象是否被销毁。

def __del__(self):
    print("敌机挂了 %s" % self.rect)

plane_game.py 文件中,创建敌机精灵组并更新,代码略。