前一篇博客为游戏实现前所用的基础知识介绍
Python飞机大战项目前篇
此篇为飞机大战游戏项目的整个实现过程。从游戏框架的搭建、游戏背景的设置、英雄飞机和敌机的设定,再到飞机发生碰撞时的检测(子弹摧毁敌机,敌机撞毁英雄)等详细的笔记描述

游戏框架搭建

游戏初始化 —— __init__() 会调用以下方法:

方法 职责
__create_sprites(self) 创建所有精灵和精灵组

游戏循环 —— start_game() 会调用以下方法:

方法 职责
__event_handler(self) 事件监听
__check_collide(self) 碰撞检测 —— 子弹销毁敌机、敌机撞毁英雄
__update_sprites(self) 精灵组更新和绘制
__game_over() 游戏结束

新建plane_main.py文件和plane_sprites.py文件记得导入图片素材(如图)

plane_main

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

游戏背景滚动实现

背景图像的显示效果:

  • 游戏启动后,背景图像连续不断地 向下方 移动
  • 视觉上 产生英雄的飞机不断向上方飞行的 错觉 —— 在很多跑酷类游戏中常用的套路
    • 游戏的背景 不断变化
    • 游戏的主角 位置保持不变

实现办法:

  1. 创建两张背景图像精灵

    • 1完全和屏幕重合
    • 2 张在 屏幕的正上方
  2. 两张图像 一起向下方运动
    • self.rect.y += self.speed
  3. 任意背景精灵rect.y >= 屏幕的高度 说明已经 移动到屏幕下方
  4. 移动到屏幕下方的这张图像 设置到 屏幕的正上方
    • rect.y = -rect.height

设计背景类

* 初始化方法

  • 直接指定 背景图片
  • is_alt 判断是否是另一张图像
    • False 表示 第一张图像,需要与屏幕重合
    • True 表示 另一张图像,在屏幕的正上方
  • update() 方法
    • 判断 是否移动出屏幕,如果是,将图像设置到 屏幕的正上方,从而实现 交替滚动

继承 如果父类提供的方法,不能满足子类的需求:

  • 派生一个子类
  • 在子类中针对特有的需求,重写父类方法,并且进行扩展

plane_sprites 新建 Background 继承自 GameSprite

class Background(GameSprite):"""游戏背景精灵"""def update(self):# 1. 调用父类的方法实现super().update()# 2. 判断是否移出屏幕,如果移出屏幕,将图像设置到屏幕的上方if self.rect.y >= SCREEN_RECT.height:self.rect.y = -self.rect.height

plane_main.py 中显示背景精灵

  1. __create_sprites 方法中创建 精灵精灵组
  2. __update_sprites 方法中,让 精灵组 调用 update()draw() 方法

__create_sprites 方法

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

__update_sprites 方法

def __update_sprites(self):self.back_group.update()self.back_group.draw(self.screen)

完整实现代码

plane_main.py部分

import pygame
from plane_sprites import *class PlaneGame(object):"""飞机大战主游戏"""def __init__(self):print('游戏初始化')# 1 创建游戏的窗口self.screen = pygame.display.set_mode(SCREEN_RECT.size)# 2 创建游戏的时钟self.clock = pygame.time.Clock()# 3 调用私有方法,精灵和精灵组的创建self.__create_spritea()def __create_spritea(self):# 创建背景精灵和精灵组bg1 = Background()bg2 = Background(True)# bg2.rect.y = -bg2.rect.height   # 定义初始位置self.back_group = pygame.sprite.Group(bg1, bg2)def start_game(self):print('游戏开始...')while True:# 1 设置刷新帧率self.clock.tick(FRAME_PER_SEC)# 2 事件监听self.__event_handler()# 3 碰撞检测self.__check_collide()# 4 更新/绘制精灵组self.__update_sprites()# 5 更新显示pygame.display.update()def __event_handler(self):for event in pygame.event.get():# 判断是否退出游戏if event.type == pygame.QUIT:PlaneGame.__game_over()def __check_collide(self):passdef __update_sprites(self):self.back_group.update()self.back_group.draw(self.screen)if __name__ == '__main__':# 创建游戏对象game = PlaneGame()# 启动游戏game.start_game()

plane_sprites.py部分

import pygame# 屏幕大小的常量
SCREEN_RECT = pygame.Rect(0, 0, 480, 700)
# 刷新的帧率
FRAME_PER_SEC = 60
# 创建敌机的定时器常量(USEREVENT  是pygame提供的用户事件)
CREATE_ENEMY_EVENT = pygame.USEREVENTclass GameSprite(pygame.sprite.Sprite):  # 第一个是模块的名称 第二个是类的名称""" 飞机大战游戏精灵"""def __init__(self, image_name, speed=1):# 调用父类的初始化方法super().__init__()# 定义对象的属性self.image = pygame.image.load(image_name)self.rect = self.image.get_rect()self.speed = speeddef update(self):# 在屏幕的垂直方向上移动self.rect.y += self.speedclass Background(GameSprite):""" 游戏背景精灵"""def __init__(self, is_alt=False):# 1 调用父类方法实现精灵组的创建(image/rect/speed)super().__init__('./images/background.png')# 2 判断是否是交替图像,如果是,需要设置初始位置if is_alt:self.rect.y = -self.rect.heightdef update(self):# 1 调用父类的方法实现super().update()# 2 判断是否移出屏幕,如果移出屏幕, 将图像设置到屏幕上方if self.rect.y >= SCREEN_RECT.height:self.rect.y = -self.rect.height

