使用Pygame制作“动态烟花”
1. 前言
在一些简单的图形工具(例如 turtle
)里,我们可以用静态绘制来模拟“烟花”形状,但这往往缺少逼真的运动轨迹和爆炸粒子的效果。本篇文章将使用 Pygame 这个 2D 游戏开发框架来实现烟花从地面升空到空中散落的一套简易动画,丰富你的编程体验,也为大家的节日增添一点氛围。
Pygame 介绍
Pygame 是用 Python 编写的 2D 游戏和多媒体程序开发库。它基于 SDL 库,可以让开发者轻松地管理窗口、图形、声音、事件(例如键盘或鼠标输入)等,实现一些基础的游戏或动画。
2. 开发环境准备
- Python 版本:Python 3.x
- Pygame:可通过
pip install pygame
安装。 - 系统:本示例在 Windows 和大部分 Linux 系统上都可运行,macOS 也支持。但需要保证你有一个可显示图形界面的环境。
安装好 Pygame 后,即可开始我们的烟花之旅!
3. 实现思路
要实现一个简单的“烟花”动画,我们可以使用粒子系统(Particle System)的思想进行设计。主要流程如下:
-
发射阶段(Rocket)
- 烟花最初是一枚从地面发射的火箭。我们给它一个初始位置(地面)、向上的速度、颜色等属性。
- 随着时间推移,火箭不断上升,速度可能会受到重力或空气阻力的影响(可根据需求简化或增加物理效果)。
-
爆炸阶段(Explosion)
- 当火箭到达一定高度或满足某些随机条件后,火箭会“爆炸”成许多粒子。
- 这些粒子会向四面八方飞散,并且在运动过程中慢慢减速下落,最终消失。
-
循环绘制
- 在游戏循环里,每帧都需要更新火箭/粒子的状态(位置、速度、寿命)并绘制它们。
- 当一束烟花完全消失后,可以根据设定随机产生新的烟花,实现持续的烟花表演。
接下来,我们会定义两个类:
Particle
:代表单个粒子(包括爆炸后的小火花,也可以代表火箭本身)。Firework
:代表一朵烟花,其内部有一个“火箭粒子”,在爆炸后会生成多个“爆炸粒子”。
4. 核心代码
下面是一个简化的示例。你可以把它保存成一个 .py
文件,然后运行观察效果。
提示:由于烟花的随机性,每次运行效果会略有不同;你也可以自由修改代码中的各项参数(速度、颜色、加速度、粒子数量等),打造自己独特的烟花效果!
import pygame
import random
import math
# 屏幕大小
WIDTH, HEIGHT = 800, 600
# 颜色定义(RGB)
BLACK = (0, 0, 0)
# 初始化 pygame
pygame.init()
screen = pygame.display.set_mode((WIDTH, HEIGHT))
pygame.display.set_caption("新年烟花 - Pygame 动画示例")
clock = pygame.time.Clock()
class Particle:
"""
粒子类:可用于表示烟花火箭或爆炸后的火花
"""
def __init__(self, x, y, vx, vy, color, lifespan=80):
self.x = x # 粒子位置 x
self.y = y # 粒子位置 y
self.vx = vx # 水平速度
self.vy = vy # 垂直速度
self.color = color # 粒子颜色
self.lifespan = lifespan # 粒子生命周期
self.alive = True # 是否存活
def update(self):
# 更新粒子状态:位置 + 重力 + 寿命衰减
self.x += self.vx
self.y += self.vy
# 简易重力模拟
self.vy += 0.1
self.lifespan -= 1
if self.lifespan <= 0:
self.alive = False
def draw(self, surface):
# 根据寿命来控制透明度(可选)
# 让粒子在即将消失时逐渐变淡
alpha = max(0, int(255 * (self.lifespan / 80)))
color_with_alpha = (*self.color, alpha)
# 在 Pygame 中可用 Surface.set_alpha 或者直接 draw circle
# 这里简单处理:画一个小圆点
pygame.draw.circle(surface, color_with_alpha[:3],
(int(self.x), int(self.y)), 3)
class Firework:
"""
烟花类:先有一个火箭,一定时间后爆炸成一堆粒子。
"""
def __init__(self):
# 随机初始位置(地面附近)
self.x = random.randint(100, WIDTH - 100)
self.y = HEIGHT
# 向上发射的速度
self.vx = random.uniform(-1, 1)
self.vy = random.uniform(-6, -8)
# 随机生成一种颜色 (较亮)
self.color = (random.randint(128, 255),
random.randint(128, 255),
random.randint(128, 255))
# 初始就有一个火箭粒子
self.rocket = Particle(self.x, self.y, self.vx, self.vy, self.color, lifespan=60)
self.exploded = False
self.particles = []
def update(self):
if not self.exploded:
# 如果火箭还没爆炸,则更新火箭位置
self.rocket.update()
# 判断是否满足爆炸条件
# 1) 火箭速度向下(vy > 0)表明到达顶点
# 2) 或者火箭寿命到头
if self.rocket.vy > 0 or self.rocket.lifespan < 20:
self.explode()
else:
# 如果已经爆炸,就更新所有爆炸粒子
for p in self.particles:
p.update()
# 清理已死亡的粒子,防止列表无限增长
self.particles = [p for p in self.particles if p.alive]
def explode(self):
""" 火箭爆炸时,生成一群粒子。 """
self.exploded = True
# 在爆炸位置生成若干粒子
num_particles = random.randint(20, 40)
for _ in range(num_particles):
# 随机方向和速度
angle = random.uniform(0, 2 * math.pi)
speed = random.uniform(2, 5)
vx = speed * math.cos(angle)
vy = speed * math.sin(angle) - 2 # 适当给点向上的初速度
# 粒子使用与火箭相同的颜色,或你也可以随机色
p = Particle(self.rocket.x, self.rocket.y, vx, vy, self.color,
lifespan=random.randint(50, 80))
self.particles.append(p)
def draw(self, surface):
# 如果火箭还在且没爆炸,画火箭
if not self.exploded and self.rocket.alive:
self.rocket.draw(surface)
else:
# 画爆炸后的粒子
for p in self.particles:
p.draw(surface)
@property
def is_alive(self):
# 烟花是否还存在?只有当火箭和所有粒子都死亡时才算结束
if not self.exploded:
return self.rocket.alive
else:
return any(p.alive for p in self.particles)
def main():
fireworks = []
running = True
while running:
clock.tick(30) # 控制帧率
screen.fill(BLACK)
# 1) 处理退出事件
for event in pygame.event.get():
if event.type == pygame.QUIT:
running = False
# 2) 随机产生新的烟花
if random.random() < 0.03: # 3% 概率每帧生成一枚新的烟花
fireworks.append(Firework())
# 3) 更新并绘制所有烟花
for fw in fireworks:
fw.update()
fw.draw(screen)
# 4) 清理已经完全结束的烟花
fireworks = [fw for fw in fireworks if fw.is_alive]
pygame.display.flip()
pygame.quit()
if __name__ == "__main__":
main()
代码说明
-
Particle 粒子类
x, y
:粒子的当前位置。vx, vy
:粒子的速度,vy
会在每帧被加上一定的重力。lifespan
:粒子存活时间,每帧递减,到 0 则alive = False
。update()
:更新粒子状态,包含位移、重力、寿命衰减。draw()
:在屏幕上以小圆点的形式绘制自己,使用pygame.draw.circle()
。
-
Firework 烟花类
- 包含一个“火箭”粒子(在
exploded=False
时代表飞向空中的火箭),和一组“爆炸粒子”列表。 update()
:- 若未爆炸,则更新火箭位置,判断爆炸条件(例如火箭已到最高点或寿命耗尽),然后进入爆炸函数。
- 若已爆炸,则更新所有爆炸粒子的位置、速度等。
explode()
:生成一定数量的粒子来模拟四散的火花,给它们随机的速度与寿命。draw()
:区分是否已经爆炸,决定画火箭还是画分散的粒子。is_alive
:只要火箭或爆炸粒子还在活动,就算烟花存活。
- 包含一个“火箭”粒子(在
-
主循环 (
main()
)fireworks
列表里存放现有的烟花对象。if random.random() < 0.03:
:每帧有 3% 的概率产生一个新的烟花,从而在整个动画过程中,烟花会不断随机出现。- 每一帧都要先 更新 所有烟花 (
fw.update()
),再 绘制 (fw.draw(screen)
)。 - 用
pygame.display.flip()
将绘制内容显示到窗口上。 - 用一个列表推导式
fireworks = [fw for fw in fireworks if fw.is_alive]
清理已经彻底消失的烟花。 - 通过
clock.tick(30)
将动画限制在 30 帧左右,流畅且不会占用太高 CPU。
5. 运行与效果
- 运行
- 将以上代码保存为
fireworks.py
,然后在命令行执行python fireworks.py
(或双击运行)。
- 将以上代码保存为
- 效果
- 程序启动后,会在 800×600 的窗口中随机产生烟花。每次烟花先从地面发射、上升到一定高度后爆炸,并生成彩色粒子四散而下。随后又会随机出现新的烟花,循环往复,直到你关闭窗口。
如果你想更“热闹”一些,可以将 if random.random() < 0.03:
改大,比如 0.1
,则产生烟花的频率会显著增加。
6. 参数调优与扩展
- 调整粒子数、速度和寿命
- 在
Firework.explode()
中的num_particles
可以让烟花更密集或稀疏; (vx, vy)
的范围和lifespan
会影响火花飞溅范围和消散速度。
- 在
- 添加随机颜色
- 目前的设计是火箭与爆炸粒子使用同色,可以改为每个粒子都生成一个随机色,让烟花更加绚丽。
- 增添交互
- 可以让玩家按空格或鼠标点击来触发一朵烟花,增加游戏化的体验。
- 添加更多物理效果
- 阻力、风力、空气阻力等等,能使烟花运动路径更真实。
- 背景音乐或音效
- Pygame 可以加载并播放音效,为烟花添加绽放的“砰”声,为你的新年更添气氛。
7. 总结
使用 Pygame 实现的烟花动画,能更直观地体验到火箭发射、空中爆炸和粒子下落的过程。通过粒子系统的思想,我们可以在游戏或动画中模拟诸如火花、尘埃、雨雪、爆炸等特效。在以上示例的基础上,你还可以扩展更多的特效和细节,打造一场绚丽多彩的新年烟花秀。
如果你对更复杂的 2D/3D 动画或游戏开发感兴趣,Pygame 也是一个很好的起点。相信随着你对粒子系统和游戏循环原理的不断理解,你能在趣味编程的道路上更进一步。
最后,祝大家新年快乐、编程顺利、烟花绚烂!