在这个数字化时代,视觉效果在游戏开发中扮演着至关重要的角色。无论是简单的2D游戏还是复杂的3D世界,视觉效果都能为用户带来沉浸式体验。今天,我们将通过Python的Pygame库,带你一步步实现一个绚丽的烟花效果。无论你是初学者还是有经验的开发者,这篇文章都将为你提供有价值的指导和灵感。
为什么选择Pygame?
Pygame是一个简单易用的Python库,专为游戏开发设计。它提供了丰富的功能,包括图像处理、声音播放和事件管理等,非常适合用来制作2D游戏和动画效果。通过Pygame,你可以轻松地将创意转化为现实。
项目概述
在本教程中,我们将创建一个烟花效果的模拟器。烟花将从屏幕底部升起,达到一定高度后爆炸,形成色彩斑斓的粒子效果。我们将使用面向对象的编程思想来组织代码,使其更易于维护和扩展。
代码实现
环境准备
在开始之前,请确保你的开发环境中已经安装了Python和Pygame库。如果没有安装,可以通过以下命令进行安装:
pip install pygame
代码结构
我们将代码分为几个主要部分:Firework类、Particle类、Trail类以及主程序逻辑。每个部分都有其特定的功能和职责。
1. Firework类
Firework类负责管理烟花的整体行为,包括升空、爆炸和粒子生成。
class Firework:
def __init__(self):
# 随机生成烟花的颜色,范围为RGB (0-255)
self.colour = tuple(randint(0, 255) for _ in range(3))
# 随机生成烟花爆炸后产生的颜色,生成3种不同的颜色
self.colours = [tuple(randint(0, 255) for _ in range(3)) for _ in range(3)]
# 创建一个粒子对象,表示烟花的初始状态,位置在屏幕底部
self.firework = Particle(randint(0, DISPLAY_WIDTH), DISPLAY_HEIGHT, True, self.colour)
# 标记烟花是否已经爆炸
self.exploded = False
# 存储爆炸后产生的粒子
self.particles = []
# 定义烟花爆炸时产生的粒子数量范围
self.min_max_particles = vector(200, 300) # 增加粒子数量
def update(self, win):
# 更新烟花状态
if not self.exploded:
# 应用重力对烟花的影响
self.firework.apply_force(gravity)
# 移动烟花
self.firework.move()
# 显示烟花的尾迹
for tf in self.firework.trails:
tf.show(win)
# 显示当前烟花
self.show(win)
# 检查烟花是否达到爆炸条件
if self.firework.vel.y >= 0:
self.exploded = True # 标记为已爆炸
self.explode() # 执行爆炸
else:
# 如果烟花已经爆炸,更新每个粒子的状态
for particle in self.particles:
# 应用重力和随机扰动对粒子的影响
particle.apply_force(
vector(gravity.x + uniform(-0.1, 0.1), gravity.y / 2 + uniform(0.01, 0.08)))
# 移动粒子
particle.move()
# 显示粒子的尾迹
for t in particle.trails:
t.show(win)
# 显示当前粒子
particle.show(win)
def explode(self):
# 随机生成爆炸产生的粒子数量
amount = randint(int(self.min_max_particles.x), int(self.min_max_particles.y))
# 创建粒子并添加到粒子列表中
self.particles.extend(
Particle(self.firework.pos.x, self.firework.pos.y, False, self.colours) for _ in range(amount)
)
def show(self, win):
# 在窗口中绘制当前烟花
pygame.draw.circle(win, self.colour, (int(self.firework.pos.x), int(self.firework.pos.y)), self.firework.size)
def remove(self):
# 移除已标记为删除的粒子
self.particles = [p for p in self.particles if not p.remove]
# 返回是否已爆炸且没有剩余粒子
return self.exploded and not self.particles
2. Particle类
Particle类负责管理每个粒子的行为,包括运动、衰减和显示。
class Particle:
def __init__(self, x, y, firework, colour):
# 初始化粒子的位置、类型(烟花或普通粒子)、颜色等属性
self.firework = firework # 布尔值,指示是否为烟花粒子
self.pos = vector(x, y) # 当前粒子的位置
self.origin = vector(x, y) # 粒子的起始位置
self.radius = 20 # 粒子的半径
self.remove = False # 标记粒子是否需要被移除
self.explosion_radius = randint(10, 25) # 随机生成爆炸半径
self.life = 0 # 粒子的生命周期
self.acc = vector(0, 0) # 粒子的加速度
# 创建粒子的尾迹,动态或静态,取决于粒子类型
self.trails = [Trail(i, 5 if firework else randint(2, 4), firework) for i in range(5)]
self.prev_posx = [-10] * 10 # 存储前10帧的x坐标
self.prev_posy = [-10] * 10 # 存储前10帧的y坐标
# 根据粒子类型设置速度、大小和颜色
if self.firework:
self.vel = vector(0, -randint(17, 20)) # 烟花粒子向上发射
self.size = 5 # 烟花粒子大小
self.colour = colour # 烟花颜色
else:
# 普通粒子随机速度和大小
self.vel = vector(uniform(-2, 2), uniform(-2, 2)) # 随机初始速度
self.vel *= randint(10, self.explosion_radius + 5) # 根据爆炸半径调整速度
self.size = randint(3, 5) # 随机粒子大小
self.colour = choice(colour) # 从颜色列表中随机选择颜色
def apply_force(self, force):
# 应用外力到粒子的加速度
self.acc += force
def move(self):
# 更新粒子的位置和状态
if not self.firework:
self.vel *= 0.9 # 普通粒子速度衰减
self.vel += self.acc # 更新速度
self.pos += self.vel # 更新位置
self.acc *= 0 # 重置加速度
# 检查普通粒子是否超出爆炸半径
if self.life == 0 and not self.firework:
distance = self.pos.distance_to(self.origin)
if distance > self.explosion_radius:
self.remove = True # 超出范围则标记为移除
self.decay() # 处理粒子的衰减
self.trail_update() # 更新尾迹
self.life += 1 # 增加生命周期
def show(self, win):
# 在窗口中绘制粒子
pygame.draw.circle(win, self.colour, (int(self.pos.x), int(self.pos.y)), self.size)
def decay(self):
# 根据粒子的生命周期决定是否移除
if 70 > self.life > 20: # 在20到70之间的粒子
if randint(0, 30) == 0: # 有小概率移除
self.remove = True
elif self.life > 70: # 超过70的粒子
if randint(0, 5) == 0: # 有更高概率移除
self.remove = True
def trail_update(self):
# 更新粒子的尾迹位置
self.prev_posx.pop() # 移除最旧的x坐标
self.prev_posx.insert(0, int(self.pos.x)) # 插入当前x坐标
self.prev_posy.pop() # 移除最旧的y坐标
self.prev_posy.insert(0, int(self.pos.y)) # 插入当前y坐标
# 更新每个尾迹的位置
for n, t in enumerate(self.trails):
if t.dynamic:
t.get_pos(self.prev_posx[n + dynamic_offset], self.prev_posy[n + dynamic_offset])
else:
t.get_pos(self.prev_posx[n + static_offset], self.prev_posy[n + static_offset])
3. Trail类
Trail类负责管理粒子的尾迹效果,使烟花看起来更加真实。
class Trail:
def __init__(self, n, size, dynamic):
# 初始化尾迹的属性
self.pos_in_line = n # 尾迹在粒子尾迹中的位置索引
self.pos = vector(-10, -10) # 尾迹的初始位置,设置为无效值
self.dynamic = dynamic # 布尔值,指示尾迹是否为动态
# 根据尾迹的动态性设置颜色
self.colour = trail_colours[n] if dynamic else (255, 255, 200) # 动态尾迹使用预定义颜色,静态尾迹为淡黄色
# 根据尾迹的动态性和位置设置大小
self.size = max(size - n // 2, 0) if dynamic else max(size - 2, 0) # 动态尾迹大小随位置变化,静态尾迹大小固定
def get_pos(self, x, y):
# 更新尾迹的位置
self.pos = vector(x, y) # 将尾迹位置设置为传入的坐标
def show(self, win):
# 在窗口中绘制尾迹
pygame.draw.circle(win, self.colour, (int(self.pos.x), int(self.pos.y)), self.size) # 绘制尾迹的圆形
4. 主程序逻辑
主程序负责初始化Pygame窗口,处理事件循环,并更新和显示烟花效果。
def update(win, fireworks):
# 更新所有烟花的状态并在窗口中绘制
for fw in fireworks:
fw.update(win) # 调用每个烟花的更新方法
if fw.remove(): # 检查烟花是否需要移除
fireworks.remove(fw) # 从列表中移除烟花
pygame.display.update() # 更新窗口显示
def main():
pygame.init() # 初始化 Pygame
pygame.display.set_caption("Fireworks in Pygame") # 设置窗口标题
win = pygame.display.set_mode((DISPLAY_WIDTH, DISPLAY_HEIGHT)) # 创建窗口
clock = pygame.time.Clock() # 创建时钟对象以控制帧率
fireworks = [Firework() for _ in range(2)] # 初始化两个烟花对象
running = True # 运行标志
while running:
clock.tick(60) # 控制游戏循环的帧率为60帧每秒
for event in pygame.event.get(): # 处理事件
if event.type == pygame.QUIT: # 检查是否关闭窗口
running = False # 设置运行标志为 False,退出循环
if event.type == pygame.KEYDOWN: # 检查键盘按下事件
if event.key == pygame.K_1: # 按下数字1键
fireworks.append(Firework()) # 添加一个新的烟花
if event.key == pygame.K_2: # 按下数字2键
fireworks.extend(Firework() for _ in range(10)) # 添加10个新的烟花
win.fill((20, 20, 30)) # 清空窗口,填充背景色
if randint(0, 20) == 1: # 随机生成新的烟花
fireworks.append(Firework()) # 添加一个新的烟花
update(win, fireworks) # 更新窗口和烟花状态
pygame.quit() # 退出 Pygame
main() # 调用主函数开始程序
想体验OpenAI-o1模型的可以点击这里:pc.aihao123.cn/index.html#…。
源代码
import pygame
from random import randint, uniform, choice
import math
# 定义向量和重力
vector = pygame.math.Vector2
gravity = vector(0, 0.3) # 模拟重力效果
DISPLAY_WIDTH = DISPLAY_HEIGHT = 800 # 窗口大小
# 定义尾迹颜色
trail_colours = [(45, 45, 45), (60, 60, 60), (75, 75, 75),
(125, 125, 125), (150, 150, 150)]
dynamic_offset = 1 # 动态尾迹偏移
static_offset = 5 # 静态尾迹偏移
class Firework:
def __init__(self):
# 初始化烟花的颜色和粒子
self.colour = tuple(randint(0, 255) for _ in range(3)) # 随机颜色
self.colours = [tuple(randint(0, 255) for _ in range(3)) for _ in range(3)] # 粒子颜色
self.firework = Particle(randint(0, DISPLAY_WIDTH), DISPLAY_HEIGHT, True, self.colour) # 创建烟花粒子
self.exploded = False # 标记烟花是否已爆炸
self.particles = [] # 存储爆炸后的粒子
self.min_max_particles = vector(200, 300) # 粒子数量范围
def update(self, win):
# 更新烟花状态
if not self.exploded:
self.firework.apply_force(gravity) # 应用重力
self.firework.move() # 移动烟花粒子
for tf in self.firework.trails:
tf.show(win) # 显示尾迹
self.show(win) # 显示烟花
if self.firework.vel.y >= 0: # 检查烟花是否达到最高点
self.exploded = True # 标记为已爆炸
self.explode() # 执行爆炸
else:
# 更新爆炸后的粒子
for particle in self.particles:
particle.apply_force(vector(gravity.x + uniform(-0.1, 0.1), gravity.y / 2 + uniform(0.01, 0.08)))
particle.move() # 移动粒子
for t in particle.trails:
t.show(win) # 显示粒子的尾迹
particle.show(win) # 显示粒子
def explode(self):
# 生成爆炸后的粒子
amount = randint(int(self.min_max_particles.x), int(self.min_max_particles.y))
self.particles.extend(
Particle(self.firework.pos.x, self.firework.pos.y, False, self.colours) for _ in range(amount)
)
def show(self, win):
# 在窗口中绘制烟花
pygame.draw.circle(win, self.colour, (int(self.firework.pos.x), int(self.firework.pos.y)), self.firework.size)
def remove(self):
# 移除已标记的粒子
self.particles = [p for p in self.particles if not p.remove]
return self.exploded and not self.particles # 返回是否可以移除烟花
class Particle:
def __init__(self, x, y, firework, colour):
# 初始化粒子的属性
self.firework = firework
self.pos = vector(x, y) # 当前粒子位置
self.origin = vector(x, y) # 粒子起始位置
self.radius = 20 # 粒子半径
self.remove = False # 标记粒子是否需要移除
self.explosion_radius = randint(10, 25) # 随机爆炸半径
self.life = 0 # 粒子生命周期
self.acc = vector(0, 0) # 粒子加速度
# 创建尾迹
self.trails = [Trail(i, 5 if firework else randint(2, 4), firework) for i in range(5)]
self.prev_posx = [-10] * 10 # 存储前10帧的x坐标
self.prev_posy = [-10] * 10 # 存储前10帧的y坐标
# 根据粒子类型设置速度、大小和颜色
if self.firework:
self.vel = vector(0, -randint(17, 20)) # 烟花粒子向上发射
self.size = 5 # 烟花粒子大小
self.colour = colour # 烟花颜色
else:
# 普通粒子随机速度和大小
self.vel = vector(uniform(-2, 2), uniform(-2, 2)) # 随机初始速度
self.vel *= randint(10, self.explosion_radius + 5) # 根据爆炸半径调整速度
self.size = randint(3, 5) # 随机粒子大小
self.colour = choice(colour) # 从颜色列表中随机选择颜色
def apply_force(self, force):
# 应用外力到粒子的加速度
self.acc += force
def move(self):
# 更新粒子的位置和状态
if not self.firework:
self.vel *= 0.9 # 普通粒子速度衰减
self.vel += self.acc # 更新速度
self.pos += self.vel # 更新位置
self.acc *= 0 # 重置加速度
# 检查普通粒子是否超出爆炸半径
if self.life == 0 and not self.firework:
distance = self.pos.distance_to(self.origin)
if distance > self.explosion_radius:
self.remove = True # 超出范围则标记为移除
self.decay() # 处理粒子的衰减
self.trail_update() # 更新尾迹
self.life += 1 # 增加生命周期
def show(self, win):
# 在窗口中绘制粒子
pygame.draw.circle(win, self.colour, (int(self.pos.x), int(self.pos.y)), self.size)
def decay(self):
# 根据粒子的生命周期决定是否移除
if 70 > self.life > 20: # 在20到70之间的粒子
if randint(0, 30) == 0: # 有小概率移除
self.remove = True
elif self.life > 70: # 超过70的粒子
if randint(0, 5) == 0: # 有更高概率移除
self.remove = True
def trail_update(self):
# 更新粒子的尾迹位置
self.prev_posx.pop() # 移除最旧的x坐标
self.prev_posx.insert(0, int(self.pos.x)) # 插入当前x坐标
self.prev_posy.pop() # 移除最旧的y坐标
self.prev_posy.insert(0, int(self.pos.y)) # 插入当前y坐标
# 更新每个尾迹的位置
for n, t in enumerate(self.trails):
if t.dynamic:
t.get_pos(self.prev_posx[n + dynamic_offset], self.prev_posy[n + dynamic_offset])
else:
t.get_pos(self.prev_posx[n + static_offset], self.prev_posy[n + static_offset])
class Trail:
def __init__(self, n, size, dynamic):
# 初始化尾迹的属性
self.pos_in_line = n # 尾迹在粒子尾迹中的位置索引
self.pos = vector(-10, -10) # 尾迹的初始位置,设置为无效值
self.dynamic = dynamic # 布尔值,指示尾迹是否为动态
# 根据尾迹的动态性设置颜色
self.colour = trail_colours[n] if dynamic else (255, 255, 200) # 动态尾迹使用预定义颜色,静态尾迹为淡黄色
# 根据尾迹的动态性和位置设置大小
self.size = max(size - n // 2, 0) if dynamic else max(size - 2, 0) # 动态尾迹大小随位置变化,静态尾迹大小固定
def get_pos(self, x, y):
# 更新尾迹的位置
self.pos = vector(x, y) # 将尾迹位置设置为传入的坐标
def show(self, win):
# 在窗口中绘制尾迹
pygame.draw.circle(win, self.colour, (int(self.pos.x), int(self.pos.y)), self.size) # 绘制尾迹的圆形
def update(win, fireworks):
# 更新所有烟花的状态并在窗口中绘制
for fw in fireworks:
fw.update(win) # 调用每个烟花的更新方法
if fw.remove(): # 检查烟花是否需要移除
fireworks.remove(fw) # 从列表中移除烟花
pygame.display.update() # 更新窗口显示
def main():
pygame.init() # 初始化 Pygame
pygame.display.set_caption("Fireworks in Pygame") # 设置窗口标题
win = pygame.display.set_mode((DISPLAY_WIDTH, DISPLAY_HEIGHT)) # 创建窗口
clock = pygame.time.Clock() # 创建时钟对象以控制帧率
fireworks = [Firework() for _ in range(2)] # 初始化两个烟花对象
running = True # 运行标志
while running:
clock.tick(60) # 控制游戏循环的帧率为60帧每秒
for event in pygame.event.get(): # 处理事件
if event.type == pygame.QUIT: # 检查是否关闭窗口
running = False # 设置运行标志为 False,退出循环
if event.type == pygame.KEYDOWN: # 检查键盘按下事件
if event.key == pygame.K_1: # 按下数字1键
fireworks.append(Firework()) # 添加一个新的烟花
if event.key == pygame.K_2: # 按下数字2键
fireworks.extend(Firework() for _ in range(10)) # 添加10个新的烟花
win.fill((20, 20, 30)) # 清空窗口,填充背景色
if randint(0, 20) == 1: # 随机生成新的烟花
fireworks.append(Firework()) # 添加一个新的烟花
update(win, fireworks) # 更新窗口和烟花状态
pygame.quit() # 退出 Pygame
main() # 调用主函数开始程序
更多提效文章
【IDER、PyCharm】免费AI编程工具完整教程:ChatGPT Free - Support Key call AI GPT-o1 Claude3.5
【OpenAI】获取OpenAI API KEY的两种方式,开发者必看全方面教程!
【Cursor】揭秘Cursor:如何免费无限使用这款AI编程神器?
结尾
通过本教程,你已经掌握了如何使用Pygame创建一个简单而又绚丽的烟花效果。这个项目不仅可以帮助你理解Pygame的基本用法,还能为你在游戏开发中实现复杂的视觉效果提供灵感。希望你能在此基础上,继续探索更多有趣的项目。🎇
如果你对Pygame或游戏开发有任何疑问,欢迎在评论区留言,我们将共同探讨。祝你在编程的旅程中不断进步!