实现效果图

敌机部分实现

敌机的 出现规律

  1. 游戏启动后,每隔 1 秒 会出现一架敌机
  2. 每架敌机 向屏幕下方飞行,飞行速度各不相同
  3. 每架敌机出现的 水平位置 也不尽相同
  4. 当敌机 从屏幕下方飞出,不会再飞回到屏幕中

定义并监听创建敌机的定时器事件

  • pygame 中可以使用 pygame.time.set_timer() 来添加 定时器
  • 所谓 定时器,就是 每隔一段时间,去 执行一些动作
set_timer(eventid, milliseconds) -> None
  • set_timer 可以创建一个事件
  • 可以在游戏循环的事件监听方法中捕获到该事件
  • 第 1 个参数事件代号需要基于常量 pygame.USEREVENT 来指定
    • USEREVENT 是一个整数,再增加的事件可以使用 USEREVENT + 1 指定,依次类推…
  • 第 2 个参数是事件触发间隔的毫秒值
    pygame 的定时器使用套路非常固定:
  1. 定义定时器常量 —— eventid
  2. 在 初始化方法中,调用 set_timer 方法设置定时器事件
  3. 在 游戏循环 中,监听定时器事件

设计 Enemy

  1. 游戏启动后,每隔 1 秒会 出现一架敌机
  2. 每架敌机 向屏幕下方飞行,飞行速度各不相同
  3. 每架敌机出现的水平位置也不尽相同
  4. 当敌机从屏幕下方飞出,不会再飞回到屏幕中

敌机类的准备

  • plane_sprites 新建 Enemy 继承自 GameSprite
  • 重写 初始化方法,直接指定 图片名称
  • 暂时 不实现 随机速度 和 随机位置 的指定
  • 重写 update 方法,判断是否飞出屏幕

创建敌机

  1. __create_sprites,添加 敌机精灵组

    • 敌机是 定时被创建的,因此在初始化方法中,不需要创建敌机
  2. __event_handler,创建敌机,并且 添加到精灵组
    • 调用 精灵组 的 add 方法可以 向精灵组添加精灵
  3. __update_sprites,让 敌机精灵组 调用 updatedraw 方法

随机敌机位置和速度
修改 plane_sprites.py 增加 random 的导入

import random

随机位置:使用 pygame.Rect 提供的 bottom 属性,在指定敌机初始位置时,会比较方便

  • bottom = y + height
  • y = bottom - height

移出屏幕销毁敌机

  • 敌机移出屏幕之后,如果 没有撞到英雄,敌机的历史使命已经终结

  • 需要从 敌机组 删除,否则会造成 内存浪费

  • __del__ 内置方法会在对象被销毁前调用,在开发中,可以用于 判断对象是否被销毁

  • 判断敌机是否飞出屏幕,如果是,调用 kill() 方法从所有组中删除

完整实现代码

plane_main.py部分

import pygame
from plane_sprites import *class PlaneGame(object):"""飞机大战主游戏"""def __init__(self):print("游戏初始化")# 1. 创建游戏的窗口self.screen = pygame.display.set_mode(SCREEN_RECT.size)# 2. 创建游戏的时钟self.clock = pygame.time.Clock()# 3. 调用私有方法,精灵和精灵组的创建self.__create_sprites()# 4. 设置定时器事件 - 创建敌机 1spygame.time.set_timer(CREATE_ENEMY_EVENT, 1000)def __create_sprites(self):# 创建背景精灵和精灵组bg1 = Background()bg2 = Background(True)self.back_group = pygame.sprite.Group(bg1, bg2)# 创建敌机的精灵组self.enemy_group = pygame.sprite.Group()def start_game(self):print("游戏开始...")while True:# 1. 设置刷新帧率self.clock.tick(FRAME_PER_SEC)# 2. 事件监听self.__event_handler()# 3. 碰撞检测self.__check_collide()# 4. 更新/绘制精灵组self.__update_sprites()# 5. 更新显示pygame.display.update()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("敌机出场...")# 创建敌机精灵enemy = Enemy()# 将敌机精灵添加到敌机精灵组self.enemy_group.add(enemy)def __check_collide(self):passdef __update_sprites(self):self.back_group.update()self.back_group.draw(self.screen)self.enemy_group.update()self.enemy_group.draw(self.screen)@staticmethoddef __game_over():print("游戏结束")pygame.quit()exit()if __name__ == '__main__':# 创建游戏对象game = PlaneGame()# 启动游戏game.start_game()

plane_sprites.py部分

