一、游戏框架搭建
目标:使用面向对象设计飞机大战游戏类
01.命令主程序职责
一个游戏主程序的职责可以分为以下两个部分:
● 游戏初始化
● 游戏循环
根据明确的职责,设计PlanGame类:
02.实现飞机大战主游戏类
1.明确文件职责
● plane_main
1.封装主游戏类
2.创建游戏对象
3.启动游戏
● plane_sprites
1.封装游戏中所有需要使用的精灵子类
2.提供游戏的相关工具
代码实现:
1.新建 plane_main.py 文件,并且设置为可执行
2.编写基础代码
2.游戏初始化
import pygame
from plane_sprites import *
"""飞机大战主游戏 """
class PlaneGame(object):
def __init__(self):
print("游戏初始化")
# 1.创建游戏窗口
self.screen = pygame.display.set_mode((350,573))
# 2.创建游戏时钟
self.clock = pygame.time.Clock()
# 3.调用私有方法
self.__create_sprites()
# 定义私有方法,创建精灵和精灵组
def __create_sprites(self):
pass
def start_game(self):
print("游戏开始...")
if __name__ == '__main__':
# 创建游戏对象
game = PlaneGame()
# 启动游戏
game.start_game()
3.常量的使用
常量:不变化的量 变量:可以变化的量
应用场景:
● 在开发时,可能会需要使用固定的数值,例如:屏幕的高度是700
● 在这个时侯,建议不要直接使用固定数值,而应该使用常量
● 在开发时,为了保证代码的可维护性,尽量不要使用魔法数字
常量的定义:
● 定义常量和定义变量的语法完全一样,都是使用赋值语句
● 常量的命名应该所有字母都使用大写,单词与单词之间使用下划线连接
常量的好处:
● 阅读代码时,通过常量名见名知意,不需要猜测数字的含义
● 如果需要调整值,只需要修改常量定义就可以实现统一修改
改进:使用 常量 代替 固定的数值
plane_sprites.py文件内容:
import pygame
## 定义屏幕大小的常量
SCREEN_RECT = pygame.Rect(0,0,350,573)
"""飞机大战游戏精灵"""
# 继承pygame.sprite.Sprite父类
class GameSprite(pygame.sprite.Sprite):
# 1.初始化方法:定义属性
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
# 2.定义方法
def update(self):
# 在屏幕的垂直方向上移动
self.rect.y += self.speed
plane_mian.py文件内容:
import pygame
from plane_sprites import *
"""飞机大战主游戏 """
class PlaneGame(object):
def __init__(self):
print("游戏初始化")
# 1.创建游戏窗口;SCREEN_RECT.size:获取矩形对象中的元组
self.screen = pygame.display.set_mode(SCREEN_RECT.size)
# 2.创建游戏时钟
self.clock = pygame.time.Clock()
# 3.调用私有方法
self.__create_sprites()
# 定义私有方法,创建精灵和精灵组
def __create_sprites(self):
pass
def start_game(self):
print("游戏开始...")
while True:
pass
if __name__ == '__main__':
# 创建游戏对象
game = PlaneGame()
# 启动游戏
game.start_game()
4.搭建启动游戏方法结构
plane_sprites.py文件添加内容:
# 定义刷新帧率的常量
FRAME_PRE_SEC = 60
plane_mian.py文件内容:
import pygame
from plane_sprites import *
"""飞机大战主游戏 """
class PlaneGame(object):
def __init__(self):
print("游戏初始化")
# 1.创建游戏窗口;SCREEN_RECT.size:获取矩形对象中的元组
self.screen = pygame.display.set_mode(SCREEN_RECT.size)
# 2.创建游戏时钟
self.clock = pygame.time.Clock()
# 3.调用私有方法
self.__create_sprites()
# 定义私有方法,创建精灵和精灵组
def __create_sprites(self):
pass
def start_game(self):
print("游戏开始...")
# 游戏循环
while True:
# 1.设置刷新帧率
self.clock.tick(FRAME_PRE_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):
pass
def __update_sprites(self):
pass
@staticmethod # 静态方法
def __game__over():
print("游戏结束")
pygame.quit()
exit()
if __name__ == '__main__':
# 创建游戏对象
game = PlaneGame()
# 启动游戏
game.start_game()
二、游戏背景
目标:显示游戏背景交替滚动
01.背景交替滚动的思路确定
● 游戏启动后,背景图像会连续不断的向下方移动
● 在视觉上产生英雄的飞机不断向上方飞行的错觉,在很多跑酷类游戏中常用的套路
游戏的背景 不断变化
游戏的主角 位置保持不变
1.实现思路分析
1.创建两张背景图像精灵
● 第一张完全和屏幕重合
● 第二张在屏幕的正上方
2.两张图像一起向下运动
● self.rect.y += self.speed
3.当任意一张背景精灵的rect.y >= 屏幕的高度 说明已经移动到屏幕下方
4.将移动到屏幕下方的这张图像设置到屏幕的正上方
● rect.y = -rect.height
2.设计背景类
初始化方法:
● 直接指定背景图片
● is_alt 判断是否设另一张图像:False表示第一张图像,需要与屏幕重合;True表示另一张图像,在屏幕的正上方
update()方法:
● 判断是否移出屏幕,如果是,将图像设置到屏幕的正上方,从而实现交替滚动
在继承中,如果父类提供的方法不能满足子类的需求:
派生一个子类,在子类中针对特有的需求,重写父类方法,并且进行扩展
02.显示游戏背景
1.背景精灵的基本实现
在 plane_sprites.py文件中 新建 Background类 继承自 GameSprite类
import pygame
# 定义屏幕大小的常量
SCREEN_RECT = pygame.Rect(0,0,350,573)
# 定义刷新帧率的常量
FRAME_PRE_SEC = 60
"""飞机大战游戏精灵"""
# 继承系统自带的pygame.sprite.Sprite类
class GameSprite(pygame.sprite.Sprite):
# 1.初始化方法:定义属性
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
# 2.定义方法
def update(self):
# 在屏幕的垂直方向上向下移动
self.rect.y += self.speed
"""背景精灵"""
# 继承GameSprite类
class Background(GameSprite):
def update(self):
# 1.调用父类的方法实现
super().update()
# 2.判断是否移出屏幕
if self.rect.y >= SCREEN_RECT.height:
# 如果移出屏幕,则将图像设置到屏幕的上方
self.rect.y = -self.rect.height
2.在 plane_main.py 中显示背景精灵
实现步骤:
1.在 __create_sprites 方法中,创建精灵和精灵组
2.在 __update_sprites 方法中,让精灵组调用 update() 和 draw() 方法
import pygame
from plane_sprites import *
"""飞机大战主游戏 """
class PlaneGame(object):
def __init__(self):
print("游戏初始化")
# 1.创建游戏窗口;SCREEN_RECT.size:获取矩形对象中的元组
self.screen = pygame.display.set_mode(SCREEN_RECT.size)
# 2.创建游戏时钟
self.clock = pygame.time.Clock()
# 3.调用私有方法
self.__create_sprites()
# 定义私有方法,创建精灵和精灵组
def __create_sprites(self):
# 创建背景精灵
bg1 = Background("./images/background.png")
bg2 = Background("./images/background.png")
# 将第二张图像的初始位置设置到屏幕的正上方
bg2.rect.y = -bg2.rect.height
# 创建背景精灵组
self.back_ground = pygame.sprite.Group(bg1,bg2)
def start_game(self):
print("游戏开始...")
# 游戏循环
while True:
# 1.设置刷新帧率
self.clock.tick(FRAME_PRE_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):
pass
def __update_sprites(self):
# 精灵组调用 update 和 draw 方法
self.back_ground.update()
self.back_ground.draw(self.screen)
@staticmethod # 静态方法
def __game__over():
print("游戏结束")
pygame.quit()
exit()
if __name__ == '__main__':
# 创建游戏对象
game = PlaneGame()
# 启动游戏
game.start_game()
3.利用初始化方法,简化背景精灵创建
思考:
● 在主程序中,创建的两个背景精灵,传入了相同的图像文件路径
● 创建第二个背景精灵时,在主程序中,设置背景精灵的图像位置
改进:精灵的初始位置的设置,应该由主程序负责
● 根据面向对象设计原则,应该将对象的职责,封装到类的代码内部
● 尽量简化程序调用一方的代码调用
初始化方法:
● 直接指定背景图片
● is_alt 判断是否为另一张图像
False --- 表示第一张图像,需要与屏幕重合
True --- 表示另一张图像,在屏幕的正上方
在 plane_sprites.py 中实现 Background 的初始化方法
import pygame
# 定义屏幕大小的常量
SCREEN_RECT = pygame.Rect(0,0,350,573)
# 定义刷新帧率的常量
FRAME_PRE_SEC = 60
"""飞机大战游戏精灵"""
# 继承系统自带的pygame.sprite.Sprite类
class GameSprite(pygame.sprite.Sprite):
# 1.初始化方法:定义属性
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
# 2.定义方法
def update(self):
# 在屏幕的垂直方向上向下移动
self.rect.y += self.speed
"""背景精灵"""
# 继承GameSprite类
class Background(GameSprite):
def __init__(self,is_alt=False):
# 1.调用父类的方法实现精灵的创建
super().__init__("./images/background.png")
# 2.判断是否交替图像,如果是,需要设置初始位置
if is_alt:
self.rect.y = -self.rect.height
def update(self):
# 1.调用父类的方法实现
super().update()
# 2.判断是否移出屏幕
if self.rect.y >= SCREEN_RECT.height:
# 如果移出屏幕,则将图像设置到屏幕的上方
self.rect.y = -self.rect.height
简化 plane_mian.py 文件内容
import pygame
from plane_sprites import *
"""飞机大战主游戏 """
class PlaneGame(object):
def __init__(self):
print("游戏初始化")
# 1.创建游戏窗口;SCREEN_RECT.size:获取矩形对象中的元组
self.screen = pygame.display.set_mode(SCREEN_RECT.size)
# 2.创建游戏时钟
self.clock = pygame.time.Clock()
# 3.调用私有方法
self.__create_sprites()
# 定义私有方法,创建精灵和精灵组
def __create_sprites(self):
# 创建背景精灵
bg1 = Background()
bg2 = Background(is_alt=True)
# 创建背景精灵组
self.back_group = pygame.sprite.Group(bg1,bg2)
def start_game(self):
print("游戏开始...")
# 游戏循环
while True:
# 1.设置刷新帧率
self.clock.tick(FRAME_PRE_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):
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 类
01.使用定时器添加敌机
根据飞机大战游戏,观察敌机规律:
1.游戏启动后,每隔1秒会出现一架敌机
2.每架敌机向屏幕下方飞行,飞行速度各不相同
3.每架飞机出现的水平位置也不尽相同
4.当敌机从屏幕下方飞出,不会再飞回到屏幕中
1.定时器
● 在 pygame 中可以使用 pygame.time.set_timer() 来添加 定时器
● 所谓定时器,就是每隔一段时间,去执行一些动作
set_timer(eventid,milliseconds)
set_timer 可以创建一个事件,可以在游戏循环的事件监听方法中捕获到该事件
eventid:事件代号,需要基于常量 pygame.USEREVENT 来指定;USEREVENT是一个整数,再增加的事件可以使用USEREVENT+1指定
milliseconds:事件触发间隔的毫秒值
定时器事件的监听
● 通过 pygame.event.get() 可以获取当前时刻所有的事件列表
● 遍历列表并且判断 event.type 是否等于 eventid,如果相等,表示定时器事件发生
2.定义并监听创建敌机事件
定时器的使用:
1.定义定时器常量:eventid
2.在初始化方法中,调用 set_timer 方法 设置定时器事件
3.在游戏循环中,监听定时器事件
02.设计 Enemy 类
● 初始化方法
指定 敌机图片
指定 随机敌机的 初始位置 和 初始速度
● 重写 update() 方法
判断是否飞出屏幕,若是,则从精灵组删
1.敌机类的准备
import pygame
# 定义屏幕大小的常量
SCREEN_RECT = pygame.Rect(0,0,346,567)
# 定义刷新帧率的常量
FRAME_PRE_SEC = 60
# 定义创建敌机的定时器常量
CREATE_ENEMY_EVENT = pygame.USEREVENT
"""飞机大战游戏精灵"""
# 继承系统自带的pygame.sprite.Sprite类
class GameSprite(pygame.sprite.Sprite):
# 1.初始化方法:定义属性
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
# 2.定义方法
def update(self):
# 在屏幕的垂直方向上向下移动
self.rect.y += self.speed
"""背景精灵"""
# 继承GameSprite类
class Background(GameSprite):
def __init__(self,is_alt=False):
# 1.调用父类的方法实现精灵的创建
super().__init__("./images/background.png")
# 2.判断是否交替图像,如果是,需要设置初始位置
if is_alt:
self.rect.y = -self.rect.height
def update(self):
# 1.调用父类的方法实现
super().update()
# 2.判断是否移出屏幕
if self.rect.y >= SCREEN_RECT.height:
# 如果移出屏幕,则将图像设置到屏幕的上方
self.rect.y = -self.rect.height
"""敌机精灵"""
class Enemy(GameSprite):
def __init__(self):
# 1.调用父类方法,创建敌机精灵
super().__init__("./images/enemy1.png")
# 2.指定敌机的初始随机速度
# 3.指定敌机的初始位置
def update(self):
# 1.调用父类方法
super().update()
# 2.判断是否飞出屏幕,若是,则需从精灵组删除敌机
if self.rect.y >= SCREEN_RECT.height:
print("飞出屏幕,需从精灵组删除...")
2.创建敌机
演练步骤:
1.在 __create_sprites,添加敌机精灵组
● 敌机是定时被创建的,因此在初始化方法中,不需要创建敌机
2.在 __event_handler,创建敌机,并添加到精灵组
● 调用精灵组的 add 方法 可以向精灵组添加精灵
3.在 __update_sprites,让敌机精灵组调用 update 和 draw 方法
import pygame
from plane_sprites import *
"""飞机大战主游戏 """
class PlaneGame(object):
def __init__(self):
print("游戏初始化")
# 1.创建游戏窗口;SCREEN_RECT.size:获取矩形对象中的元组
self.screen = pygame.display.set_mode(SCREEN_RECT.size)
# 2.创建游戏时钟
self.clock = pygame.time.Clock()
# 3.调用私有方法,创建精灵和精灵组
self.__create_sprites()
# 设置定时器事件,创建敌机;1000ms=1s
pygame.time.set_timer(CREATE_ENEMY_EVENT,1000)
# 定义私有方法,创建精灵和精灵组
def __create_sprites(self):
# 创建背景精灵
bg1 = Background()
bg2 = Background(is_alt=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_PRE_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 = Enemy()
# 将敌机精灵添加到敌机精灵组中
self.enemy_group.add(enemy)
def __check_collide(self):
pass
def __update_sprites(self):
self.back_group.update()
self.back_group.draw(self.screen)
self.enemy_group.update()
self.enemy_group.draw(self.screen)
@staticmethod # 静态方法
def __game__over():
print("游戏结束")
pygame.quit()
exit()
if __name__ == '__main__':
# 创建游戏对象
game = PlaneGame()
# 启动游戏
game.start_game()
3.随机敌机位置和速度
(1).导入模块
● 在导入模块时,建议按照以下顺序导入
1.官方标准模块
2.第三方模块
3.应用程序模块
注意:pygame 属于第三方模块,random 属于官方标准模块
(2).随机位置
使用 pygame.Rect 提供的 bottom 属性,在指定敌机初始位置时,会比较方便
● bottom = y + height
● y = bottom - height
即 y = -height 等价于 bottom = 0
(3).代码实习
修改初始化方法,随机敌机出现速度和位置
import random
import pygame
# 定义屏幕大小的常量
SCREEN_RECT = pygame.Rect(0,0,346,567)
# 定义刷新帧率的常量
FRAME_PRE_SEC = 60
# 定义创建敌机的定时器常量
CREATE_ENEMY_EVENT = pygame.USEREVENT
"""飞机大战游戏精灵"""
# 继承系统自带的pygame.sprite.Sprite类
class GameSprite(pygame.sprite.Sprite):
# 1.初始化方法:定义属性
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
# 2.定义方法
def update(self):
# 在屏幕的垂直方向上向下移动
self.rect.y += self.speed
"""背景精灵"""
# 继承GameSprite类
class Background(GameSprite):
def __init__(self,is_alt=False):
# 1.调用父类的方法实现精灵的创建
super().__init__("./images/background.png")
# 2.判断是否交替图像,如果是,需要设置初始位置
if is_alt:
self.rect.y = -self.rect.height
def update(self):
# 1.调用父类的方法实现
super().update()
# 2.判断是否移出屏幕
if self.rect.y >= SCREEN_RECT.height:
# 如果移出屏幕,则将图像设置到屏幕的上方
self.rect.y = -self.rect.height
"""敌机精灵"""
class Enemy(GameSprite):
def __init__(self):
# 1.调用父类方法,创建敌机精灵
super().__init__("./images/enemy1.png")
# 2.指定敌机的初始随机速度 1-3
self.speed = random.randint(1,3)
# 3.指定敌机的初始位置
# 垂直方向 y
self.rect.bottom = 0
# 水平方向 x
max_x = SCREEN_RECT.width - self.rect.width
self.rect.x = random.randint(0,max_x)
def update(self):
# 1.调用父类方法
super().update()
# 2.判断是否飞出屏幕,若是,则需从精灵组删除敌机
if self.rect.y >= SCREEN_RECT.height:
print("飞出屏幕,需从精灵组删除...")
4.移出屏幕销毁敌机
● 敌机移出屏幕之后,如果没有撞到英雄,敌机的历史使命已经终结
● 需要从敌机组删除,否则会造成内存浪费
检测敌机被销毁
● __del__ 内置方法会在对象被销毁前调用,在开发中,可以用于判断对象是否被销毁
判断敌机是否飞出屏幕,若是,则调用 kill() 方法从所有组中删除
import random
import pygame
# 定义屏幕大小的常量
SCREEN_RECT = pygame.Rect(0,0,346,567)
# 定义刷新帧率的常量
FRAME_PRE_SEC = 60
# 定义创建敌机的定时器常量
CREATE_ENEMY_EVENT = pygame.USEREVENT
"""飞机大战游戏精灵"""
# 继承系统自带的pygame.sprite.Sprite类
class GameSprite(pygame.sprite.Sprite):
# 1.初始化方法:定义属性
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
# 2.定义方法
def update(self):
# 在屏幕的垂直方向上向下移动
self.rect.y += self.speed
"""背景精灵"""
# 继承GameSprite类
class Background(GameSprite):
def __init__(self,is_alt=False):
# 1.调用父类的方法实现精灵的创建
super().__init__("./images/background.png")
# 2.判断是否交替图像,如果是,需要设置初始位置
if is_alt:
self.rect.y = -self.rect.height
def update(self):
# 1.调用父类的方法实现
super().update()
# 2.判断是否移出屏幕
if self.rect.y >= SCREEN_RECT.height:
# 如果移出屏幕,则将图像设置到屏幕的上方
self.rect.y = -self.rect.height
"""敌机精灵"""
class Enemy(GameSprite):
def __init__(self):
# 1.调用父类方法,创建敌机精灵
super().__init__("./images/enemy1.png")
# 2.指定敌机的初始随机速度
self.speed = random.randint(1,3)
# 3.指定敌机的初始位置
# 垂直方向 y
self.rect.bottom = 0
# 水平方向 x
max_x = SCREEN_RECT.width - self.rect.width
self.rect.x = random.randint(0,max_x)
def update(self):
# 1.调用父类方法
super().update()
# 2.判断是否飞出屏幕,若是,则需从精灵组删除敌机
if self.rect.y >= SCREEN_RECT.height:
print("飞出屏幕,需从精灵组删除...")
# 将精灵从所有的精灵组中移出,精灵便会被自动销毁
self.kill()
def __del__(self):
print("敌机挂了 %s" % self.rect)
四、英雄登场
目标
● 设计英雄和子弹类
● 使用 pygame.key.get_pressed() 移动英雄
● 发射子弹
01.设计英雄和子弹类
英雄需求:
1.游戏启动后,英雄出现在屏幕的水平中间位置,距离屏幕底部120像素
2.英雄每隔0.5秒发射一次子弹,每次连发三枚子弹
3.英雄默认不会移动,需要通过左/右方向键,控制英雄在水平方向移动
子弹需求:
1.子弹从英雄的正上方发射沿直线向上飞行
2.飞出屏幕后,需要从精灵组中删除
Hero 英雄
● 初始化方法
指定英雄图片
初始速度 = 0 --- 英雄默认静止不动
定义 bullets 子弹精灵组 保存子弹精灵
● 重写 update() 方法
英雄需要水平移动
并且需要保证不能移出屏幕
● 增加 bullers 属性,记录所有子弹精灵
● 增加 fire 方法,用于发射子弹
Bullet 子弹
● 初始化方法
指定子弹图片
初始速度 = -2 ---- 子弹需要向上方飞行
● 重写 update() 方法
判断是否飞出屏幕,如果是,从精灵组删除
02.创建英雄
1.准备英雄类
● 在 plane_sprites 新建 Hero类
● 重写初始化方法,直接指定图片名称,并且将初始速度设置为0
● 设置英雄的初始位置
centerx = x + 0.5 * width
centery = y + 0.5 * height
bottom = y + height
import random
import pygame
# 定义屏幕大小的常量
SCREEN_RECT = pygame.Rect(0,0,346,567)
# 定义刷新帧率的常量
FRAME_PRE_SEC = 60
# 定义创建敌机的定时器常量
CREATE_ENEMY_EVENT = pygame.USEREVENT
"""飞机大战游戏精灵"""
# 继承系统自带的pygame.sprite.Sprite类
class GameSprite(pygame.sprite.Sprite):
# 1.初始化方法:定义属性
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
# 2.定义方法
def update(self):
# 在屏幕的垂直方向上向下移动
self.rect.y += self.speed
"""背景精灵"""
# 继承GameSprite类
class Background(GameSprite):
def __init__(self,is_alt=False):
# 1.调用父类的方法实现精灵的创建
super().__init__("./images/background.png")
# 2.判断是否交替图像,如果是,需要设置初始位置
if is_alt:
self.rect.y = -self.rect.height
def update(self):
# 1.调用父类的方法实现
super().update()
# 2.判断是否移出屏幕
if self.rect.y >= SCREEN_RECT.height:
# 如果移出屏幕,则将图像设置到屏幕的上方
self.rect.y = -self.rect.height
"""敌机精灵"""
class Enemy(GameSprite):
def __init__(self):
# 1.调用父类方法,创建敌机精灵
super().__init__("./images/enemy1.png")
# 2.指定敌机的初始随机速度
self.speed = random.randint(1,3)
# 3.指定敌机的初始位置
# 垂直方向 y
self.rect.bottom = 0
# 水平方向 x
max_x = SCREEN_RECT.width - self.rect.width
self.rect.x = random.randint(0,max_x)
def update(self):
# 1.调用父类方法
super().update()
# 2.判断是否飞出屏幕,若是,则需从精灵组删除敌机
if self.rect.y >= SCREEN_RECT.height:
# print("飞出屏幕,需从精灵组删除...")
# 将精灵从所有的精灵组中移出,精灵便会被自动销毁
self.kill()
def __del__(self):
# print("敌机挂了 %s" % self.rect)
pass
"""英雄精灵"""
class Hero(GameSprite):
def __init__(self):
# 1.调用父类方法,设置image和speed
super().__init__("./images/me1.png",0)
# 2.设置英雄的初始位置
# 水平位置 x
self.rect.centerx = SCREEN_RECT.centerx
# 垂直位置 y
self.rect.bottom = SCREEN_RECT.bottom -120
2.绘制英雄
操作步骤:
1.在 __create_sprites 方法中,添加 英雄精灵 和 英雄精灵组
● 后续要针对英雄做 碰撞检测 以及 发射子弹,所以 英雄 需要单独定义成属性
2.在 __update_sprites 方法中,让英雄精灵组调用 update 和 draw 方法
import pygame
from plane_sprites import *
"""飞机大战主游戏 """
class PlaneGame(object):
def __init__(self):
print("游戏初始化")
# 1.创建游戏窗口;SCREEN_RECT.size:获取矩形对象中的元组
self.screen = pygame.display.set_mode(SCREEN_RECT.size)
# 2.创建游戏时钟
self.clock = pygame.time.Clock()
# 3.调用私有方法,创建精灵和精灵组
self.__create_sprites()
# 设置定时器事件,创建敌机;1000ms=1s
pygame.time.set_timer(CREATE_ENEMY_EVENT,1000)
# 定义私有方法,创建精灵和精灵组
def __create_sprites(self):
# 创建背景精灵
bg1 = Background()
bg2 = Background(is_alt=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_PRE_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 = Enemy()
# 将敌机精灵添加到敌机精灵组中
self.enemy_group.add(enemy)
def __check_collide(self):
pass
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)
@staticmethod # 静态方法
def __game__over():
print("游戏结束")
pygame.quit()
exit()
if __name__ == '__main__':
# 创建游戏对象
game = PlaneGame()
# 启动游戏
game.start_game()
03.移动英雄位置
在 pygame 中,针对 键盘按键的捕获,有两种方式
第一种方式:判断 event.type == pygame.KEYDOWN
elif event.type == pygame.KEYDOWN and event.key == pygame.K_RIGHT:
print("向右移动...")
第二种方式:首先使用 pygame.key.get_pressed() 返回所有按键元组,然后通过键盘常量,判断元组中某一个键是否被按下,如果被按下,对应数值为1
# 使用键盘提供的方法获取按键
keys_pressed = pygame.key.get_pressed()
# 判断元组中对应的按键索引值 1
if keys_pressed[pygame.K_RIGHT]:
print("向右移动...")
区别:
第一种方式,用户必须要抬起按键才能算一次按键事件,操作灵活性会大打折扣
第二种方式,用户可以按住方向键不放,就能够实现持续向某一个方向移动了,操作灵活性更好
import pygame
from plane_sprites import *
"""飞机大战主游戏 """
class PlaneGame(object):
def __init__(self):
print("游戏初始化")
# 1.创建游戏窗口;SCREEN_RECT.size:获取矩形对象中的元组
self.screen = pygame.display.set_mode(SCREEN_RECT.size)
# 2.创建游戏时钟
self.clock = pygame.time.Clock()
# 3.调用私有方法,创建精灵和精灵组
self.__create_sprites()
# 设置定时器事件,创建敌机;1000ms=1s
pygame.time.set_timer(CREATE_ENEMY_EVENT,1000)
# 定义私有方法,创建精灵和精灵组
def __create_sprites(self):
# 创建背景精灵
bg1 = Background()
bg2 = Background(is_alt=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_PRE_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 = Enemy()
# 将敌机精灵添加到敌机精灵组中
self.enemy_group.add(enemy)
# 捕获键盘按键
# elif event.type == pygame.KEYDOWN and event.key == pygame.K_RIGHT:
# print("向右移动...")
# 使用键盘提供的方法获取按键
keys_pressed = pygame.key.get_pressed()
# 判断元组中对应的按键索引值 1
if keys_pressed[pygame.K_RIGHT]:
print("向右移动...")
def __check_collide(self):
pass
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)
@staticmethod # 静态方法
def __game__over():
print("游戏结束")
pygame.quit()
exit()
if __name__ == '__main__':
# 创建游戏对象
game = PlaneGame()
# 启动游戏
game.start_game()
1.移动英雄位置
演练步骤:
1.在 Hero 类中重写 update 方法
● 用速度 speed 和 英雄 rect.x 进行叠加
● 不需要调用父类方法 --- 父类方法只是实现了单纯的垂直运动
2.在__event_handler 方法中根据 左右方向键 设置英雄的 速度
● 向右 speed = 2
● 向左 speed =-2
● 其他 speed = 0
plane_sprites.py 文件内容:
import random
import pygame
# 定义屏幕大小的常量
SCREEN_RECT = pygame.Rect(0,0,346,567)
# 定义刷新帧率的常量
FRAME_PRE_SEC = 60
# 定义创建敌机的定时器常量
CREATE_ENEMY_EVENT = pygame.USEREVENT
"""飞机大战游戏精灵"""
# 继承系统自带的pygame.sprite.Sprite类
class GameSprite(pygame.sprite.Sprite):
# 1.初始化方法:定义属性
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
# 2.定义方法
def update(self):
# 在屏幕的垂直方向上向下移动
self.rect.y += self.speed
"""背景精灵"""
# 继承GameSprite类
class Background(GameSprite):
def __init__(self,is_alt=False):
# 1.调用父类的方法实现精灵的创建
super().__init__("./images/background.png")
# 2.判断是否交替图像,如果是,需要设置初始位置
if is_alt:
self.rect.y = -self.rect.height
def update(self):
# 1.调用父类的方法实现
super().update()
# 2.判断是否移出屏幕
if self.rect.y >= SCREEN_RECT.height:
# 如果移出屏幕,则将图像设置到屏幕的上方
self.rect.y = -self.rect.height
"""敌机精灵"""
class Enemy(GameSprite):
def __init__(self):
# 1.调用父类方法,创建敌机精灵
super().__init__("./images/enemy1.png")
# 2.指定敌机的初始随机速度
self.speed = random.randint(1,3)
# 3.指定敌机的初始位置
# 垂直方向 y
self.rect.bottom = 0
# 水平方向 x
max_x = SCREEN_RECT.width - self.rect.width
self.rect.x = random.randint(0,max_x)
def update(self):
# 1.调用父类方法
super().update()
# 2.判断是否飞出屏幕,若是,则需从精灵组删除敌机
if self.rect.y >= SCREEN_RECT.height:
# print("飞出屏幕,需从精灵组删除...")
# 将精灵从所有的精灵组中移出,精灵便会被自动销毁
self.kill()
def __del__(self):
# print("敌机挂了 %s" % self.rect)
pass
"""英雄精灵"""
class Hero(GameSprite):
def __init__(self):
# 1.调用父类方法,设置image和speed
super().__init__("./images/me1.png",0)
# 2.设置英雄的初始位置
# 水平位置 x
self.rect.centerx = SCREEN_RECT.centerx
# 垂直位置 y
self.rect.bottom = SCREEN_RECT.bottom -120
def update(self):
# 水平方向移动
self.rect.x += self.speed
plane_mian.py 文件内容:
import pygame
from plane_sprites import *
"""飞机大战主游戏 """
class PlaneGame(object):
def __init__(self):
print("游戏初始化")
# 1.创建游戏窗口;SCREEN_RECT.size:获取矩形对象中的元组
self.screen = pygame.display.set_mode(SCREEN_RECT.size)
# 2.创建游戏时钟
self.clock = pygame.time.Clock()
# 3.调用私有方法,创建精灵和精灵组
self.__create_sprites()
# 设置定时器事件,创建敌机;1000ms=1s
pygame.time.set_timer(CREATE_ENEMY_EVENT,1000)
# 定义私有方法,创建精灵和精灵组
def __create_sprites(self):
# 创建背景精灵
bg1 = Background()
bg2 = Background(is_alt=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_PRE_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 = Enemy()
# 将敌机精灵添加到敌机精灵组中
self.enemy_group.add(enemy)
# 捕获键盘按键
# elif event.type == pygame.KEYDOWN and event.key == pygame.K_RIGHT:
# print("向右移动...")
# 使用键盘提供的方法获取按键
keys_pressed = pygame.key.get_pressed()
# 判断元组中对应的按键索引值 1
# 右移
if keys_pressed[pygame.K_RIGHT]:
self.hero.speed = 2
# 左移
elif keys_pressed[pygame.K_LEFT]:
self.hero.speed = -2
else:
self.hero.speed = 0
def __check_collide(self):
pass
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)
@staticmethod # 静态方法
def __game__over():
print("游戏结束")
pygame.quit()
exit()
if __name__ == '__main__':
# 创建游戏对象
game = PlaneGame()
# 启动游戏
game.start_game()
2.控制英雄运动边界
● 在 Hero 类的 update() 方法判断英雄是否超出屏幕边界
● right = x + width 利用 right 属性可以非常容易的针对右侧设置精灵位置
import random
import pygame
# 定义屏幕大小的常量
SCREEN_RECT = pygame.Rect(0,0,346,567)
# 定义刷新帧率的常量
FRAME_PRE_SEC = 60
# 定义创建敌机的定时器常量
CREATE_ENEMY_EVENT = pygame.USEREVENT
"""飞机大战游戏精灵"""
# 继承系统自带的pygame.sprite.Sprite类
class GameSprite(pygame.sprite.Sprite):
# 1.初始化方法:定义属性
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
# 2.定义方法
def update(self):
# 在屏幕的垂直方向上向下移动
self.rect.y += self.speed
"""背景精灵"""
# 继承GameSprite类
class Background(GameSprite):
def __init__(self,is_alt=False):
# 1.调用父类的方法实现精灵的创建
super().__init__("./images/background.png")
# 2.判断是否交替图像,如果是,需要设置初始位置
if is_alt:
self.rect.y = -self.rect.height
def update(self):
# 1.调用父类的方法实现
super().update()
# 2.判断是否移出屏幕
if self.rect.y >= SCREEN_RECT.height:
# 如果移出屏幕,则将图像设置到屏幕的上方
self.rect.y = -self.rect.height
"""敌机精灵"""
class Enemy(GameSprite):
def __init__(self):
# 1.调用父类方法,创建敌机精灵
super().__init__("./images/enemy1.png")
# 2.指定敌机的初始随机速度
self.speed = random.randint(1,3)
# 3.指定敌机的初始位置
# 垂直方向 y
self.rect.bottom = 0
# 水平方向 x
max_x = SCREEN_RECT.width - self.rect.width
self.rect.x = random.randint(0,max_x)
def update(self):
# 1.调用父类方法
super().update()
# 2.判断是否飞出屏幕,若是,则需从精灵组删除敌机
if self.rect.y >= SCREEN_RECT.height:
# print("飞出屏幕,需从精灵组删除...")
# 将精灵从所有的精灵组中移出,精灵便会被自动销毁
self.kill()
def __del__(self):
# print("敌机挂了 %s" % self.rect)
pass
"""英雄精灵"""
class Hero(GameSprite):
def __init__(self):
# 1.调用父类方法,设置image和speed
super().__init__("./images/me1.png",0)
# 2.设置英雄的初始位置
# 水平位置 x
self.rect.centerx = SCREEN_RECT.centerx
# 垂直位置 y
self.rect.bottom = SCREEN_RECT.bottom -120
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
04.发射子弹
1.添加发射子弹事件
定时器的使用,套路非常固定
1.定义定时器常量:eventid
2.在初始化方法中,调用 set_timer 方法 设置定时器事件
3.在游戏循环中,监听定时器事件
先在 Hero类 中 定义 fire 方法,并定义定时器常量
import random
import pygame
# 定义屏幕大小的常量
SCREEN_RECT = pygame.Rect(0,0,346,567)
# 定义刷新帧率的常量
FRAME_PRE_SEC = 60
# 定义创建敌机的定时器常量
CREATE_ENEMY_EVENT = pygame.USEREVENT
# 定义英雄发射子弹的定时器常量
HERO_FIRE_EVENT = pygame.USEREVENT + 1
"""飞机大战游戏精灵"""
# 继承系统自带的pygame.sprite.Sprite类
class GameSprite(pygame.sprite.Sprite):
# 1.初始化方法:定义属性
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
# 2.定义方法
def update(self):
# 在屏幕的垂直方向上向下移动
self.rect.y += self.speed
"""背景精灵"""
# 继承GameSprite类
class Background(GameSprite):
def __init__(self,is_alt=False):
# 1.调用父类的方法实现精灵的创建
super().__init__("./images/background.png")
# 2.判断是否交替图像,如果是,需要设置初始位置
if is_alt:
self.rect.y = -self.rect.height
def update(self):
# 1.调用父类的方法实现
super().update()
# 2.判断是否移出屏幕
if self.rect.y >= SCREEN_RECT.height:
# 如果移出屏幕,则将图像设置到屏幕的上方
self.rect.y = -self.rect.height
"""敌机精灵"""
class Enemy(GameSprite):
def __init__(self):
# 1.调用父类方法,创建敌机精灵
super().__init__("./images/enemy1.png")
# 2.指定敌机的初始随机速度
self.speed = random.randint(1,3)
# 3.指定敌机的初始位置
# 垂直方向 y
self.rect.bottom = 0
# 水平方向 x
max_x = SCREEN_RECT.width - self.rect.width
self.rect.x = random.randint(0,max_x)
def update(self):
# 1.调用父类方法
super().update()
# 2.判断是否飞出屏幕,若是,则需从精灵组删除敌机
if self.rect.y >= SCREEN_RECT.height:
# print("飞出屏幕,需从精灵组删除...")
# 将精灵从所有的精灵组中移出,精灵便会被自动销毁
self.kill()
def __del__(self):
# print("敌机挂了 %s" % self.rect)
pass
"""英雄精灵"""
class Hero(GameSprite):
def __init__(self):
# 1.调用父类方法,设置image和speed
super().__init__("./images/me1.png",0)
# 2.设置英雄的初始位置
# 水平位置 x
self.rect.centerx = SCREEN_RECT.centerx
# 垂直位置 y
self.rect.bottom = SCREEN_RECT.bottom -120
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("发射子弹...")
在 plane_mian.py 文件中,设置 并 监听 定时器事件
import pygame
from plane_sprites import *
"""飞机大战主游戏 """
class PlaneGame(object):
def __init__(self):
print("游戏初始化")
# 1.创建游戏窗口;SCREEN_RECT.size:获取矩形对象中的元组
self.screen = pygame.display.set_mode(SCREEN_RECT.size)
# 2.创建游戏时钟
self.clock = pygame.time.Clock()
# 3.调用私有方法,创建精灵和精灵组
self.__create_sprites()
# 设置定时器事件
# 创建敌机;1000ms=1s
pygame.time.set_timer(CREATE_ENEMY_EVENT,1000)
# 创建英雄子弹;500ms=0.5s
pygame.time.set_timer(HERO_FIRE_EVENT,500)
# 定义私有方法,创建精灵和精灵组
def __create_sprites(self):
# 创建背景精灵
bg1 = Background()
bg2 = Background(is_alt=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_PRE_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 = Enemy()
# 将敌机精灵添加到敌机精灵组中
self.enemy_group.add(enemy)
# 定时发射子弹
# 判断事件类型是否为“英雄发射子弹的定时器常量”
elif event.type == HERO_FIRE_EVENT:
self.hero.fire()
# 使用键盘提供的方法获取按键
keys_pressed = pygame.key.get_pressed()
# 判断元组中对应的按键索引值 1
# 右移
if keys_pressed[pygame.K_RIGHT]:
self.hero.speed = 2
# 左移
elif keys_pressed[pygame.K_LEFT]:
self.hero.speed = -2
else:
self.hero.speed = 0
def __check_collide(self):
pass
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)
@staticmethod # 静态方法
def __game__over():
print("游戏结束")
pygame.quit()
exit()
if __name__ == '__main__':
# 创建游戏对象
game = PlaneGame()
# 启动游戏
game.start_game()
2.定义子弹类
子弹需求:
1.子弹从英雄的正上方发射沿直线向上飞行
2.飞出屏幕后,需要从精灵组中删除
定义子弹类
● 在 plane_sprites.py文件中 新建 Bullet类 继承自 GameSprite类
● 重写 初始化方法,直接指定图片名称,并且设置初始速度为 -2(因为子弹需要向上方飞行)
● 重写 update() 方法,判断子弹是否飞出屏幕,若是,则从精灵组删除
import random
import pygame
# 定义屏幕大小的常量
SCREEN_RECT = pygame.Rect(0,0,346,567)
# 定义刷新帧率的常量
FRAME_PRE_SEC = 60
# 定义创建敌机的定时器常量
CREATE_ENEMY_EVENT = pygame.USEREVENT
# 定义英雄发射子弹的定时器常量
HERO_FIRE_EVENT = pygame.USEREVENT + 1
"""飞机大战游戏精灵"""
# 继承系统自带的pygame.sprite.Sprite类
class GameSprite(pygame.sprite.Sprite):
# 1.初始化方法:定义属性
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
# 2.定义方法
def update(self):
# 在屏幕的垂直方向上移动
self.rect.y += self.speed
"""背景精灵"""
# 继承GameSprite类
class Background(GameSprite):
def __init__(self,is_alt=False):
# 1.调用父类的方法实现精灵的创建
super().__init__("./images/background.png")
# 2.判断是否交替图像,如果是,需要设置初始位置
if is_alt:
self.rect.y = -self.rect.height
def update(self):
# 1.调用父类的方法,让背景沿垂直方向向下移动
super().update()
# 2.判断是否移出屏幕
if self.rect.y >= SCREEN_RECT.height:
# 如果移出屏幕,则将图像设置到屏幕的上方
self.rect.y = -self.rect.height
"""敌机精灵"""
class Enemy(GameSprite):
def __init__(self):
# 1.调用父类方法,创建敌机精灵
super().__init__("./images/enemy1.png")
# 2.指定敌机的初始随机速度
self.speed = random.randint(1,3)
# 3.指定敌机的初始位置
# 垂直方向 y
self.rect.bottom = 0
# 水平方向 x
max_x = SCREEN_RECT.width - self.rect.width
self.rect.x = random.randint(0,max_x)
def update(self):
# 1.调用父类方法,让敌机沿垂直方向向下移动
super().update()
# 2.判断是否飞出屏幕,若是,则需从精灵组删除敌机
if self.rect.y >= SCREEN_RECT.height:
# print("飞出屏幕,敌机精灵需从精灵组删除...")
# 将精灵从所有的精灵组中移出,精灵便会被自动销毁
self.kill()
def __del__(self):
# print("敌机挂了 %s" % self.rect)
pass
"""英雄精灵"""
class Hero(GameSprite):
def __init__(self):
# 1.调用父类方法,设置image和speed
super().__init__("./images/me1.png",0)
# 2.设置英雄的初始位置
# 水平位置 x
self.rect.centerx = SCREEN_RECT.centerx
# 垂直位置 y
self.rect.bottom = SCREEN_RECT.bottom -120
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("发射子弹...")
"""子弹精灵"""
class Bullet(GameSprite):
def __init__(self):
# 调用父类方法,设置子弹图像和初始速度
super().__init__("./images/bullet1.png",-2)
def update(self):
# 1.调用父类方法,让子弹沿垂直方向向上飞行(因为速度为负值)
super().update()
# 2.判断子弹是否飞出屏幕
if self.rect.bottom < 0:
# print("飞出屏幕,子弹精灵需从精灵组删除...")
# 将精灵从所有的精灵组中移出,精灵便会被自动销毁
self.kill()
def __del__(self):
print("子弹被销毁...")
3.发射子弹
演练步骤:
1. 在 Hero 的 初始化方法 中 创建 子弹精灵组 属性
2.修改 plane_main.py 的 __update_sprites 方法,让子弹精灵组调用 update 和 draw 方法
3.实现 fire() 方法
● 创建子弹精灵
● 设置初始位置 --- 在英雄的正上方
● 将子弹添加到精灵组
plane_sprites.py文件内容:
import random
import pygame
# 定义屏幕大小的常量
SCREEN_RECT = pygame.Rect(0,0,346,567)
# 定义刷新帧率的常量
FRAME_PRE_SEC = 60
# 定义创建敌机的定时器常量
CREATE_ENEMY_EVENT = pygame.USEREVENT
# 定义英雄发射子弹的定时器常量
HERO_FIRE_EVENT = pygame.USEREVENT + 1
"""飞机大战游戏精灵"""
# 继承系统自带的pygame.sprite.Sprite类
class GameSprite(pygame.sprite.Sprite):
# 1.初始化方法:定义属性
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
# 2.定义方法
def update(self):
# 在屏幕的垂直方向上移动
self.rect.y += self.speed
"""背景精灵"""
# 继承GameSprite类
class Background(GameSprite):
def __init__(self,is_alt=False):
# 1.调用父类的方法实现精灵的创建
super().__init__("./images/background.png")
# 2.判断是否交替图像,如果是,需要设置初始位置
if is_alt:
self.rect.y = -self.rect.height
def update(self):
# 1.调用父类的方法,让背景沿垂直方向向下移动
super().update()
# 2.判断是否移出屏幕
if self.rect.y >= SCREEN_RECT.height:
# 如果移出屏幕,则将图像设置到屏幕的上方
self.rect.y = -self.rect.height
"""敌机精灵"""
class Enemy(GameSprite):
def __init__(self):
# 1.调用父类方法,创建敌机精灵
super().__init__("./images/enemy1.png")
# 2.指定敌机的初始随机速度
self.speed = random.randint(1,3)
# 3.指定敌机的初始位置
# 垂直方向 y
self.rect.bottom = 0
# 水平方向 x
max_x = SCREEN_RECT.width - self.rect.width
self.rect.x = random.randint(0,max_x)
def update(self):
# 1.调用父类方法,让敌机沿垂直方向向下移动
super().update()
# 2.判断是否飞出屏幕,若是,则需从精灵组删除敌机
if self.rect.y >= SCREEN_RECT.height:
# print("飞出屏幕,敌机精灵需从精灵组删除...")
# 将精灵从所有的精灵组中移出,精灵便会被自动销毁
self.kill()
def __del__(self):
# print("敌机挂了 %s" % self.rect)
pass
"""英雄精灵"""
class Hero(GameSprite):
def __init__(self):
# 1.调用父类方法,设置image和speed
super().__init__("./images/me1.png",0)
# 2.设置英雄的初始位置
# 水平位置 x
self.rect.centerx = SCREEN_RECT.centerx
# 垂直位置 y
self.rect.bottom = SCREEN_RECT.bottom -120
# 3.创建子弹的精灵组
self.bullets_group = 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("发射子弹...")
# 1.创建子弹精灵
bullet = Bullet()
# 2.设置精灵的初始位置
bullet.rect.bottom = self.rect.y - 20
bullet.rect.centerx = self.rect.centerx
# 3.将精灵添加到精灵组
self.bullets_group.add(bullet)
"""子弹精灵"""
class Bullet(GameSprite):
def __init__(self):
# 调用父类方法,设置子弹图像和初始速度
super().__init__("./images/bullet1.png",-2)
def update(self):
# 1.调用父类方法,让子弹沿垂直方向向上飞行(因为速度为负值)
super().update()
# 2.判断子弹是否飞出屏幕
if self.rect.bottom < 0:
# print("飞出屏幕,子弹精灵需从精灵组删除...")
# 将精灵从所有的精灵组中移出,精灵便会被自动销毁
self.kill()
def __del__(self):
print("子弹被销毁...")
pass
plane_main.py 文件内容:
import pygame
from plane_sprites import *
"""飞机大战主游戏 """
class PlaneGame(object):
def __init__(self):
print("游戏初始化")
# 1.创建游戏窗口;SCREEN_RECT.size:获取矩形对象中的元组
self.screen = pygame.display.set_mode(SCREEN_RECT.size)
# 2.创建游戏时钟
self.clock = pygame.time.Clock()
# 3.调用私有方法,创建精灵和精灵组
self.__create_sprites()
# 设置定时器事件
# 创建敌机;1000ms=1s
pygame.time.set_timer(CREATE_ENEMY_EVENT,1000)
# 创建英雄子弹;500ms=0.5s
pygame.time.set_timer(HERO_FIRE_EVENT,500)
# 定义私有方法,创建精灵和精灵组
def __create_sprites(self):
# 创建背景精灵
bg1 = Background()
bg2 = Background(is_alt=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_PRE_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 = Enemy()
# 将敌机精灵添加到敌机精灵组中
self.enemy_group.add(enemy)
# 定时发射子弹
# 判断事件类型是否为“英雄发射子弹的定时器常量”
elif event.type == HERO_FIRE_EVENT:
self.hero.fire()
# 使用键盘提供的方法获取按键
keys_pressed = pygame.key.get_pressed()
# 判断元组中对应的按键索引值 1
# 右移
if keys_pressed[pygame.K_RIGHT]:
self.hero.speed = 2
# 左移
elif keys_pressed[pygame.K_LEFT]:
self.hero.speed = -2
else:
self.hero.speed = 0
def __check_collide(self):
pass
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_group.update()
self.hero.bullets_group.draw(self.screen)
@staticmethod # 静态方法
def __game__over():
print("游戏结束")
pygame.quit()
exit()
if __name__ == '__main__':
# 创建游戏对象
game = PlaneGame()
# 启动游戏
game.start_game()
修改 fire() 方法,一次发射三枚子弹:
def fire(self):
print("发射子弹...")
for i in range(3):
# 1.创建子弹精灵
bullet = Bullet()
# 2.设置精灵的初始位置
bullet.rect.bottom = self.rect.y - 20*i
bullet.rect.centerx = self.rect.centerx
# 3.将精灵添加到精灵组
self.bullets.add(bullet)
五、碰撞检测
1.碰撞检测方法
pygame 提供了两个非常方便的方法可以实现碰撞检测
子弹撞毁敌机:
● pygame.sprite.groupcollide() 检测 两个精灵组中所有精灵是否发生碰撞
groupcollide(group1, group2, dokill1, dokill2, collided = None)
dokill1, dokill2 的数据类型均为 布尔型,且 group1 和 dokill1 的对应,group2 和 dokill2 的对应
如果将 dokill 设置为 True,则发生碰撞的精灵将被自动移除,即若 dokill1 为 True,则group1中的精灵与group2中的精灵发生碰撞,则group1中的精灵将被销毁
collided 参数是用于 计算碰撞的回调函数,如果没有指定,则每隔精灵必须有一个 rect 属性
敌机撞毁英雄:
● pygame.sprite.spritecollide() 判断 某个精灵和指定的精灵组中的精灵的碰撞
spritecollide(sprite, group, dokill, collided = None)
如果将 dokill 设置为 True,则指定精灵组中发生碰撞的精灵将被自动移除
collided 参数适用于计算碰撞的回调函数,如果没有指定,则每个精灵必须有一个 rect 属性
返回 精灵组 中跟 精灵 发生碰撞的 精灵列表
import pygame
from plane_sprites import *
"""飞机大战主游戏 """
class PlaneGame(object):
def __init__(self):
print("游戏初始化")
# 1.创建游戏窗口;SCREEN_RECT.size:获取矩形对象中的元组
self.screen = pygame.display.set_mode(SCREEN_RECT.size)
# 2.创建游戏时钟
self.clock = pygame.time.Clock()
# 3.调用私有方法,创建精灵和精灵组
self.__create_sprites()
# 设置定时器事件
# 创建敌机;1000ms=1s
pygame.time.set_timer(CREATE_ENEMY_EVENT,1000)
# 创建英雄子弹;500ms=0.5s
pygame.time.set_timer(HERO_FIRE_EVENT,500)
# 定义私有方法,创建精灵和精灵组
def __create_sprites(self):
# 创建背景精灵
bg1 = Background()
bg2 = Background(is_alt=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_PRE_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 = Enemy()
# 将敌机精灵添加到敌机精灵组中
self.enemy_group.add(enemy)
# 定时发射子弹
# 判断事件类型是否为“英雄发射子弹的定时器常量”
elif event.type == HERO_FIRE_EVENT:
self.hero.fire()
# 使用键盘提供的方法获取按键
keys_pressed = pygame.key.get_pressed()
# 判断元组中对应的按键索引值 1
# 右移
if keys_pressed[pygame.K_RIGHT]:
self.hero.speed = 2
# 左移
elif keys_pressed[pygame.K_LEFT]:
self.hero.speed = -2
else:
self.hero.speed = 0
def __check_collide(self):
# 1.子弹摧毁敌机
pygame.sprite.groupcollide(self.hero.bullets_group,self.enemy_group,True,True)
# 2.敌机撞毁英雄;返回结果为列表
enemies = pygame.sprite.spritecollide(self.hero,self.enemy_group,True)
# 判断列表是否有内容
if len(enemies) > 0:
# 1.英雄牺牲
self.hero.kill()
# 2.结束游戏
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_group.update()
self.hero.bullets_group.draw(self.screen)
@staticmethod # 静态方法
def __game__over():
print("游戏结束")
pygame.quit()
exit()
if __name__ == '__main__':
# 创建游戏对象
game = PlaneGame()
# 启动游戏
game.start_game()