飞机大战(项目实战)
左右移动飞机,子弹可以消灭飞机
目的: 综合复习 面向对象
-
需要模块: pygame
安装(在终端输入)
sudo pip3 install pygame
验证安装
python3 -m pygame.examples.aliens -
需要游戏素材照片
-
设计思路
将静止的图片,放到游戏窗口中取
通过用户交互,移动图片,产生动画效果
使用pygame创建游戏初始化窗口
pygame.init() 导入初始化 的pygame模块
pygame.quit() 卸载pygame模块
游戏窗口中的坐标系
x和y确定矩形的左上角出发点,width和height确定矩形的大小
pygame.React 用于描述矩阵区域
案例:
定义hero_act 描述英雄的位置和大小
import pygame
pygame.init()
hero_act = pygame.Rect(100, 500, 120, 125)
print("英雄原点 %d %d" % (hero_act.x, hero_act.y))
print("英雄尺寸 %d %d" % (hero_act.width, hero_act.height))
print('%d %d' % (hero_act.width, hero_act.height))
pygame.quit()
pygame 2.0.1 (SDL 2.0.14, Python 3.8.8)
Hello from the pygame community. https://www.pygame.org/contribute.html
英雄原点 100 500
英雄尺寸 120 125
120 125
创建游戏主窗口
pygame.display 用于创建,管理游戏窗口
setmodel创建游戏窗口,需要三个参数。
第一个参数:窗口大小
flags 附加选项
depth 颜色的位数,默认自动匹配
使用pygame绘制图像
第一步 加载图片
第二步 将 图片 blit到指定位置
第三步 调用pygame.display.update()更新屏幕
import pygame
pygame.init()
screen = pygame.display.set_mode((480, 700))
# 绘制背景图像
bg = pygame.image.load("./images/background.png")
screen.blit(bg, (0, 0))
pygame.display.update()
while True:
pass
pygame.quit()
绘制英雄飞机图像
import pygame
pygame.init()
screen = pygame.display.set_mode((480, 700))
# 绘制背景图像
bg = pygame.image.load("./images/background.png")
screen.blit(bg, (0, 0))
pygame.display.update()
# 绘制英雄的图像
hero = pygame.image.load("./images/me1.png")
screen.blit(hero, (200, 500))
pygame.display.update()
while True:
pass
pygame.quit()
update的作用
可以在所有绘制完成之后,统一调用update方法
screen 好比一个画布
screen.blit 在画布上 画图
display.update 展示最终画的情况
动起来
游戏动画实现原理: 跟电影类似,将多张图像连续快速播放。
在频幕上 快速绘制多张图像。
帧: 每个静止的画面
对应调用的 update方法
每秒60帧就可以产生连续高品质的动画效果。
游戏时钟
用于控制游戏内部循环的帧率
- 游戏初始化 创建 时钟对象
- 时钟对象调用tick(帧率)方法
clock.tick(数字) 表示 每秒 循环 数字 次数
# 游戏初始化
pygame.init()
screen = pygame.display.set_mode((480, 700))
bg = pygame.image.load("./images/background.png")
screen.blit(bg, (0, 0))
hero = pygame.image.load("./images/me1.png")
screen.blit(hero, (200, 500))
pygame.display.update()
# 创建时钟对象
clock = pygame.time.Clock()
i = 0
while True:
# 指定循环体 内部的代码执行的频率
clock.tick(1) # 每秒循环1次
print(i)
i += 1
pass
pygame.quit()
英雄的简单动画实现
import pygame
# 游戏初始化
pygame.init()
screen = pygame.display.set_mode((480, 700))
bg = pygame.image.load("./images/background.png")
screen.blit(bg, (0, 0))
hero = pygame.image.load("./images/me1.png")
screen.blit(hero, (150, 500))
pygame.display.update()
# 创建时钟对象
clock = pygame.time.Clock()
# 定义rect 记录飞机的初始位置
hero_rect = pygame.Rect(150, 500, 102, 126)
i = 0
while True:
# 指定循环体 内部的代码执行的频率,60表示每秒执行60次
clock.tick(60)
# 修改飞机的位置
hero_rect.y -= 1
# 调用blit方法
screen.blit(bg, (0, 0))
screen.blit(hero, hero_rect)
# 调用update方法更新显示
pygame.display.update()
pygame.quit()
升级版
import pygame
# 游戏初始化
pygame.init()
screen = pygame.display.set_mode((480, 700))
bg = pygame.image.load("./images/background.png")
screen.blit(bg, (0, 0))
hero = pygame.image.load("./images/me1.png")
screen.blit(hero, (150, 500))
pygame.display.update()
# 创建时钟对象
clock = pygame.time.Clock()
# 定义rect 记录飞机的初始位置
hero_rect = pygame.Rect(150, 500, 102, 126)
i = 0
while True:
# 指定循环体 内部的代码执行的频率,60表示每秒执行60次
clock.tick(60)
# 修改飞机的位置
hero_rect.y -= 1
# 判断飞机的位置 hero_rect.bottom是表示图片的底部
if hero_rect.bottom <= 0:
hero_rect.y = 700
# 调用blit方法
screen.blit(bg, (0, 0))
screen.blit(hero, hero_rect)
# 调用update方法更新显示
pygame.display.update()
pygame.quit()
监听事件
事件(event): 用户针对游戏的操作
eg: 点击关闭按键,点击鼠标 按下键盘
监听: 判断用户 的具体操作
pygame.event.get() 可以获得用户当前所做动作 的 事件列表
import pygame
# 游戏初始化
pygame.init()
screen = pygame.display.set_mode((480, 700))
bg = pygame.image.load("./images/background.png")
screen.blit(bg, (0, 0))
hero = pygame.image.load("./images/me1.png")
screen.blit(hero, (150, 500))
pygame.display.update()
# 创建时钟对象
clock = pygame.time.Clock()
# 定义rect 记录飞机的初始位置
hero_rect = pygame.Rect(150, 500, 102, 126)
i = 0
while True:
# 指定循环体 内部的代码执行的频率,60表示每秒执行60次
clock.tick(60)
# 捕获事件
event_list = pygame.event.get()
if event_list:
print(event_list)
# 修改飞机的位置
hero_rect.y -= 1
# 判断飞机的位置 hero_rect.bottom是表示图片的底部
if hero_rect.bottom <= 0:
hero_rect.y = 700
# 调用blit方法
screen.blit(bg, (0, 0))
screen.blit(hero, hero_rect)
# 调用update方法更新显示
pygame.display.update()
pygame.quit()
增加 退出事件 的捕获
import pygame
# 游戏初始化
pygame.init()
screen = pygame.display.set_mode((480, 700))
bg = pygame.image.load("./images/background.png")
screen.blit(bg, (0, 0))
hero = pygame.image.load("./images/me1.png")
screen.blit(hero, (150, 500))
pygame.display.update()
# 创建时钟对象
clock = pygame.time.Clock()
# 定义rect 记录飞机的初始位置
hero_rect = pygame.Rect(150, 500, 102, 126)
i = 0
while True:
# 指定循环体 内部的代码执行的频率,60表示每秒执行60次
clock.tick(60)
# 捕获事件
event_list = pygame.event.get()
for event in event_list:
# 判断事件类型是否满足退出事件
if event.type == pygame.QUIT:
print("游戏退出...")
# 卸载所有模块 退出
pygame.quit()
exit()
# 修改飞机的位置
hero_rect.y -= 1
# 判断飞机的位置 hero_rect.bottom是表示图片的底部
if hero_rect.bottom <= 0:
hero_rect.y = 700
# 调用blit方法
screen.blit(bg, (0, 0))
screen.blit(hero, hero_rect)
# 调用update方法更新显示
pygame.display.update()
pygame.quit()
精灵 和 精灵组
精灵: 包含了图像位置 和 显示的对象
精灵 通俗的理解:就好比一个可以移动的类,用来制作可以移动的对象。
精灵组: 包含多个精灵
目的是为了操控 所有精灵
精灵组 update 一下子更新所有精灵的位置
精灵组 draw 一下子 绘制所有精灵组的图片
精灵组display.update() 将图片展示出来
import pygame
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 = speed
def update(self):
# 在屏幕的垂直方向上移动
self.rect.y += self.speed
派生精灵子类
新建 plane_sprites.py文件
在其中定义 GameSprite 继承 pygame.sprite.Sprite
封装三个属性:
图像
位置
速度
重写一个方法:
垂直方向的运动
使用游戏精灵 和 精灵组 创建敌机
#!/usr/bin/python3
# coding=utf-8
# Author by Zhenzhou Zhou
import pygame
from plane_sprites import *
# 游戏初始化
pygame.init()
screen = pygame.display.set_mode((480, 700))
bg = pygame.image.load("./images/background.png")
screen.blit(bg, (0, 0))
hero = pygame.image.load("./images/me1.png")
screen.blit(hero, (150, 500))
pygame.display.update()
# 创建时钟对象
clock = pygame.time.Clock()
# 定义rect 记录飞机的初始位置
hero_rect = pygame.Rect(150, 500, 102, 126)
i = 0
# 创建敌机的精灵
enemy = GameSprite("./images/enemy1.png")
enemy1 = GameSprite("./images/enemy1.png", 2)
# 创建敌机的精灵组
enemy_group = pygame.sprite.Group(enemy, enemy1)
while True:
# 指定循环体 内部的代码执行的频率,60表示每秒执行60次
clock.tick(60)
# 捕获事件
event_list = pygame.event.get()
for event in event_list:
# 判断事件类型是否满足退出事件
if event.type == pygame.QUIT:
print("游戏退出...")
# 卸载所有模块 退出
pygame.quit()
exit()
# 修改飞机的位置
hero_rect.y -= 1
# 判断飞机的位置 hero_rect.bottom是表示图片的底部
if hero_rect.bottom <= 0:
hero_rect.y = 700
# 调用blit方法
screen.blit(bg, (0, 0))
screen.blit(hero, hero_rect)
# 调用update方法更新显示
# 让精灵组调用两个方法
# update - 让组中的所有精灵更新位置
enemy_group.update()
# draw - 在screen上绘制所有的精灵
enemy_group.draw(screen)
pygame.display.update()
pygame.quit()
游戏框架的搭建
两个部分: 游戏初始化 和 游戏循环
游戏初始化:
创建游戏窗口,用于展示。
创建游戏时钟,通过时钟设置 刷新帧率。
根据游戏需求,创建 不同精灵 和 精灵组。
封装一个私有方法,用于 调用精灵组or精灵的属性,负责精灵组 和 精灵的创建。
游戏循环: 一旦开始执行,游戏就开始了。
start game 开启游戏循环。
设置刷新帧率,控制游戏内部代码执行的频率。
事件监听,捕获用户 对游戏的操作。
碰撞检测,玩家 和 敌机 碰撞,导致双方销毁。
更新,绘制精灵组在屏幕上,更新屏幕显示。
game over 结束游戏。
属性:
封装 屏幕 用于 精灵绘制
封装 时间 用于 设置刷新帧率
封装 精灵
方法:
初始化方法 完成 游戏初期制作
startgame 完成 游戏循环
明确几个文件用于开发游戏
两个文件
plane_main: 游戏主程序
plane_sprites: 屏幕尺寸,游戏子精灵
主游戏类文件: 负责启动游戏,创建玩家
飞机精灵文件 : 负责提供攻击的敌人 和 相关工具
常量和变量替代固定数值
常量: 不可变化的量
变量: 可以变化的量
定义常量 和 变量的语法完全一样,使用赋值语句
但是定义常量 时候,所有的字母都得使用大写,单词和单词之间使用下划线连接
SCREEN_RECT = pygame.Rect(0, 0, 480, 700)
import pygame
from plane_sprites import *
SCREEN_RECT = pygame.Rect(0, 0, 480, 700)
class PlaneGame(object):
'''飞机大战主游戏'''
def __init__(self):
print("游戏初始化")
# 创建游戏的窗口set_mode方法
self.screen = pygame.display.set_mode(SCREEN_RECT.size)
# 创建游戏的时钟
self.clock = pygame.time.Clock()
# 调用私有方法,精灵和精灵组的创建
self.__create_sprites()
def __create_sprites(self):
pass
def start_game(self):
print("游戏开始...")
while True:
if __name__ == '__main__':
# 创建游戏对象
game = PlaneGame()
# 启动游戏
game.start_game()
主文件:框架
#!/usr/bin/python3
# coding=utf-8
# Author by Zhenzhou Zhou
import pygame
from plane_sprites import *
class PlaneGame(object):
'''飞机大战主游戏'''
def __init__(self):
print("游戏初始化")
# 创建游戏的窗口set_mode方法
self.screen = pygame.display.set_mode(SCREEN_RECT.size)
# 创建游戏的时钟
self.clock = pygame.time.Clock()
# 调用私有方法,精灵和精灵组的创建
self.__create_sprites()
def __create_sprites(self):
pass
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()
pass
def __event_handler(self):
for event in pygame.event.get():
# 判断是否退出游戏
if event.type == pygame.QUIT:
PlaneGame.__game_over()
def __check_collide(self):
pass
def __update_sprites(self):
pass
@staticmethod
def __game_over():
print("游戏结束")
pygame.quit()
exit()
if __name__ == '__main__':
# 创建游戏对象
game = PlaneGame()
# 启动游戏
game.start_game()
背景图像交替滚动实现
背景图像连续变化,游戏的玩家的位置并没有变化
class Background(GameSprite):
'''游戏背景精灵'''
def update(self):
# 调用父类的方法实现
super().update()
# 判断是否移除屏幕,如果移除屏幕,将图像设置到屏幕的上方
if self.rect.y >= SCREEN_RECT.height:
self.rect.y = -self.rect.height
敌机出场
使用 定时器 添加 敌机
设计 Enemy类
使用定时器 添加敌机
- 游戏启动,每隔1s出现一架敌机
- 每架敌机向屏幕下方飞行,飞行速度不相同
- 每架敌机出现的 水平位置 不尽相同
- 当敌机从屏幕下方飞出,不再会飞回到屏幕中去
定时器
语法:
set_timer(eventid, milliseconds)
使用set_timer,每隔milliseconds时间创建一个(事件类型为eventid)的空事件。
定时器的监听:
通过pygame.event.get() 获取当前时刻所有事件列表。
遍历列表,判断event.type是否等于eventid,如果相等,表示 定时器事件 发生。
定义并监听创建敌机的定时器事件
定义定时器常量 eventid
初始化方法中,通过 set_timer 设定定时器事件
游戏循环中, 监听定时器事件
#!/usr/bin/python3
# coding=utf-8
# Author by Zhenzhou Zhou
import pygame
from plane_sprites import *
class PlaneGame(object):
'''飞机大战主游戏'''
def __init__(self):
print("游戏初始化")
# 创建游戏的窗口set_mode方法
self.screen = pygame.display.set_mode(SCREEN_RECT.size)
# 创建游戏的时钟
self.clock = pygame.time.Clock()
# 调用私有方法,精灵和精灵组的创建
self.__create_sprites()
pygame.init()
# 设置定时器事件 - 创建敌机 1s,这里单位是毫秒,100表示1秒
# 表示 每1s创建一下CREATE_ENERMY_EVENT事件
pygame.time.set_timer(CREATE_ENEMY_EVENT, 1000)
def __create_sprites(self):
'''创建 背景精灵 和 精灵组'''
bg1 = Background()
bg2 = Background(True)
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()
pass
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('敌机出场......')
def __check_collide(self):
pass
def __update_sprites(self):
self.back_group.update()
self.back_group.draw(self.screen)
@staticmethod
def __game_over():
print("游戏结束")
pygame.quit()
exit()
if __name__ == '__main__':
# 创建游戏对象
game = PlaneGame()
# 启动游戏
game.start_game()
设计 Enemy类(敌人)
指定敌机的图片
随机 敌机的 初始位置 和 初始速度
初始化:
指定敌机图片
随机 敌机初始位置 和 初始速度
重写update方法
判断敌机是否飞出屏幕,如果是,从精灵组中删除
class Enemy(GameSprite):
'''敌机精灵'''
def __init__(self):
# 调用父类方法,创建敌机精灵,指定敌机图片
super().__init__('./imaegs/enemy1.png')
# 指定敌机的初始随机速度
# 指定敌机的初始随机位置
pass
def update(self):
# 调用父类方法,保持垂直方向的飞行
super().update()
# 判断是否飞出屏幕,如果是,需要从精灵组删除敌机
if self.rect.y >= SCREEN_RECT.height:
print("飞出屏幕,需要从精灵组删除...")
pass
创建敌机
在__create_sprites中,添加 敌机精灵组
敌机是定时被创建的, 初始化方法中,不需要创建敌机
在__event_hander 创建敌机,添加到精灵组
在 __update_sprites,让敌机精灵组调用update和draw方法
def __create_sprites(self):
# 创建 背景精灵 和 精灵组
bg1 = Background()
bg2 = Background(True)
self.back_group = pygame.sprite.Group(bg1, bg2)
# 创建敌机的精灵组
self.enemy_group = pygame.sprite.Group()
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 __update_sprites(self):
self.back_group.update()
self.back_group.draw(self.screen)
self.enemy_group.update() # 更新所有敌机位置
self.enemy_group.draw(self.screen) # 将敌机位置绘制到图上
随机敌机位置 和速度
导入模块顺序:
官方标准模块
第三方模块
应用程序模块
移除屏幕前 销毁敌机
敌机移出屏幕前,销毁敌机
class Enemy(GameSprite):
'''敌机精灵'''
def __init__(self):
# 调用父类方法,创建敌机精灵,指定敌机图片
super().__init__('./images/enemy1.png')
# 指定敌机的初始随机速度(1~~3)
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()
def __del__(self):
print("敌机挂了! %s" % self.rect)
玩家飞机登场
两个 类:
玩家飞机 (英雄)
子弹
定时器功能:发射字典
键盘控制 飞机移动: pygame.keyboard
子弹上飞 y值不断减少,子弹的初始速度设置为负数
子弹飞出屏幕,从精灵组中删除,避免占用内存
飞机定义发射子弹
重接介绍React的属性
class Hero(GameSprite):
'''玩家飞机-英雄精灵'''
def __init__(self):
# 调用父类,设置 image和speed
super().__init__('./images/me1.png', 0) # 初始速度为0
# 设置英雄初始位置
self.rect.centerx = SCREEN_RECT.centerx
self.rect.bottom = SCREEN_RECT.bottom - 120
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 __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)
移动英雄的位置
捕获键盘 按键 操作
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)
# 键盘按键:方法1
# elif event.type == pygame.KEYDOWN and event.key == pygame.K_RIGHT:
# print('向右移动...')
# 使用键盘提供方法-按键元组:方法2
key_pressed = pygame.key.get_pressed()
if key_pressed[pygame.K_RIGHT]:
print('向右移动...')
移动英雄位置
叠加speed 和rect.x
通过update方法
先准备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)
# 键盘按键:方法1
# elif event.type == pygame.KEYDOWN and event.key == pygame.K_RIGHT:
# print('向右移动...')
# 使用键盘提供方法-按键元组:方法2
key_pressed = pygame.key.get_pressed()
if key_pressed[pygame.K_RIGHT]:
print('向右移动...')
self.hero.speed = 2
elif key_pressed[pygame.K_LEFT]:
print('向左移动...')
self.hero.speed = -2
class Hero(GameSprite):
'''玩家飞机-英雄精灵'''
def __init__(self):
# 调用父类,设置 image和speed
super().__init__('./images/me1.png', 0) # 初始速度为0
# 设置英雄初始位置
self.rect.centerx = SCREEN_RECT.centerx
self.rect.bottom = SCREEN_RECT.bottom - 120
def update(self):
# 英雄在水平方向移动
self.rect.x += self.speed
控制英雄的移动位置
update方法
理解 react的right属性
def update(self):
# 英雄在水平方向移动
self.rect.x += self.speed
# 控制英雄不能离开屏幕
if self.rect.x < 0:
self.rect.x = 0
elif self.rect.right > SCREEN_RECT.right:
self.rect.right = SCREEN_RECT.right
发射子弹
英雄类 准备fire方法
一旦检测到发射子弹事件,就让英雄调用fire方法
class PlaneGame(object):
'''飞机大战主游戏'''
def __init__(self):
print("游戏初始化")
# 创建游戏的窗口set_mode方法
self.screen = pygame.display.set_mode(SCREEN_RECT.size)
# 创建游戏的时钟
self.clock = pygame.time.Clock()
# 调用私有方法,精灵和精灵组的创建
self.__create_sprites()
pygame.init()
# 设置定时器事件 - 创建敌机 1s
pygame.time.set_timer(CREATE_ENEMY_EVENT, 1000)
# 设置定时器事件 - 发射子弹 0.5s(每隔0.5s发生子弹)
pygame.time.set_timer(HERO_FIRE_EVENT, 500)
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)
# 键盘按键:方法1
# elif event.type == pygame.KEYDOWN and event.key == pygame.K_RIGHT:
# print('向右移动...')
# 使用键盘提供方法-按键元组:方法2
key_pressed = pygame.key.get_pressed()
if key_pressed[pygame.K_RIGHT]:
print('向右移动...')
self.hero.speed = 2
elif key_pressed[pygame.K_LEFT]:
print('向左移动...')
self.hero.speed = -2
elif event.type == HERO_FIRE_EVENT:
self.hero.fire()
def fire(self):
print("发射子弹")
定义子弹类
子弹 从英雄正上方,向上方飞行(速度设置为负数)
飞出屏幕后,从精灵组中删除
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('子弹被销毁了...')
发射子弹
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('子弹被销毁了...')
一次发射三个子弹
以玩家飞机 为标准
上方 三个位置三个子弹
0 20 40
def fire(self):
print("发射子弹")
for i in (0, 1, 2):
# 创建子弹精灵
bullet = Bullet()
# 设置精灵位置 (水平位置 和 垂直位置)
# 指定 y值
bullet.rect.bottom = self.rect.y - i * 20 # 在英雄上方0,20,40高处
# 指定 中心点
bullet.rect.centerx = self.rect.centerx
# 将子弹精灵添加到精灵组
self.bullets.add(bullet)
碰撞检测
pygame.sprite.groupcollide()
4个参数
group1对象 和 group2对象 都是精灵组
检测这两个精灵组的精灵对象 是否相碰
dokill1 摧毁精灵1
dokill2 摧毁精灵2
def __check_collide(self):
# 子弹摧毁敌机
pygame.sprite.groupcollide(self.hero.bullets, self.enemy_group, True, True)
方法会返回一个列表
Sprite_list 列表
def __check_collide(self):
# 子弹摧毁敌机
pygame.sprite.groupcollide(self.hero.bullets, self.enemy_group, True, True)
# 敌机撞毁英雄
ememies = pygame.sprite.groupcollide(self.hero_group, self.enemy_group, False, True)
# 判断列表是否有内容
if len(ememies) > 0:
# 让英雄牺牲
self.hero.kill()
# 结束游戏
PlaneGame.__game_over()
最后代码总结
两个文件
plan_main.py
#!/usr/bin/python3
# coding=utf-8
# Author by Zhenzhou Zhou
import pygame
from plane_sprites import *
class PlaneGame(object):
'''飞机大战主游戏'''
def __init__(self):
print("游戏初始化")
# 创建游戏的窗口set_mode方法
self.screen = pygame.display.set_mode(SCREEN_RECT.size)
# 创建游戏的时钟
self.clock = pygame.time.Clock()
# 调用私有方法,精灵和精灵组的创建
self.__create_sprites()
pygame.init()
# 设置定时器事件 - 创建敌机 1s
pygame.time.set_timer(CREATE_ENEMY_EVENT, 1000)
# 设置定时器事件 - 发射子弹 0.5s(每隔0.5s发生子弹)
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()
pass
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)
# 键盘按键:方法1
# elif event.type == pygame.KEYDOWN and event.key == pygame.K_RIGHT:
# print('向右移动...')
# 使用键盘提供方法-按键元组:方法2
key_pressed = pygame.key.get_pressed()
if key_pressed[pygame.K_RIGHT]:
print('向右移动...')
self.hero.speed = 2
elif key_pressed[pygame.K_LEFT]:
print('向左移动...')
self.hero.speed = -2
elif event.type == HERO_FIRE_EVENT:
self.hero.fire()
def __check_collide(self):
# 子弹摧毁敌机
pygame.sprite.groupcollide(self.hero.bullets, self.enemy_group, True, True)
# 敌机撞毁英雄
ememies = pygame.sprite.groupcollide(self.hero_group, self.enemy_group, False, True)
# 判断列表是否有内容
if len(ememies) > 0:
# 让英雄牺牲
self.hero.kill()
# 结束游戏
PlaneGame.__game_over()
def __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)
@staticmethod
def __game_over():
print("游戏结束")
pygame.quit()
exit()
if __name__ == '__main__':
# 创建游戏对象
game = PlaneGame()
# 启动游戏
game.start_game()
plane_sprites.py
#!/usr/bin/python3
# coding=utf-8
# Author by Zhenzhou Zhou
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 + 1
class GameSprite(pygame.sprite.Sprite):
"""飞机大战游戏-精灵"""
def __init__(self, image_name, speed=1):
# 调用父类初始化方法
super().__init__()
# 定义对象的属性
# 图像名
self.image = pygame.image.load(image_name)
# 图像初始化位置 get_react()会自动计算图像矩阵
self.rect = self.image.get_rect()
# 飞机速度
self.speed = speed
def update(self):
# 在屏幕的垂直方向上移动 速度
self.rect.y += self.speed
class Background(GameSprite):
'''游戏背景精灵'''
def __init__(self, is_alt=False):
# 调用父类方法实现精灵的创建(image/rect/speed)
super().__init__("./images/background.png")
# 判断是否是交替互相,如果是,设置初始位置
if is_alt:
self.rect.y = -self.rect.height
def update(self):
# 调用父类的方法实现
super().update()
# 判断是否移除屏幕,如果移除屏幕,将图像设置到屏幕的上方
if self.rect.y >= SCREEN_RECT.height:
self.rect.y = -self.rect.height
class Enemy(GameSprite):
'''敌机精灵'''
def __init__(self):
# 调用父类方法,创建敌机精灵,指定敌机图片
super().__init__('./images/enemy1.png')
# 指定敌机的初始随机速度(1~~3)
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()
def __del__(self):
# print("敌机挂了! %s" % self.rect)
pass
class Hero(GameSprite):
'''玩家飞机-英雄精灵'''
def __init__(self):
# 调用父类,设置 image和speed
super().__init__('./images/me1.png', 0) # 初始速度为0
# 设置英雄初始位置
self.rect.centerx = SCREEN_RECT.centerx
self.rect.bottom = SCREEN_RECT.bottom - 120
# 创建子弹精灵组
self.bullets = pygame.sprite.Group()
def update(self):
# 英雄在水平方向移动
self.rect.x += self.speed
# 控制英雄不能离开屏幕
if self.rect.x < 0:
self.rect.x = 0
elif self.rect.right > SCREEN_RECT.right:
self.rect.right = SCREEN_RECT.right
def fire(self):
print("发射子弹")
for i in (0, 1, 2):
# 创建子弹精灵
bullet = Bullet()
# 设置精灵位置 (水平位置 和 垂直位置)
# 指定 y值
bullet.rect.bottom = self.rect.y - i * 20 # 在英雄上方0,20,40高处
# 指定 中心点
bullet.rect.centerx = self.rect.centerx
# 将子弹精灵添加到精灵组
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('子弹被销毁了...')