import random
import pygame# 屏幕大小的常量
SCREEN_RECT = pygame.Rect(0, 0, 480, 700)
# 刷新的帧率
FRAME_PER_SEC = 60
# 创建敌机的定时器常量
CREATE_ENEMY_EVENT = pygame.USEREVENTclass GameSprite(pygame.sprite.Sprite):"""飞机大战游戏精灵"""def __init__(self, image_name, speed=1):# 调用父类的初始化方法super().__init__()# 定义对象的属性self.image = pygame.image.load(image_name)self.rect = self.image.get_rect()self.speed = speeddef update(self):# 在屏幕的垂直方向上移动self.rect.y += self.speedclass Background(GameSprite):"""游戏背景精灵"""def __init__(self, is_alt=False):# 1. 调用父类方法实现精灵的创建(image/rect/speed)super().__init__("./images/background.png")# 2. 判断是否是交替图像,如果是,需要设置初始位置if is_alt:self.rect.y = -self.rect.heightdef update(self):# 1. 调用父类的方法实现super().update()# 2. 判断是否移出屏幕,如果移出屏幕,将图像设置到屏幕的上方if self.rect.y >= SCREEN_RECT.height:self.rect.y = -self.rect.heightclass Enemy(GameSprite):"""敌机精灵"""def __init__(self):# 1. 调用父类方法,创建敌机精灵,同时指定敌机图片super().__init__("./images/enemy1.png")# 2. 指定敌机的初始随机速度 1 ~ 3self.speed = random.randint(1, 3)# 3. 指定敌机的初始随机位置self.rect.bottom = 0max_x = SCREEN_RECT.width - self.rect.widthself.rect.x = random.randint(0, max_x)def update(self):# 1. 调用父类方法,保持垂直方向的飞行super().update()# 2. 判断是否飞出屏幕,如果是,需要从精灵组删除敌机if self.rect.y >= SCREEN_RECT.height:print("飞出屏幕,需要从精灵组删除...")# kill方法可以将精灵从所有精灵组中移出,精灵就会被自动销毁self.kill()def __del__(self):print("敌机挂了 %s" % self.rect)

实现效果图

英雄部分实现

英雄需求

  1. 游戏启动后,英雄 出现在屏幕的 水平中间 位置,距离 屏幕底部120 像素
  2. 英雄 每隔 0.5 秒发射一次子弹,每次 连发三枚子弹
  3. 英雄 默认不会移动,需要通过 左/右 方向键,控制 英雄 在水平方向移动

子弹需求

  1. 子弹 从 英雄 的正上方发射沿直线向 上方飞行
  2. 飞出屏幕后,需要从 精灵组中删除

Hero —— 英雄

  • 初始化方法

    • 指定 英雄图片
    • 初始速度 = 0 —— 英雄默认静止不动
    • 定义 bullets 子弹精灵组保存子弹精灵
  • 重写 update()方法
    • 英雄需要 水平移动
    • 并且需要保证不能移出屏幕
  • 增加 bullets 属性,记录所有子弹精灵
  • 增加 fire 方法,用于发射子弹

创建英雄

  • plane_sprites 新建 Hero
  • 重写 初始化方法,直接指定图片名称,并且将初始速度设置为 0
  • 设置 英雄的初始位置
  • centerx = x + 0.5 * width
  • centery = y + 0.5 * height
  • bottom = y + height
    绘制英雄
  1. __create_sprites,添加 英雄精灵 和 英雄精灵组

    • 后续要针对 英雄 做 碰撞检测 以及 发射子弹
    • 所以 英雄 需要 单独定义成属性
  2. __update_sprites,让 英雄精灵组 调用 updatedraw 方法
    移动英雄位置
  3. Hero 类中重写 update 方法
    • 用 速度 speed 和 英雄 rect.x 进行叠加
    • 不需要调用父类方法 —— 父类方法只是实现了单纯的垂直运动
  4. __event_handler 方法中根据 左右方向键 设置英雄的 速度
    • 向右 => speed = 2
    • 向左 => speed = -2
    • 其他 => speed = 0

Bullet —— 子弹

  • 初始化方法

    • 指定子弹图片
    • 初始速度 = -2 —— 子弹需要向上方飞行
  • 重写update()方法
    • 判断 是否飞出屏幕,如果是,从精灵组删除

发射子弹
pygame定时器 使用套路非常固定:

  1. 定义 定时器常量 —— eventid
  2. 在 初始化方法 中,调用 set_timer 方法 设置定时器事件
  3. 在 游戏循环 中,监听定时器事件
  • Hero 中定义 fire 方法
def fire(self):print("发射子弹...")
  • plane_main.py 的顶部定义 发射子弹 事件常量
# 英雄发射子弹事件
HERO_FIRE_EVENT = pygame.USEREVENT + 1
  • __init__ 方法末尾中添加 发射子弹 事件
# 每隔 0.5 秒发射一次子弹
pygame.time.set_timer(HERO_FIRE_EVENT, 500)
  • __event_handler 方法中让英雄发射子弹
elif event.type == HERO_FIRE_EVENT:self.hero.fire()

定义子弹类

  • plane_sprites 新建 Bullet 继承自 GameSprite
  • 重写 初始化方法,直接指定 图片名称,并且设置 初始速度
  • 重写 update() 方法,判断子弹 飞出屏幕从精灵组删除

发射子弹

  1. Hero 的 初始化方法 中创建 子弹精灵组 属性
  2. 修改 plane_main.py__update_sprites 方法,让 子弹精灵组 调用 updatedraw 方法
  3. 实现 fire() 方法
    • 创建子弹精灵
    • 设置初始位置 —— 在 英雄的正上方
    • 将 子弹 添加到精灵组

完整实现代码

plane_main.py部分

