用 Python 打造梦幻烟花特效:从基础实现到极致美化
一、前置准备:环境与工具
1. 核心库安装
本次烟花特效的核心是 Pygame 库(用于图形绘制和动画控制),这是 Python 中最适合做 2D 图形和小游戏的库,安装方式非常简单,打开终端执行:
pip install pygame 验证安装成功的标志:运行代码时会出现Hello from the pygame community.的提示,无报错即可。
2. 环境要求
- Python 3.8 及以上版本(兼容主流 Python 版本);
- 操作系统:Windows/macOS/Linux 均可(本文以 Windows 为例);
- 可选:准备firework_sound.wav音效文件,放在代码同目录下,可实现烟花爆炸声效。
二、基础版:实现核心烟花效果
1. 核心思路
烟花特效的本质是粒子系统 + 物理模拟:
- 烟花弹:从屏幕底部向上运动,到达指定高度后爆炸;
- 粒子:烟花爆炸后生成大量粒子,向四周扩散,受重力下落并逐渐消失;
- 动画循环:通过 Pygame 的主循环持续更新粒子位置、绘制图形,形成动态效果。
2. 基础代码核心模块
(1)粒子类(Particle)
单个粒子是烟花的最小单元,负责模拟爆炸后的碎片运动:
class Particle:
def __init__(self, x, y, color):
self.x = x # 粒子坐标
self.y = y
self.color = color # 粒子颜色
# 随机扩散角度和速度
angle = random.uniform(0, 2 * math.pi)
speed = random.uniform(1, 4)
self.vx = math.cos(angle) * speed # x方向速度
self.vy = math.sin(angle) * speed # y方向速度
self.life = 60 # 粒子生命周期(帧数)
self.gravity = 0.05 # 重力加速度
def update(self):
# 模拟重力和空气阻力
self.vy += self.gravity
self.x += self.vx
self.y += self.vy
self.life -= 1
self.vx *= 0.98 # 速度衰减
self.vy *= 0.98
def draw(self):
# 透明度随生命周期渐变
alpha = int((self.life / 60) * 255)
color_with_alpha = self.color + (alpha,)
temp_surface = pygame.Surface((4, 4), pygame.SRCALPHA)
pygame.draw.circle(temp_surface, color_with_alpha, (2, 2), 2)
screen.blit(temp_surface, (int(self.x), int(self.y)))(2)烟花类(Firework)
控制烟花弹的发射和爆炸逻辑:
class Firework:
def __init__(self):
self.x = random.randint(100, WIDTH - 100) # 发射起点
self.y = HEIGHT
self.color = random.choice(COLORS) # 随机颜色
self.vy = random.uniform(-8, -12) # 向上发射速度
self.exploded = False
self.particles = []
self.explode_height = random.randint(100, 300) # 爆炸高度
def update(self):
if not self.exploded:
self.y += self.vy
self.vy += 0.1 # 发射速度衰减
if self.y <= self.explode_height:
self.explode() # 到达高度则爆炸
else:
for particle in self.particles[:]:
particle.update()
if particle.life <= 0:
self.particles.remove(particle)
def explode(self):
self.exploded = True
# 生成80-120个粒子
for _ in range(random.randint(80, 120)):
self.particles.append(Particle(self.x, self.y, self.color))(3)主循环
负责窗口渲染、事件处理(鼠标点击发射烟花)、动画刷新:
def main():
pygame.init()
WIDTH, HEIGHT = 800, 600
screen = pygame.display.set_mode((WIDTH, HEIGHT))
clock = pygame.time.Clock()
fireworks = []
running = True
while running:
clock.tick(60) # 60帧/秒,保证动画流畅
screen.fill((0,0,0), special_flags=pygame.BLEND_RGBA_MULT) # 黑色背景+拖影
# 事件处理:关闭窗口、鼠标点击发射烟花
for event in pygame.event.get():
if event.type == pygame.QUIT:
running = False
elif event.type == pygame.MOUSEBUTTONDOWN:
fireworks.append(Firework())
# 自动生成烟花
if random.randint(0, 30) == 0:
fireworks.append(Firework())
# 更新并绘制所有烟花
for firework in fireworks[:]:
firework.update()
firework.draw()
if firework.exploded and len(firework.particles) == 0:
fireworks.remove(firework)
pygame.display.flip()
pygame.quit()3. 基础版效果
运行代码后,会出现 800×600 的黑色窗口:
- 自动随机发射烟花,鼠标点击可手动发射;
- 烟花弹从底部升空,爆炸成彩色粒子,粒子受重力下落并逐渐消失,带有轻微拖影效果。
三、进阶美化:提升视觉氛围感
基础版实现了核心功能,但视觉效果较单调,我们可以从「背景、粒子、拖尾、爆炸模式」四个维度优化:
1. 核心优化点
| 优化方向 | 具体实现 | 效果提升 |
|---|---|---|
| 星空背景 | 新增 Star 类,生成 200 颗带闪烁效果的星星 | 黑色背景不再单调,营造夜晚氛围 |
| 发射拖尾 | 为烟花弹添加渐变透明的拖尾 | 模拟真实烟花的上升轨迹,更自然 |
| 粒子渐变 | 粒子从亮色渐变到基础色,支持大小 / 透明度渐变 | 视觉层次更丰富,避免单一色调 |
| 多爆炸模式 | 新增圆形、放射、密集三种爆炸模式 | 烟花形态更多样,不重复 |
2. 进阶版关键改进
(1)动态星空
class Star:
def __init__(self):
self.x = random.randint(0, WIDTH)
self.y = random.randint(0, HEIGHT)
self.size = random.uniform(0.5, 2)
self.brightness = random.randint(100, 255)
self.twinkle_speed = random.uniform(0.01, 0.03)
def update(self):
# 星星闪烁效果:亮度波动
self.brightness += random.uniform(-5, 5) * self.twinkle_speed
self.brightness = max(50, min(255, self.brightness))
def draw(self):
color = (self.brightness, self.brightness, self.brightness)
pygame.draw.circle(screen, color, (int(self.x), int(self.y)), int(self.size))(2)渐变粒子
粒子不再是单一颜色,而是从高光色渐变到基础色:
# 颜色定义:基础色+高光色配对
BASE_COLORS = [(255,80,80), (80,255,80), ...]
LIGHT_COLORS = [(255,150,150), (150,255,150), ...]
# 粒子绘制时的渐变逻辑
life_ratio = self.life / self.max_life
r = int(self.light_color[0] * life_ratio + self.base_color[0] * (1 - life_ratio))
g = int(self.light_color[1] * life_ratio + self.base_color[1] * (1 - life_ratio))
b = int(self.light_color[2] * life_ratio + self.base_color[2] * (1 - life_ratio))四、终极美化:打造梦幻级视觉效果
进阶版已经足够好看,但我们可以再突破,新增「异形爆炸、双层光晕、粒子闪烁、音效」等细节,让效果达到 “惊艳” 级别。
1. 终极优化亮点
(1)异形爆炸:心形 + 星形
通过数学公式实现心形和八角星形的爆炸轨迹,让烟花更有惊喜感:
# 心形轨迹公式
if explode_type == "heart":
t = random.uniform(0, 2 * math.pi)
speed = random.uniform(2, 4)
self.vx = (16 * math.sin(t)**3) * speed / 10
self.vy = -(13 * math.cos(t) - 5 * math.cos(2*t) - 2 * math.cos(3*t) - math.cos(4*t)) * speed / 10
# 星形轨迹公式
elif explode_type == "star":
t = random.uniform(0, 2 * math.pi)
speed = random.uniform(2, 5)
r = math.sin(5*t) * math.cos(4*t) * 5 + 10
base_angle = t
speed = speed * (r / 10)(2)双层光晕效果
每个粒子都有 “核心 + 外发光”,模拟真实烟花的光散射效果:
# 绘制光晕(外发光)
glow_size = core_size + 4
glow_surface = pygame.Surface((glow_size*2, glow_size*2), pygame.SRCALPHA)
glow_color = (self.glow_color[0], self.glow_color[1], self.glow_color[2], int(alpha * 0.3))
pygame.draw.circle(glow_surface, glow_color, (glow_size, glow_size), glow_size)
screen.blit(glow_surface, (int(self.x - glow_size), int(self.y - glow_size)))
# 绘制粒子核心
core_surface = pygame.Surface((core_size*2, core_size*2), pygame.SRCALPHA)
core_color = (r, g, b, alpha)
pygame.draw.circle(core_surface, core_color, (core_size, core_size), core_size)
screen.blit(core_surface, (int(self.x - core_size), int(self.y - core_size)))(3)粒子随机闪烁
模拟烟花爆炸时的火星迸射效果,让粒子更生动:
# 在Particle类的update方法中
self.flash = random.random() < 0.05 and self.life > self.max_life/2
# 在draw方法中
if self.flash:
r = min(255, r + 50)
g = min(255, g + 50)
b = min(255, b + 50)(4)音效支持
添加烟花爆炸声效,视听结合更沉浸:
try:
explode_sound = pygame.mixer.Sound("firework_sound.wav")
explode_sound.set_volume(0.5)
except:
explode_sound = None # 无音效文件则跳过
# 烟花爆炸时播放音效
if explode_sound:
explode_sound.play()五、常见问题与修复
开发过程中,新手容易遇到以下问题,这里给出解决方案:
1. AttributeError: 'Particle' object has no attribute 'flash'
原因
self.flash属性仅在update()方法中赋值,但draw()方法可能先于update()调用,导致属性不存在。
解决方案
在Particle类的__init__方法中提前初始化self.flash:
class Particle:
def __init__(self, x, y, base_color, highlight_color, glow_color, explode_type, angle_offset=0):
# 其他属性初始化...
self.flash = False # 新增:默认值为False2. 动画卡顿
原因
帧率不稳定、未及时清理已消失的烟花 / 粒子,导致内存占用过高。
解决方案
- 固定帧率:clock.tick(60)保证 60 帧 / 秒;
- 及时清理:爆炸后无粒子的烟花从列表中移除:
if firework.exploded and len(firework.particles) == 0:
fireworks.remove(firework)六、总结与扩展
总的代码:
import pygame
import random
import math
import time
# 初始化pygame
pygame.init()
pygame.mixer.init() # 音效初始化
# 屏幕设置(高清尺寸+抗锯齿)
WIDTH, HEIGHT = 1200, 800
screen = pygame.display.set_mode((WIDTH, HEIGHT))
pygame.display.set_caption("极致美化版烟花特效 | Python")
clock = pygame.time.Clock()
# 双缓冲表面(提升渲染流畅度)
buffer_surface = pygame.Surface((WIDTH, HEIGHT), pygame.SRCALPHA)
# 颜色系统(分级渐变+梦幻色调)
BLACK = (0, 0, 0)
# 主色(莫兰迪+霓虹混合)
PRIMARY_COLORS = [
(255, 90, 90), # 柔红
(80, 240, 80), # 嫩绿
(90, 90, 255), # 天蓝
(255, 240, 80), # 鹅黄
(240, 90, 255), # 柔紫
(80, 240, 240), # 薄荷青
(255, 180, 90), # 暖橙
(255, 120, 200), # 蜜桃粉
(180, 120, 255), # 薰衣草紫
(120, 200, 255) # 浅海蓝
]
# 高光色(更亮的渐变层)
HIGHLIGHT_COLORS = [
(255, 150, 150),
(150, 255, 150),
(150, 150, 255),
(255, 255, 150),
(255, 150, 255),
(150, 255, 255),
(255, 200, 150),
(255, 180, 220),
(200, 150, 255),
(150, 220, 255)
]
# 光晕色(极浅的渐变层)
GLOW_COLORS = [
(255, 200, 200),
(200, 255, 200),
(200, 200, 255),
(255, 255, 200),
(255, 200, 255),
(200, 255, 255),
(255, 220, 200),
(255, 220, 240),
(220, 200, 255),
(200, 240, 255)
]
# 加载音效(可选,注释掉则无音效)
try:
explode_sound = pygame.mixer.Sound("firework_sound.wav")
explode_sound.set_volume(0.5) # 音量0-1
except:
explode_sound = None # 无音效文件则跳过
# 动态星空类(移动+闪烁+大小变化)
class Star:
def __init__(self):
self.x = random.randint(0, WIDTH)
self.y = random.randint(0, HEIGHT)
self.base_size = random.uniform(0.3, 2)
self.size = self.base_size
self.base_brightness = random.randint(80, 255)
self.brightness = self.base_brightness
self.twinkle_speed = random.uniform(0.02, 0.05)
self.move_speed = random.uniform(0.05, 0.2) # 缓慢移动
def update(self):
# 闪烁效果(亮度波动)
self.brightness += random.uniform(-10, 10) * self.twinkle_speed
self.brightness = max(30, min(255, self.brightness))
# 大小随亮度变化
self.size = self.base_size * (self.brightness / 255)
# 缓慢移动(模拟星空旋转)
self.x += self.move_speed
self.y += random.uniform(-0.1, 0.1)
# 超出屏幕则重置
if self.x > WIDTH:
self.x = 0
if self.y < 0 or self.y > HEIGHT:
self.y = random.randint(0, HEIGHT)
def draw(self):
color = (self.brightness, self.brightness, self.brightness)
pygame.draw.circle(screen, color, (int(self.x), int(self.y)), int(self.size))
# 增强粒子类(光晕+闪烁+异形轨迹)
class Particle:
def __init__(self, x, y, base_color, highlight_color, glow_color, explode_type, angle_offset=0):
self.x = x
self.y = y
self.base_color = base_color
self.highlight_color = highlight_color
self.glow_color = glow_color
self.life = random.randint(60, 90) # 更长生命周期
self.max_life = self.life
self.gravity = 0.03 # 更轻柔的重力
self.explode_type = explode_type
# 关键修复:初始化flash属性为False,避免属性不存在报错
self.flash = False
# 根据爆炸类型计算角度和速度
base_angle = random.uniform(0, 2 * math.pi) + angle_offset
if explode_type == "circle":
speed = random.uniform(2, 5)
elif explode_type == "burst":
speed = random.uniform(3, 7)
elif explode_type == "heart":
# 心形轨迹公式
t = random.uniform(0, 2 * math.pi)
speed = random.uniform(2, 4)
self.vx = (16 * math.sin(t)**3) * speed / 10
self.vy = -(13 * math.cos(t) - 5 * math.cos(2*t) - 2 * math.cos(3*t) - math.cos(4*t)) * speed / 10
return
elif explode_type == "star":
# 星形轨迹(8角星)
t = random.uniform(0, 2 * math.pi)
speed = random.uniform(2, 5)
r = math.sin(5*t) * math.cos(4*t) * 5 + 10
base_angle = t
speed = speed * (r / 10)
else: # dense
speed = random.uniform(1, 3)
self.vx = math.cos(base_angle) * speed
self.vy = math.sin(base_angle) * speed
def update(self):
# 重力+空气阻力(更自然的运动)
self.vy += self.gravity
self.vx *= 0.96
self.vy *= 0.96
self.x += self.vx
self.y += self.vy
self.life -= 1
# 随机闪烁(生命周期中偶尔高亮)
self.flash = random.random() < 0.05 and self.life > self.max_life/2
def draw(self):
life_ratio = self.life / self.max_life
if life_ratio <= 0:
return
# 1. 计算渐变颜色(高光→主色→光晕)
r = int(self.highlight_color[0] * life_ratio + self.base_color[0] * (1 - life_ratio))
g = int(self.highlight_color[1] * life_ratio + self.base_color[1] * (1 - life_ratio))
b = int(self.highlight_color[2] * life_ratio + self.base_color[2] * (1 - life_ratio))
# 2. 闪烁效果(临时提升亮度)
if self.flash:
r = min(255, r + 50)
g = min(255, g + 50)
b = min(255, b + 50)
# 3. 透明度渐变
alpha = int(life_ratio * 255)
# 4. 粒子大小渐变
core_size = int(life_ratio * 3 + 1)
glow_size = core_size + 4 # 光晕大小
# 5. 绘制光晕(外发光效果)
glow_surface = pygame.Surface((glow_size*2, glow_size*2), pygame.SRCALPHA)
glow_color = (self.glow_color[0], self.glow_color[1], self.glow_color[2], int(alpha * 0.3))
pygame.draw.circle(glow_surface, glow_color, (glow_size, glow_size), glow_size)
screen.blit(glow_surface, (int(self.x - glow_size), int(self.y - glow_size)))
# 6. 绘制粒子核心
core_surface = pygame.Surface((core_size*2, core_size*2), pygame.SRCALPHA)
core_color = (r, g, b, alpha)
pygame.draw.circle(core_surface, core_color, (core_size, core_size), core_size)
screen.blit(core_surface, (int(self.x - core_size), int(self.y - core_size)))
# 终极烟花类(多层拖尾+光晕+异形爆炸)
class Firework:
def __init__(self):
# 发射起点
self.x = random.randint(100, WIDTH - 100)
self.y = HEIGHT
# 颜色配对(主色+高光+光晕)
color_idx = random.randint(0, len(PRIMARY_COLORS)-1)
self.base_color = PRIMARY_COLORS[color_idx]
self.highlight_color = HIGHLIGHT_COLORS[color_idx]
self.glow_color = GLOW_COLORS[color_idx]
# 发射速度
self.vy = random.uniform(-10, -15)
self.vx = random.uniform(-2, 2)
self.exploded = False
self.particles = []
# 爆炸参数
self.explode_height = random.randint(150, 400)
self.explode_type = random.choice(["circle", "burst", "dense", "heart", "star"])
# 多层拖尾(主拖尾+光晕拖尾)
self.main_trail = [] # 主拖尾
self.glow_trail = [] # 光晕拖尾
self.trail_length = 20 # 拖尾长度
# 烟花弹光晕
self.shell_glow_size = 8
def update(self):
if not self.exploded:
# 更新烟花弹位置
self.y += self.vy
self.x += self.vx
self.vy += 0.07 # 轻柔的重力衰减
# 更新多层拖尾
self.main_trail.append((self.x, self.y, self.vy))
self.glow_trail.append((self.x, self.y))
if len(self.main_trail) > self.trail_length:
self.main_trail.pop(0)
self.glow_trail.pop(0)
# 到达爆炸高度则爆炸
if self.y <= self.explode_height or self.vy >= 0:
self.explode()
else:
# 更新粒子
for particle in self.particles[:]:
particle.update()
if particle.life <= 0:
self.particles.remove(particle)
def explode(self):
self.exploded = True
# 播放爆炸音效(可选)
if explode_sound:
explode_sound.play()
# 根据爆炸类型调整粒子数量
particle_counts = {
"circle": random.randint(120, 180),
"burst": random.randint(100, 150),
"dense": random.randint(150, 220),
"heart": random.randint(80, 120),
"star": random.randint(100, 160)
}
num_particles = particle_counts[self.explode_type]
# 生成粒子
angle_offset = random.uniform(0, math.pi/4) # 轻微角度偏移,增加自然感
for _ in range(num_particles):
self.particles.append(Particle(
self.x, self.y,
self.base_color, self.highlight_color, self.glow_color,
self.explode_type, angle_offset
))
# 爆炸中心强光
flash_surface = pygame.Surface((40, 40), pygame.SRCALPHA)
pygame.draw.circle(flash_surface, (*self.highlight_color, 220), (20, 20), 20)
screen.blit(flash_surface, (int(self.x-20), int(self.y-20)))
def draw(self):
if not self.exploded:
# 1. 绘制烟花弹光晕
shell_glow_surface = pygame.Surface((self.shell_glow_size*2, self.shell_glow_size*2), pygame.SRCALPHA)
glow_color = (*self.glow_color, 100)
pygame.draw.circle(shell_glow_surface, glow_color, (self.shell_glow_size, self.shell_glow_size), self.shell_glow_size)
screen.blit(shell_glow_surface, (int(self.x - self.shell_glow_size), int(self.y - self.shell_glow_size)))
# 2. 绘制烟花弹核心
pygame.draw.circle(screen, self.highlight_color, (int(self.x), int(self.y)), 5)
# 3. 绘制多层拖尾(渐变透明+颜色过渡)
for i, ((tx, ty, vy), (gx, gy)) in enumerate(zip(self.main_trail, self.glow_trail)):
# 拖尾透明度随位置变化(越远越淡)
trail_alpha = int((i / len(self.main_trail)) * 180)
# 拖尾颜色随速度变化(速度越快越亮)
speed_ratio = abs(vy) / 15
trail_r = int(self.highlight_color[0] * speed_ratio + self.base_color[0] * (1 - speed_ratio))
trail_g = int(self.highlight_color[1] * speed_ratio + self.base_color[1] * (1 - speed_ratio))
trail_b = int(self.highlight_color[2] * speed_ratio + self.base_color[2] * (1 - speed_ratio))
# 主拖尾
main_trail_surface = pygame.Surface((4, 4), pygame.SRCALPHA)
main_color = (trail_r, trail_g, trail_b, trail_alpha)
pygame.draw.circle(main_trail_surface, main_color, (2, 2), 2)
screen.blit(main_trail_surface, (int(tx), int(ty)))
# 光晕拖尾
glow_trail_surface = pygame.Surface((8, 8), pygame.SRCALPHA)
glow_color = (self.glow_color[0], self.glow_color[1], self.glow_color[2], int(trail_alpha * 0.2))
pygame.draw.circle(glow_trail_surface, glow_color, (4, 4), 4)
screen.blit(glow_trail_surface, (int(gx - 4), int(gy - 4)))
else:
# 绘制爆炸粒子
for particle in self.particles:
particle.draw()
# 主函数(优化渲染+流畅度)
def main():
# 创建动态星空(300颗星星,更密集)
stars = [Star() for _ in range(300)]
fireworks = []
running = True
last_firework_time = time.time()
while running:
# 稳定60帧,避免卡顿
clock.tick(60)
# 半透明背景(保留拖影,更柔和)
screen.fill((0, 0, 0, 30), special_flags=pygame.BLEND_RGBA_MULT)
# 事件处理
for event in pygame.event.get():
if event.type == pygame.QUIT:
running = False
# 鼠标点击发射烟花(支持连续点击,每次2-3个)
elif event.type == pygame.MOUSEBUTTONDOWN:
for _ in range(random.randint(2, 3)):
fw = Firework()
fw.explode_height = max(150, min(450, event.pos[1]))
# 心形爆炸优先出现在点击位置
if random.random() < 0.3:
fw.explode_type = "heart"
fireworks.append(fw)
# 自动生成烟花(频率更自然,避免密集)
current_time = time.time()
if current_time - last_firework_time > random.uniform(0.5, 2):
fireworks.append(Firework())
last_firework_time = current_time
# 更新并绘制星空
for star in stars:
star.update()
star.draw()
# 更新并绘制所有烟花
for firework in fireworks[:]:
firework.update()
firework.draw()
# 清理已消失的烟花
if firework.exploded and len(firework.particles) == 0:
fireworks.remove(firework)
# 双缓冲刷新,提升流畅度
pygame.display.flip()
pygame.quit()
if __name__ == "__main__":
main()1. 核心知识点回顾
- 粒子系统是 2D 特效的核心思想,可扩展到雪花、雨水、火焰等场景;
- Pygame 的SRCALPHA属性实现透明效果,BLEND_RGBA_MULT实现拖影;
- 物理模拟的关键:通过速度、加速度模拟重力、空气阻力等真实物理规律。
2. 扩展建议
- 自定义颜色:修改PRIMARY_COLORS/HIGHLIGHT_COLORS,打造专属色调(如圣诞红 + 绿、新年金红);
- 调整参数:增大num_particles让烟花更浓密,调整gravity改变粒子下落速度;
- 新增特效:添加烟花爆炸后的 “余烬”、不同大小的烟花弹、鼠标拖拽发射等交互。
从几行基础代码到梦幻级特效,这个过程不仅能学会 Pygame 的核心用法,更能理解 “面向对象 + 动画循环 + 物理模拟” 的编程思维。希望这篇文章能让你感受到编程的浪漫,动手试试,让屏幕上绽放属于你的烟花吧!
版权所属:SO JSON在线解析
原文地址:https://www.sojson.com/blog/558.html
转载时必须以链接形式注明原始出处及本声明。
如果本文对你有帮助,那么请你赞助我,让我更有激情的写下去,帮助更多的人。
