【走过路过,不要错过哦】python制作的浪漫烟花,非常烂漫
这是一个基于Python制作的烟花绽放动画程序,可以用于表白祝福。
一、设置一些画板的基本参数
# 初始化Pygame库。
pg.init()
# 设置窗口标题为“🎆 财源滚滚 🎆”。
pg.display.set_caption("🎆 财源滚滚 🎆")
# 获取当前屏幕信息并调整窗口高度以适应标题栏。
winScreen = pg.display.Info()
screenWidth = winScreen.current_w
screenHeight = winScreen.current_h - 66
# 导入Pygame的二维向量类。
vector = pg.math.Vector2
# 增加颜色多样性
trail_colors = [
(255, 0, 0), (255, 165, 0), (255, 255, 0), (0, 255, 0), (0, 0, 255),
(128, 0, 128), (128, 0, 0), (0, 128, 0), (0, 128, 128), (0, 0, 128)
] + [(ra.randint(0, 255), ra.randint(0, 255), ra.randint(0, 255)) for _ in range(50)]
二、初始化一个烟花对象
def __init__(self, initial_y=None):
"""
初始化一个烟花对象。
本构造函数设置了烟花的颜色、创建了一个粒子对象来表示烟花、设置烟花初始状态为未爆炸、
并初始化一个双端队列来存储爆炸后产生的粒子。
"""
# 随机选择一种颜色作为烟花的颜色
self.colour = trail_colors[ra.randint(0, len(trail_colors)-1)]
# 创建一个粒子对象来表示烟花,初始位置随机,带有指定颜色
#self.firework = Particle(ra.randint(50, screenWidth), screenHeight, True, [self.colour])
# 初始化烟花的位置,如果提供了initial_y则使用它,否则使用随机值
self.firework = Particle(
ra.randint(50, screenWidth) if initial_y is None else ra.randint(50, screenWidth), # x坐标随机
initial_y if initial_y is not None else screenHeight, # 使用提供的y坐标或随机值
True,
[self.colour]
)
# 设置烟花初始状态为未爆炸
self.exploded = False
# 初始化一个双端队列来存储爆炸后产生的粒子
self.particles = deque()
# 设置粒子数量的最小值和最大值
self.min_max_particles = vector(100, 300)
三、更新烟花状态
def update(self, win):
"""
更新烟花状态。
如果烟花尚未爆炸,则更新烟花的位置。当烟花需要移除时,表示烟花已爆炸,
此时生成一系列粒子。如果烟花已经爆炸,则更新所有粒子的状态,并移除需要删除的粒子。
当所有粒子都被移除后,重置烟花的爆炸状态。
参数:
win (Window): 游戏窗口对象,用于显示烟花和粒子。
返回:
无
"""
if not self.exploded:
# 更新烟花的位置和状态
self.firework.update()
# 当烟花需要移除时,表示烟花已爆炸
if self.firework.remove:
self.exploded = True
# 生成随机数量的粒子
num_particles = ra.randint(int(self.min_max_particles.x), int(self.min_max_particles.y))
# 为每个粒子随机选择颜色
particle_colors = [trail_colors[ra.randint(0, len(trail_colors) - 1)] for _ in range(num_particles)]
# 创建并添加粒子到粒子列表中
for i in range(num_particles):
self.particles.append(Particle(self.firework.pos.x, self.firework.pos.y, False, [particle_colors[i]]))
else:
# 更新所有粒子的状态
for p in list(self.particles):
p.update()
# 移除需要删除的粒子
if p.remove:
self.particles.remove(p)
# 当所有粒子都被移除后,重置烟花的爆炸状态
if not self.particles:
self.exploded = False # Reset exploded state, though typically we wouldn't reset
四、绘制烟火和粒子
def draw(self, win):
"""
绘制烟火和粒子。
如果烟火没有爆炸,则绘制烟火;否则,绘制所有粒子。
参数:
- win: 绘制烟火和粒子的窗口。
返回值:
无
"""
# 检查烟火是否已经爆炸
if not self.exploded:
# 如果没有爆炸,绘制烟火
self.firework.draw(win)
# 无论烟火是否爆炸,都绘制所有粒子
for p in self.particles:
p.draw(win)
五、定义 Particle 类
初始化烟火对象的构造函数
def __init__(self, x, y, firework, colour_list):
"""
初始化烟火对象的构造函数。
参数:
x (int): 烟火的初始x坐标。
y (int): 烟火的初始y坐标。
firework (bool): 表示是否为烟火。用于区分烟火和粒子的行为。
colour_list (list): 包含可能的颜色的列表。粒子从中选择一种颜色。
返回:
None
"""
self.firework = firework # 是否为烟火
self.pos = vector(x, y) # 当前位置
self.origin = vector(x, y) # 初始位置,用于重置或计算轨迹
self.radius = 60 # 烟火的初始半径或大小
self.remove = False # 标记是否应该移除该粒子
self.life = 0 # 粒子的生命周期计数器
self.acc = vector(0, 0.05) # 粒子的加速度,模拟重力效果
self.size = ra.randint(1, 2) # 粒子的大小或半径
self.vel = vector(
ra.uniform(-2, 2) if not firework else 0,
-ra.randint(20, 30) if firework else ra.uniform(-2, 2)
)
# 根据是否是烟火,设置初始速度。烟火有向上的速度,而粒子在水平方向有速度。
self.colour = colour_list[0] # 从传入的颜色列表中选择一个颜色作为粒子的颜色
# 轨迹的长度,即轨迹中粒子的数量
self.trail_length = 10
# 使用deque存储轨迹位置,自动管理最大长度,用于渲染轨迹效果
self.trail_positions = deque([(x, y)] * self.trail_length, maxlen=self.trail_length)
更新烟花或粒子的状态
def update(self):
"""
更新烟花或粒子的状态。
该方法主要用于更新烟花或粒子的位置,判断其是否需要被移除,以及在其生命周期内追踪其轨迹。
同时确保粒子不会超出屏幕边界。
"""
# 更新速度和位置
self.vel += self.acc
self.pos += self.vel
# 如果是烟花,判断其是否达到最高点并决定是否移除
if self.firework:
if self.pos.y < self.origin.y - 100:
self.remove = True
else:
# 如果是粒子,增加其生命周期并判断是否达到生命终点
self.life += 1
if self.life > 25:
self.remove = True
# 确保粒子不会超出屏幕边界
if self.pos.x < 0:
self.pos.x = 0
elif self.pos.x > screenWidth:
self.pos.x = screenWidth-10
if self.pos.y < 0:
self.pos.y = 0
elif self.pos.y > screenHeight:
self.pos.y = screenHeight
# 记录当前位置到轨迹列表,用于后续渲染轨迹
self.trail_positions.appendleft((self.pos.x, self.pos.y))
在指定的窗口上绘制当前对象
def draw(self, win):
"""
在指定的窗口上绘制当前对象。
参数:
- win: pygame窗口对象,用于绘制图形。
此方法负责在给定的pygame窗口上绘制当前对象的主要圆形部分和其轨迹。
"""
# 绘制主要圆形部分
pg.draw.circle(win, self.colour, (int(self.pos.x), int(self.pos.y)), self.size)
# 计算轨迹的透明度,确保其根据对象的生命周期或是否为烟火来变化
trail_alpha = min(255, self.life * 10) if not self.firework else 255
# 绘制轨迹,每个轨迹点都是一个小圆点,具有计算出的透明度
for x, y in self.trail_positions:
pg.draw.circle(win, (self.colour[0], self.colour[1], self.colour[2], trail_alpha), (int(x), int(y)), 1)
六、完整代码
import pygame as pg
import random as ra
from collections import deque
pg.init()
pg.display.set_caption("🎆 Happy New Year! 🎆")
winScreen = pg.display.Info()
screenWidth = winScreen.current_w
screenHeight = winScreen.current_h - 66 # Adjust for window title bar
vector = pg.math.Vector2
# 增加颜色多样性
trail_colors = [
(255, 0, 0), (255, 165, 0), (255, 255, 0), (0, 255, 0), (0, 0, 255),
(128, 0, 128), (128, 0, 0), (0, 128, 0), (0, 128, 128), (0, 0, 128)
] + [(ra.randint(0, 255), ra.randint(0, 255), ra.randint(0, 255)) for _ in range(50)]
class Firework:
def __init__(self, initial_y=None):
"""
初始化一个烟花对象。
本构造函数设置了烟花的颜色、创建了一个粒子对象来表示烟花、设置烟花初始状态为未爆炸、
并初始化一个双端队列来存储爆炸后产生的粒子。
"""
# 随机选择一种颜色作为烟花的颜色
self.colour = trail_colors[ra.randint(0, len(trail_colors)-1)]
# 创建一个粒子对象来表示烟花,初始位置随机,带有指定颜色
#self.firework = Particle(ra.randint(50, screenWidth), screenHeight, True, [self.colour])
# 初始化烟花的位置,如果提供了initial_y则使用它,否则使用随机值
self.firework = Particle(
ra.randint(50, screenWidth) if initial_y is None else ra.randint(50, screenWidth), # x坐标随机
initial_y if initial_y is not None else screenHeight, # 使用提供的y坐标或随机值
True,
[self.colour]
)
# 设置烟花初始状态为未爆炸
self.exploded = False
# 初始化一个双端队列来存储爆炸后产生的粒子
self.particles = deque()
# 设置粒子数量的最小值和最大值
self.min_max_particles = vector(100, 300)
def update(self, win):
"""
更新烟花状态。
如果烟花尚未爆炸,则更新烟花的位置。当烟花需要移除时,表示烟花已爆炸,
此时生成一系列粒子。如果烟花已经爆炸,则更新所有粒子的状态,并移除需要删除的粒子。
当所有粒子都被移除后,重置烟花的爆炸状态。
参数:
win (Window): 游戏窗口对象,用于显示烟花和粒子。
返回:
无
"""
if not self.exploded:
# 更新烟花的位置和状态
self.firework.update()
# 当烟花需要移除时,表示烟花已爆炸
if self.firework.remove:
self.exploded = True
# 生成随机数量的粒子
num_particles = ra.randint(int(self.min_max_particles.x), int(self.min_max_particles.y))
# 为每个粒子随机选择颜色
particle_colors = [trail_colors[ra.randint(0, len(trail_colors) - 1)] for _ in range(num_particles)]
# 创建并添加粒子到粒子列表中
for i in range(num_particles):
self.particles.append(Particle(self.firework.pos.x, self.firework.pos.y, False, [particle_colors[i]]))
else:
# 更新所有粒子的状态
for p in list(self.particles):
p.update()
# 移除需要删除的粒子
if p.remove:
self.particles.remove(p)
# 当所有粒子都被移除后,重置烟花的爆炸状态
if not self.particles:
self.exploded = False # Reset exploded state, though typically we wouldn't reset
def draw(self, win):
"""
绘制烟火和粒子。
如果烟火没有爆炸,则绘制烟火;否则,绘制所有粒子。
参数:
- win: 绘制烟火和粒子的窗口。
返回值:
无
"""
# 检查烟火是否已经爆炸
if not self.exploded:
# 如果没有爆炸,绘制烟火
self.firework.draw(win)
# 无论烟火是否爆炸,都绘制所有粒子
for p in self.particles:
p.draw(win)
class Particle:
def __init__(self, x, y, firework, colour_list):
"""
初始化烟火对象的构造函数。
参数:
x (int): 烟火的初始x坐标。
y (int): 烟火的初始y坐标。
firework (bool): 表示是否为烟火。用于区分烟火和粒子的行为。
colour_list (list): 包含可能的颜色的列表。粒子从中选择一种颜色。
返回:
None
"""
self.firework = firework # 是否为烟火
self.pos = vector(x, y) # 当前位置
self.origin = vector(x, y) # 初始位置,用于重置或计算轨迹
self.radius = 60 # 烟火的初始半径或大小
self.remove = False # 标记是否应该移除该粒子
self.life = 0 # 粒子的生命周期计数器
self.acc = vector(0, 0.05) # 粒子的加速度,模拟重力效果
self.size = ra.randint(1, 2) # 粒子的大小或半径
self.vel = vector(
ra.uniform(-2, 2) if not firework else 0,
-ra.randint(20, 30) if firework else ra.uniform(-2, 2)
)
# 根据是否是烟火,设置初始速度。烟火有向上的速度,而粒子在水平方向有速度。
self.colour = colour_list[0] # 从传入的颜色列表中选择一个颜色作为粒子的颜色
# 轨迹的长度,即轨迹中粒子的数量
self.trail_length = 10
# 使用deque存储轨迹位置,自动管理最大长度,用于渲染轨迹效果
self.trail_positions = deque([(x, y)] * self.trail_length, maxlen=self.trail_length)
def update(self):
"""
更新烟花或粒子的状态。
该方法主要用于更新烟花或粒子的位置,判断其是否需要被移除,以及在其生命周期内追踪其轨迹。
同时确保粒子不会超出屏幕边界。
"""
# 更新速度和位置
self.vel += self.acc
self.pos += self.vel
# 如果是烟花,判断其是否达到最高点并决定是否移除
if self.firework:
if self.pos.y < self.origin.y - 100:
self.remove = True
else:
# 如果是粒子,增加其生命周期并判断是否达到生命终点
self.life += 1
if self.life > 25:
self.remove = True
# 确保粒子不会超出屏幕边界
if self.pos.x < 0:
self.pos.x = 0
elif self.pos.x > screenWidth:
self.pos.x = screenWidth-10
if self.pos.y < 0:
self.pos.y = 0
elif self.pos.y > screenHeight:
self.pos.y = screenHeight
# 记录当前位置到轨迹列表,用于后续渲染轨迹
self.trail_positions.appendleft((self.pos.x, self.pos.y))
def draw(self, win):
"""
在指定的窗口上绘制当前对象。
参数:
- win: pygame窗口对象,用于绘制图形。
此方法负责在给定的pygame窗口上绘制当前对象的主要圆形部分和其轨迹。
"""
# 绘制主要圆形部分
pg.draw.circle(win, self.colour, (int(self.pos.x), int(self.pos.y)), self.size)
# 计算轨迹的透明度,确保其根据对象的生命周期或是否为烟火来变化
trail_alpha = min(255, self.life * 10) if not self.firework else 255
# 绘制轨迹,每个轨迹点都是一个小圆点,具有计算出的透明度
for x, y in self.trail_positions:
pg.draw.circle(win, (self.colour[0], self.colour[1], self.colour[2], trail_alpha), (int(x), int(y)), 1)
def main():
"""
主函数,负责初始化Pygame窗口和时钟,创建烟花对象,并开始主循环以维持程序运行。
"""
# 初始化Pygame显示窗口
screen = pg.display.set_mode((screenWidth, screenHeight))
# 创建Pygame时钟对象,用于控制帧率
clock = pg.time.Clock()
# 创建烟花对象列表,初始生成两个烟花
fireworks = [Firework() for i in range(8)]
# 设置运行状态为真,用于维持主循环
running = True
# 初始化字体对象,用于渲染文本
font = pg.font.SysFont("SimHei", 120)
# 定义要显示的文本内容
text = "财源滚滚"
# 定义文本颜色
text_color = (255, 190, 200)
# 渲染文本
rendered_text = font.render(text, True, text_color)
# 计算文本在屏幕上的位置
text_x = (screenWidth - rendered_text.get_width()) // 2
text_y = (screenHeight - rendered_text.get_height()) // 2
# 计算文本高度阈值(文本应该在屏幕中间,所以取屏幕高度的一半减去一些偏移量)
# 定义文本高度阈值,确保烟花在文本上方且离屏幕顶部有一定距离
safe_distance_from_top = 100 # 安全距离,可以根据需要调整
text_height_threshold = max(100, (screenHeight - rendered_text.get_height()) // 2 - safe_distance_from_top)
# 主循环开始
while running:
# 控制帧率为60帧/秒
clock.tick(60)
# 处理事件,如关闭窗口
for event in pg.event.get():
if event.type == pg.QUIT:
running = False
# 填充背景色
screen.fill((20, 20, 30))
# 将文本绘制到屏幕上
screen.blit(rendered_text, (text_x, text_y))
# 以一定概率生成新的烟花
# if ra.randint(0, 20) == 1:
# fireworks.append(Firework())
# 以一定概率生成新的烟花,并确保烟花在文本上方绽放
if ra.randint(0, 20) == 1:
# 生成一个初始位置在文本上方且离屏幕顶部有一定距离的烟花
new_firework_y_min = max(100, text_height_threshold - safe_distance_from_top * 2) # 确保y坐标不会太低
new_firework_y_max = text_height_threshold - safe_distance_from_top # 确保y坐标有足够空间给烟花绽放
new_firework_y = ra.randint(new_firework_y_min, new_firework_y_max) # 在这个范围内随机选择y坐标
new_firework = Firework(initial_y=new_firework_y)
fireworks.append(new_firework)
# 更新烟花状态,如果烟花爆炸且没有残留粒子,则移除烟花列表
for fw in fireworks[:]:
fw.update(screen)
if fw.exploded and not fw.particles:
fireworks.remove(fw)
# 绘制所有烟花到屏幕上
for fw in fireworks:
fw.draw(screen)
# 更新屏幕显示
pg.display.flip()
# 退出Pygame
pg.quit()
if __name__ == "__main__":
main()
作者:努力努力再努力呐