import pygame
from plane_sprites import *class PlaneGame(object):"""飞机大战主游戏"""def __init__(self):print("游戏初始化")# 1. 创建游戏的窗口self.screen = pygame.display.set_mode(SCREEN_RECT.size)# 2. 创建游戏的时钟self.clock = pygame.time.Clock()# 3. 调用私有方法,精灵和精灵组的创建self.__create_sprites()# 4. 设置定时器事件 - 创建敌机 1spygame.time.set_timer(CREATE_ENEMY_EVENT, 1000)pygame.time.set_timer(HERO_FIRE_EVENT, 500)def __create_sprites(self):# 创建背景精灵和精灵组bg1 = Background()bg2 = Background(True)self.back_group = pygame.sprite.Group(bg1, bg2)# 创建敌机的精灵组self.enemy_group = pygame.sprite.Group()# 创建英雄的精灵和精灵组self.hero = Hero()self.hero_group = pygame.sprite.Group(self.hero)def start_game(self):print("游戏开始...")while True:# 1. 设置刷新帧率self.clock.tick(FRAME_PER_SEC)# 2. 事件监听self.__event_handler()# 3. 碰撞检测self.__check_collide()# 4. 更新/绘制精灵组self.__update_sprites()# 5. 更新显示pygame.display.update()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("敌机出场...")# 创建敌机精灵enemy = Enemy()# 将敌机精灵添加到敌机精灵组self.enemy_group.add(enemy)elif event.type == HERO_FIRE_EVENT:self.hero.fire()# elif event.type == pygame.KEYDOWN and event.key == pygame.K_RIGHT:#     print("向右移动...")# 使用键盘提供的方法获取键盘按键 - 按键元组keys_pressed = pygame.key.get_pressed()# 判断元组中对应的按键索引值 1if keys_pressed[pygame.K_RIGHT]:self.hero.speed = 2elif keys_pressed[pygame.K_LEFT]:self.hero.speed = -2else:self.hero.speed = 0def __check_collide(self):passdef __update_sprites(self):self.back_group.update()self.back_group.draw(self.screen)self.enemy_group.update()self.enemy_group.draw(self.screen)self.hero_group.update()self.hero_group.draw(self.screen)self.hero.bullets.update()self.hero.bullets.draw(self.screen)@staticmethoddef __game_over():print("游戏结束")pygame.quit()exit()if __name__ == '__main__':# 创建游戏对象game = PlaneGame()# 启动游戏game.start_game()

plane_sprites.py部分

import random
import pygame# 屏幕大小的常量
SCREEN_RECT = pygame.Rect(0, 0, 480, 700)
# 刷新的帧率
FRAME_PER_SEC = 60
# 创建敌机的定时器常量
CREATE_ENEMY_EVENT = pygame.USEREVENT
# 英雄发射子弹事件
HERO_FIRE_EVENT = pygame.USEREVENT + 1class GameSprite(pygame.sprite.Sprite):"""飞机大战游戏精灵"""def __init__(self, image_name, speed=1):# 调用父类的初始化方法super().__init__()# 定义对象的属性self.image = pygame.image.load(image_name)self.rect = self.image.get_rect()self.speed = speeddef update(self):# 在屏幕的垂直方向上移动self.rect.y += self.speedclass Background(GameSprite):"""游戏背景精灵"""def __init__(self, is_alt=False):# 1. 调用父类方法实现精灵的创建(image/rect/speed)super().__init__("./images/background.png")# 2. 判断是否是交替图像,如果是,需要设置初始位置if is_alt:self.rect.y = -self.rect.heightdef update(self):# 1. 调用父类的方法实现super().update()# 2. 判断是否移出屏幕,如果移出屏幕,将图像设置到屏幕的上方if self.rect.y >= SCREEN_RECT.height:self.rect.y = -self.rect.heightclass Enemy(GameSprite):"""敌机精灵"""def __init__(self):# 1. 调用父类方法,创建敌机精灵,同时指定敌机图片super().__init__("./images/enemy1.png")# 2. 指定敌机的初始随机速度 1 ~ 3self.speed = random.randint(1, 3)# 3. 指定敌机的初始随机位置self.rect.bottom = 0max_x = SCREEN_RECT.width - self.rect.widthself.rect.x = random.randint(0, max_x)def update(self):# 1. 调用父类方法,保持垂直方向的飞行super().update()# 2. 判断是否飞出屏幕,如果是,需要从精灵组删除敌机if self.rect.y >= SCREEN_RECT.height:# print("飞出屏幕,需要从精灵组删除...")# kill方法可以将精灵从所有精灵组中移出,精灵就会被自动销毁self.kill()def __del__(self):# print("敌机挂了 %s" % self.rect)passclass Hero(GameSprite):"""英雄精灵"""def __init__(self):# 1. 调用父类方法,设置image&speedsuper().__init__("./images/me1.png", 0)# 2. 设置英雄的初始位置self.rect.centerx = SCREEN_RECT.centerxself.rect.bottom = SCREEN_RECT.bottom - 120# 3. 创建子弹的精灵组self.bullets = pygame.sprite.Group()def update(self):# 英雄在水平方向移动self.rect.x += self.speed# 控制英雄不能离开屏幕if self.rect.x < 0:self.rect.x = 0elif self.rect.right > SCREEN_RECT.right:self.rect.right = SCREEN_RECT.rightdef fire(self):print("发射子弹...")for i in (0, 1, 2):# 1. 创建子弹精灵bullet = Bullet()# 2. 设置精灵的位置bullet.rect.bottom = self.rect.y - i * 20bullet.rect.centerx = self.rect.centerx# 3. 将精灵添加到精灵组self.bullets.add(bullet)class Bullet(GameSprite):"""子弹精灵"""def __init__(self):# 调用父类方法,设置子弹图片,设置初始速度super().__init__("./images/bullet1.png", -2)def update(self):# 调用父类方法,让子弹沿垂直方向飞行super().update()# 判断子弹是否飞出屏幕if self.rect.bottom < 0:self.kill()def __del__(self):print("子弹被销毁...")

实现效果图

碰撞检测

pygame 提供了 两个非常方便 的方法可以实现碰撞检测:

pygame.sprite.groupcollide()

  • 两个精灵组 中 所有的精灵 的碰撞检测
groupcollide(group1, group2, dokill1, dokill2, collided = None) -> Sprite_dict
  • 如果将 dokill 设置为 True,则 发生碰撞的精灵将被自动移除
  • collided 参数是用于 计算碰撞的回调函数
    • 如果没有指定,则每个精灵必须有一个 rect 属性

pygame.sprite.spritecollide()

  • 判断 某个精灵 和 指定精灵组 中的精灵的碰撞
spritecollide(sprite, group, dokill, collided = None) -> Sprite_list
  • 如果将 dokill 设置为 True,则 指定精灵组 中 发生碰撞的精灵将被自动移除
  • collided 参数是用于 计算碰撞的回调函数
    • 如果没有指定,则每个精灵必须有一个 rect 属性
  • 返回 精灵组 中跟 精灵 发生碰撞的 精灵列表

完整实现代码

plane_main.py部分

import pygame
from plane_sprites import *# 敌机出现事件
CREATE_ENEMY_EVENT = pygame.USEREVENT
# 发射子弹事件
HERO_FIRE_EVENT = pygame.USEREVENT + 1class PlaneGame(object):"""飞机大战游戏类"""def __init__(self):# 1. pygame 初始化pygame.init()# 2. 创建游戏屏幕self.screen = pygame.display.set_mode(SCREEN_RECT.size)# 3. 创建游戏时钟self.clock = pygame.time.Clock()# 4. 创建精灵组self.__create_sprites()# 5. 创建用户事件PlaneGame.__create_user_events()def __create_sprites(self):"""创建精灵组"""# 背景组bg1 = Background()bg2 = Background(True)self.back_group = pygame.sprite.Group(bg1, bg2)# 敌机组enemy = Enemy()self.enemy_group = pygame.sprite.Group(enemy)# 英雄组self.hero = Hero()self.hero_group = pygame.sprite.Group(self.hero)@staticmethoddef __create_user_events():"""创建用户事件"""# 每秒添加一架敌机pygame.time.set_timer(CREATE_ENEMY_EVENT, 1 * 1000)# 每秒发射两次子弹pygame.time.set_timer(HERO_FIRE_EVENT, 500)def start_game(self):"""开始游戏"""while True:# 1. 设置刷新帧率self.clock.tick(60)# 2. 事件监听self.__event_handler()# 3. 更新精灵组self.__update_sprites()# 碰撞检测self.__check_collide()# 4. 更新屏幕显示pygame.display.update()def __event_handler(self):"""事件监听"""for event in pygame.event.get():if event.type == pygame.QUIT:print("退出游戏...")pygame.quit()exit()elif event.type == CREATE_ENEMY_EVENT:# 创建敌机,并且添加到敌机组self.enemy_group.add(Enemy())# 测试敌机精灵数量# enemy_count = len(self.enemy_group.sprites())# print("敌机精灵数量 %d" % enemy_count)elif event.type == HERO_FIRE_EVENT:# 英雄发射子弹self.hero.fire()# 通过 pygame.key 获取用户按键keys_pressed = pygame.key.get_pressed()dir = keys_pressed[pygame.K_RIGHT] - keys_pressed[pygame.K_LEFT]# 根据移动方向设置英雄的速度self.hero.speed = dir * 2def __update_sprites(self):"""更新精灵组"""for group in [self.back_group, self.enemy_group,self.hero_group, self.hero.bullets]:group.update()group.draw(self.screen)def __check_collide(self):"""碰撞检测"""# 1. 子弹摧毁敌机pygame.sprite.groupcollide(self.hero.bullets, self.enemy_group, True, True)# 2. 英雄被撞毁collide_list = pygame.sprite.spritecollide(self.hero, self.enemy_group, False)if len(collide_list) > 0:self.hero.is_alive = Falseprint("英雄牺牲...")pygame.quit()exit()if __name__ == '__main__':# 1. 创建游戏对象game = PlaneGame()# 2. 开始游戏game.start_game()

plane_sprites.py部分

import random
import pygame# 游戏屏幕大小
SCREEN_RECT = pygame.Rect(0, 0, 480, 700)class GameSprite(pygame.sprite.Sprite):"""游戏精灵基类"""def __init__(self, image_name, speed=1):# 调用父类的初始化方法super().__init__()# 加载图像self.image = pygame.image.load(image_name)# 设置尺寸self.rect = self.image.get_rect()# 记录速度self.speed = speeddef update(self, *args):# 默认在垂直方向移动self.rect.top += self.speedclass Background(GameSprite):"""背景精灵"""def __init__(self, is_alt=False):image_name = "./images/background.png"super().__init__(image_name)# 判断是否交替图片,如果是,将图片设置到屏幕顶部if is_alt:self.rect.bottom = 0def update(self, *args):# 调用父类方法super().update(args)# 判断是否超出屏幕if self.rect.top >= SCREEN_RECT.height:self.rect.bottom = 0class Enemy(GameSprite):"""敌机精灵"""def __init__(self):image_name = "./images/enemy1.png"super().__init__(image_name)# 随机敌机出现位置width = SCREEN_RECT.width - self.rect.widthself.rect.left = random.randint(0, width)self.rect.bottom = 0# 随机速度self.speed = random.randint(1, 3)def update(self, *args):super().update(args)# 判断敌机是否移出屏幕if self.rect.top >= SCREEN_RECT.height:# 将精灵从所有组中删除self.kill()class Hero(GameSprite):"""英雄精灵"""def __init__(self):image_name = "./images/me1.png"super().__init__(image_name, 0)# 设置初始位置self.rect.centerx = SCREEN_RECT.centerxself.rect.bottom = SCREEN_RECT.bottom - 120# 创建子弹组self.bullets = pygame.sprite.Group()def update(self, *args):# 飞机水平移动self.rect.left += self.speed# 超出屏幕检测if self.rect.left < 0:self.rect.left = 0if self.rect.right > SCREEN_RECT.right:self.rect.right = SCREEN_RECT.rightdef fire(self):# bullet_count = len(self.bullets.sprites())# print("子弹数量 %d" % bullet_count)for i in range(0, 3):# 创建子弹精灵bullet = Bullet()# 设置子弹位置bullet.rect.bottom = self.rect.top - i * 20bullet.rect.centerx = self.rect.centerx# 将子弹添加到精灵组self.bullets.add(bullet)class Bullet(GameSprite):"""子弹精灵"""def __init__(self):image_name = "./images/bullet1.png"super().__init__(image_name, -2)def update(self, *args):super().update(args)# 判断是否超出屏幕if self.rect.bottom < 0:self.kill()

实现效果图

**简单的的一个飞机大战小游戏就实现了!
但是这太简略了
比如:游戏前没有设置开始页面,代码一运行就直接开始游戏
再比如:游戏中没有吸引人的爆炸效果,没有分数的统计,没有实现飞机的升级以及随着英雄的升级还有敌机的升级,直至游戏的难度升级。
再再比如:游戏后的结束页面,最高分的排名记录等功能。
有兴趣的同学可以尝试再整整!

又添加了一个爆炸效果的功能

更新2.0版

完整实现代码

plane_main.py部分

#! /usr/bin/python3import pygame
from plane_sprites import *# 敌机出现事件
CREATE_ENEMY_EVENT = pygame.USEREVENT
# 发射子弹事件
HERO_FIRE_EVENT = pygame.USEREVENT + 1class PlaneGame:"""飞机大战游戏类"""def __init__(self):print("游戏初始化...")pygame.init()self.screen = pygame.display.set_mode(SCREEN_RECT.size)self.clock = pygame.time.Clock()self.__create_sprites()PlaneGame.__create_user_events()def __create_sprites(self):"""创建精灵组"""self.back_group = pygame.sprite.Group(Background(), Background(True))self.hero = Hero()self.hero_group = pygame.sprite.Group(self.hero)self.enemy_group = pygame.sprite.Group()self.destroy_group = pygame.sprite.Group()@staticmethoddef __create_user_events():"""创建用户事件"""# 每秒添加一架敌机pygame.time.set_timer(CREATE_ENEMY_EVENT, 1 * 1000)# 每秒发射两次子弹pygame.time.set_timer(HERO_FIRE_EVENT, 500)def start_game(self):"""开启游戏循环"""while True:self.clock.tick(60)self.__event_handler()self.__update_sprites()self.__check_collide()pygame.display.update()def __check_collide(self):"""碰撞检测"""# 子弹摧毁敌机enemies = pygame.sprite.groupcollide(self.enemy_group,self.hero.bullets,False,True).keys()for enemy in enemies:enemy.life -= 1if enemy.life <= 0:enemy.add(self.destroy_group)enemy.remove(self.enemy_group)enemy.destroied()# 敌机撞毁英雄for hero in pygame.sprite.spritecollide(self.hero,self.enemy_group,True):print("英雄牺牲了...")self.hero.destroied()def __event_handler(self):"""事件处理"""for event in pygame.event.get():if event.type == pygame.QUIT:PlaneGame.__finished_game()elif event.type == HERO_FIRE_EVENT:self.hero.fire()elif event.type == CREATE_ENEMY_EVENT:self.enemy_group.add(Enemy())# 按下 b 英雄自爆elif event.type == pygame.KEYDOWN and event.key == pygame.K_b:# self.hero.destroied()# 集体自爆for enemy in self.enemy_group.sprites():enemy.destroied()# 判断英雄是否已经被销毁,如果是,游戏结束!if self.hero.can_destroied:PlaneGame.__finished_game()# 通过 pygame.key 获取用户按键keys_pressed = pygame.key.get_pressed()dir = keys_pressed[pygame.K_RIGHT] - keys_pressed[pygame.K_LEFT]# 根据移动方向设置英雄的速度self.hero.speed = dir * 2def __update_sprites(self):"""更新/绘制精灵组"""for group in [self.back_group, self.hero_group,self.hero.bullets, self.enemy_group,self.destroy_group]:group.update()group.draw(self.screen)@staticmethoddef __finished_game():"""退出游戏"""print("退出游戏")pygame.quit()exit()if __name__ == '__main__':PlaneGame().start_game()

plane_sprites.py部分

import random
import pygame# 屏幕尺寸
SCREEN_RECT = pygame.Rect(0, 0, 480, 700)class GameSprite(pygame.sprite.Sprite):"""游戏精灵"""def __init__(self, image_name, speed=1):super().__init__()self.image = pygame.image.load(image_name)self.rect = self.image.get_rect()self.speed = speeddef update(self, *args):self.rect.top += self.speed@staticmethoddef image_names(prefix, count):names = []for i in range(1, count + 1):names.append("./images/" + prefix + str(i) + ".png")return namesclass Background(GameSprite):"""背景精灵"""def __init__(self, is_alt=False):super().__init__("./images/background.png")if is_alt:self.rect.bottom = 0def update(self, *args):super().update(args)if self.rect.top >= SCREEN_RECT.height:self.rect.bottom = 0class PlaneSprite(GameSprite):"""飞机精灵,包括敌机和英雄"""def __init__(self, image_names, destroy_names, life, speed):image_name = image_names[0]super().__init__(image_name, speed)# 生命值self.life = life# 正常图像列表self.__life_images = []for file_name in image_names:image = pygame.image.load(file_name)self.__life_images.append(image)# 被摧毁图像列表self.__destroy_images = []for file_name in destroy_names:image = pygame.image.load(file_name)self.__destroy_images.append(image)# 默认播放生存图片self.images = self.__life_images# 显示图像索引self.show_image_index = 0# 是否循环播放self.is_loop_show = True# 是否可以被删除self.can_destroied = Falsedef update(self, *args):self.update_images()super().update(args)def update_images(self):"""更新图像"""pre_index = int(self.show_image_index)self.show_image_index += 0.05count = len(self.images)# 判断是否循环播放if self.is_loop_show:self.show_image_index %= len(self.images)elif self.show_image_index > count - 1:self.show_image_index = count - 1self.can_destroied = Truecurrent_index = int(self.show_image_index)if pre_index != current_index:self.image = self.images[current_index]def destroied(self):"""飞机被摧毁"""# 默认播放生存图片self.images = self.__destroy_images# 显示图像索引self.show_image_index = 0# 是否循环播放self.is_loop_show = Falseclass Hero(PlaneSprite):"""英雄精灵"""def __init__(self):image_names = GameSprite.image_names("me", 2)destroy_names = GameSprite.image_names("me_destroy_", 4)super().__init__(image_names, destroy_names, 0, 0)# 设置初始位置self.rect.centerx = SCREEN_RECT.centerxself.rect.bottom = SCREEN_RECT.bottom - 120# 创建子弹组self.bullets = pygame.sprite.Group()def update(self, *args):self.update_images()# 飞机水平移动self.rect.left += self.speed# 超出屏幕检测if self.rect.left < 0:self.rect.left = 0if self.rect.right > SCREEN_RECT.right:self.rect.right = SCREEN_RECT.rightdef fire(self):"""发射子弹"""# bullet_count = len(self.bullets.sprites())# print("子弹数量 %d" % bullet_count)for i in range(0, 3):# 创建子弹精灵bullet = Bullet()# 设置子弹位置bullet.rect.bottom = self.rect.top - i * 20bullet.rect.centerx = self.rect.centerx# 将子弹添加到精灵组self.bullets.add(bullet)class Bullet(GameSprite):"""子弹精灵"""def __init__(self):image_name = "./images/bullet1.png"super().__init__(image_name, -2)def update(self, *args):super().update(args)# 判断是否超出屏幕if self.rect.bottom < 0:self.kill()class Enemy(PlaneSprite):"""敌机精灵"""def __init__(self):image_names = ["./images/enemy1.png"]destroy_names = GameSprite.image_names("enemy1_down", 4)super().__init__(image_names, destroy_names, 2, 1)# 随机敌机出现位置width = SCREEN_RECT.width - self.rect.widthself.rect.left = random.randint(0, width)self.rect.bottom = 0# 随机速度self.speed = random.randint(1, 3)def update(self, *args):super().update(args)# 判断敌机是否移出屏幕if self.rect.top >= SCREEN_RECT.height:# 将精灵从所有组中删除self.kill()# 判断敌机是否已经被销毁if self.can_destroied:self.kill()

实现效果图


图片素材:

链接:https://pan.baidu.com/s/1LKoVQCRApscxpgg4MYznyw
提取码:chui


此项目为学习python时笔记
来源于:黑马程序员python教程

Python飞机大战项目终篇(一步一步实现---最全笔记)相关推荐

  1. C++的学习心得和知识总结(十六)|基于EasyX实现小甲鱼Python飞机大战项目(C++版)

    目录结构 注:提前言明 本文借鉴了以下博主.书籍或网站的内容,其列表如下: 1.小甲鱼Python项目 – 飞机大战 2.本文使用的掩码图生成工具 自动生成遮罩图的程序,点击前往 3.EasyX官方链 ...

  2. 通过pygame实现的python飞机大战项目

    飞机大战 游戏主要特点 一个佛系介绍 游戏玩法 一段没有素材等于废话的全代码 游戏主要特点 游戏自带时间计数和成绩计数. 游戏的子弹有7种,通过拾取道具可以升级子弹,子弹拥有持续时间,超过持续时间会降 ...

  3. python飞机大战项目描述_飞机大战项目梳理(一)

    尽力而为和志在必得还是有点差别的. 一.思路梳理 今天的内容:建立一个空白的窗口,在这个窗口里需要有一个小飞机(图片),小飞机可以根据用户按键向右或向左移动. "建立窗口"  就需 ...

  4. python飞机大战任务报告_Python飞机大战实战项目案例

    都说实践是检验知识掌握程度的最好测试.随着Python学习者的增长,越来越多的零基础入门课程让人眼花缭乱.虽然说基础理论的学习十分重要,但是如果仅仅只学习理论知识,也是远远不够的.飞机大战的项目实战可 ...

  5. python飞机大战程序导入_Python飞机大战项目的准备 导入Pygame最全攻略~

    1.导入pygame 首先是导入pygame模块,下面介绍的是pycharm中导入 先建立一个项目 左上角File->Setting->project:飞机大战项目(你自己的文件名字)-& ...

  6. Python飞机大战(究极迫害版)

    最近Python学到了项目开发,里面有一个飞机大战的项目挺感兴趣的,不过只是把别人做过的项目重做一遍未免太无聊,所以打算做一个究极迫害版. 首先是对原来项目图片的处理 在上面的图片中,原来是战机的图片 ...

  7. python飞机大战源码素材包_小甲鱼python基础教程飞机大战源码及素材

    原博文 2018-12-22 23:32 − 百度了半天小甲鱼python飞机大战的源码和素材,搜出一堆不知道是什么玩意儿的玩意儿. 最终还是自己对着视频一行行代码敲出来. 需要的同学点下面的链接自取 ...

  8. python飞机大战加背景音乐_python实现飞机大战小游戏 python飞机大战中的音频文件怎么改成MP3...

    怎么样用Python写飞机大战游戏 python开发飞机大战外星人游戏怎么弄双人模式新的一年,哪怕仍是一个人,也要活得像一支队伍,为自己的头脑和心灵招兵买马,不气馁,有召唤,爱自由. 主函数 impo ...

  9. Python“飞机大战”上下左右移动空格发射子弹

    下载点此去 最后面有运行视频 一.项目背景 作为一名学习计算机的学生,在以往,我认为学习计算机要么就是无所不能的黑客,要么就是能制作出各种软件程序的大神.我选择pygame板块,制作一款能随意更改游戏 ...

  10. Python和Java结合的项目实战_[项目实战] Python高级教程项目实战篇 Python和Java结合的项目实战 视频教程 [...

    资源介绍 课程简介:xa0xa0 Python高级教程项目实战篇 Python和Java结合的项目实战 视频教程 教学视频 ----------------------课程目录 Python项目实战篇 ...

最新文章

  1. 抽象工厂与工厂模式例子
  2. mybatis一级缓存导致sql查询出现问题
  3. Javascript调用OCX控件
  4. matlab上位机串口通信,MATLAB GUIDE 上位机串口通信开发 绘制图形
  5. qdialog不允许放大缩小弹窗_iOS 13系统太烦人!频繁弹窗提醒App正在定位,竟是为保护隐私?...
  6. 深度学习-Tensorflow2.2-模型保存与恢复{9}-保存与恢复-21
  7. Centos 7 文件管理基础命令
  8. python自增_Python的自增运算与Python变量的浅析
  9. linux下配置ndk路径,NDK调试arm-linux-androideabi-addr2line工具的使用
  10. 继承类 基类的赋值_Chapter10:继承与派生(四)
  11. PhoneGap开发环境搭建
  12. 程序员专属段子集锦 1/10
  13. 16.定位模板,布局和样式
  14. 2010_8_16_Router_路由器的密码恢复
  15. 常量、变量;基本数据类型;input()、if、while、break、continue
  16. dstwo linux 模拟器,dstwo使用gba模拟器V1.30版本下载和使用作弊功能的作弊教程
  17. 【gp数据库】你可能不知道却超级实用的函数
  18. 软件测试面试题和简历模板(面试前准备篇)
  19. Scrapy爬虫之网站图片爬取
  20. 网络编辑必知常识:什么是PV、UV和PR值

热门文章

  1. DirectX修复工具全部文件修复失败或下载失败的解决方法
  2. 程序员软件工程常用的画图软件推荐
  3. apktool + dex2jar + xjad反编译apk文件
  4. 马尔可夫链、隐马尔科夫模型、贝叶斯网络、因子图
  5. 线性判别分析LDA算法与python实现
  6. android svn上传代码,Android应用开发之项目上传svn(Android Studio)
  7. (一)appium-desktop定位元素原理
  8. 【zbrush】毛发系统 -打散、分区、制作等
  9. iPad最新型笔刷,足足五种分类,几百款
  10. 3DMax室内设计初学者须知灯光布光…