在这篇博客中,我们将深入剖析一个使用 Python 的 Pygame 库实现的新年烟花效果代码。这个代码通过创建烟花和粒子对象,模拟了烟花在夜空中绽放的绚丽场景。接下来,我们会分段解释每个函数的用法,同时指出哪些参数可以改动以及改动后会产生什么样的效果。
先来看看整体代码。
import pygame
import random
import math
# 初始化pygame
pygame.init()
# 设置窗口大小
WIDTH, HEIGHT = 800, 600
screen = pygame.display.set_mode((WIDTH, HEIGHT))
pygame.display.set_caption("新年烟花")
# 定义颜色列表
COLORS = [
(255, 0, 0), (0, 255, 0), (0, 0, 255),
(255, 255, 0), (255, 0, 255), (0, 255, 255),
(255, 128, 0), (128, 0, 255), (0, 128, 255),
(255, 20, 147), (50, 205, 50), (218, 165, 32)
]
# 尝试加载底图
try:
background = pygame.image.load(r"C:\Users\zh\Desktop\869155184d2b9b789c89e244b492cbf9_00003.jpg") # 请替换为你自己的底图文件名
background = pygame.transform.scale(background, (WIDTH, HEIGHT))
except pygame.error:
print("底图加载失败,将使用黑色背景。")
background = None
# 设置文字,使用支持中文的字体
font = pygame.font.Font(None, 60) # 确保 simhei.ttf 存在,或者指定完整路径
text = font.render("2025 Happy New Year!", True, (255, 255, 255))
text_rect = text.get_rect(center=(WIDTH // 2, HEIGHT // 2))
class Particle:
def __init__(self, x, y, color, speed, angle, life_decay=0.1):
self.x = x
self.y = y
self.color = color
self.speed = speed
self.angle = angle
self.lifetime = random.randint(20, 40)
self.life_decay = life_decay
self.size = random.randint(1, 3) # 随机粒子大小
self.remove = False
def update(self):
self.x += self.speed * math.cos(self.angle)
self.y += self.speed * math.sin(self.angle)
self.speed *= 0.9
self.lifetime -= self.life_decay
if self.lifetime <= 0 or self.x < 0 or self.x > WIDTH or self.y < 0 or self.y > HEIGHT:
self.remove = True
def draw(self, screen):
if self.lifetime > 0:
alpha = int((self.lifetime / 40) * 255)
s = pygame.Surface((self.size * 2, self.size * 2), pygame.SRCALPHA)
pygame.draw.circle(s, (*self.color, alpha), (self.size, self.size), self.size)
screen.blit(s, (int(self.x - self.size), int(self.y - self.size)))
class Firework:
def __init__(self):
self.x = random.randint(0, WIDTH)
self.y = HEIGHT
self.color = random.choice(COLORS)
self.speed = random.randint(8, 12)
self.exploded = False
self.particles = []
self.trail = []
self.trail_length = 10
self.remove = False
def update(self):
if not self.exploded:
self.trail.append((self.x, self.y))
if len(self.trail) > self.trail_length:
self.trail.pop(0)
self.y -= self.speed
self.speed -= 0.1
if self.speed <= 0:
self.explode()
elif self.y < 0:
self.remove = True
else:
for particle in self.particles[:]:
particle.update()
if particle.remove:
self.particles.remove(particle)
if len(self.particles) == 0:
self.remove = True
def explode(self):
self.exploded = True
num_particles = random.randint(150, 250)
# 随机决定是花瓣状还是圆形
is_petal = random.random() < 0.5
if is_petal:
num_petals = random.randint(3, 48) # 随机花瓣数量
petal_angle = 2 * math.pi / num_petals
for i in range(num_particles):
petal_index = i % num_petals
base_angle = petal_index * petal_angle
angle_variation = random.uniform(-math.pi / 12, math.pi / 12)
angle = base_angle + angle_variation
speed = random.uniform(1, 5)
color = random.choice(COLORS)
particle = Particle(self.x, self.y, color, speed, angle)
self.particles.append(particle)
else:
for _ in range(num_particles):
angle = random.uniform(0, 2 * math.pi)
speed = random.uniform(1, 5)
color = random.choice(COLORS)
particle = Particle(self.x, self.y, color, speed, angle)
self.particles.append(particle)
def draw(self, screen):
if not self.exploded:
for i, (tx, ty) in enumerate(self.trail):
alpha = int((i + 1) / len(self.trail) * 255)
s = pygame.Surface((2, 2), pygame.SRCALPHA)
pygame.draw.circle(s, (*self.color, alpha), (1, 1), 1)
screen.blit(s, (int(tx - 1), int(ty - 1)))
pygame.draw.circle(screen, self.color, (int(self.x), int(self.y)), 2)
else:
for particle in self.particles:
particle.draw(screen)
# 创建烟花列表
fireworks = []
clock = pygame.time.Clock()
running = True
while running:
for event in pygame.event.get():
if event.type == pygame.QUIT:
running = False
# 每帧有一定概率生成新的烟花
if random.random() < 0.04:
fireworks.append(Firework())
if background:
screen.blit(background, (0, 0))
else:
screen.fill((0, 0, 0))
screen.blit(text, text_rect) # 绘制文字
for firework in fireworks[:]:
firework.update()
firework.draw(screen)
if firework.remove:
fireworks.remove(firework)
pygame.display.flip()
clock.tick(60)
pygame.quit()
再来看看效果如何:
下面我会详细解释每一段代码的作用,以及各种参数的作用和更改之后的影响。
1. 导入库和初始化部分
import pygame
import random
import math
# 初始化pygame
pygame.init()
# 设置窗口大小
WIDTH, HEIGHT = 800, 600
screen = pygame.display.set_mode((WIDTH, HEIGHT))
pygame.display.set_caption("新年烟花")
代码解释
import pygame
、import random
和import math
:导入 Pygame 库用于图形绘制和动画处理,random
库用于生成随机数,math
库用于数学计算。pygame.init()
:初始化 Pygame 的所有模块,为后续的操作做好准备。WIDTH, HEIGHT = 800, 600
:定义窗口的宽度和高度。screen = pygame.display.set_mode((WIDTH, HEIGHT))
:创建一个指定大小的窗口,并返回一个用于绘制的Surface
对象。pygame.display.set_caption("新年烟花")
:设置窗口的标题为“新年烟花”。
可改动参数及效果
WIDTH
和HEIGHT
:修改窗口的大小。增大数值会使窗口变大,烟花和文字会在更大的区域内显示;减小数值则会使窗口变小。
2. 颜色列表和背景设置部分
# 定义颜色列表
COLORS = [
(255, 0, 0), (0, 255, 0), (0, 0, 255),
(255, 255, 0), (255, 0, 255), (0, 255, 255),
(255, 128, 0), (128, 0, 255), (0, 128, 255),
(255, 20, 147), (50, 205, 50), (218, 165, 32)
]
# 尝试加载底图
try:
background = pygame.image.load(r"C:\Users\zh\Desktop\869155184d2b9b789c89e244b492cbf9_00003.jpg") # 请替换为你自己的底图文件名
background = pygame.transform.scale(background, (WIDTH, HEIGHT))
except pygame.error:
print("底图加载失败,将使用黑色背景。")
background = None
代码解释
COLORS
:定义了一个包含多种 RGB 颜色元组的列表,用于随机选择烟花和粒子的颜色。pygame.image.load()
:尝试加载指定路径的图片作为背景图。pygame.transform.scale()
:将加载的图片缩放为窗口的大小。except
块:如果图片加载失败,会打印错误信息,并将background
设为None
,后续会使用黑色背景。
可改动参数及效果
COLORS
列表:可以添加、删除或修改颜色元组,从而改变烟花和粒子的颜色。pygame.image.load()
中的图片路径:更换为其他图片的路径,可改变窗口的背景图。
3. 文字设置部分
# 设置文字,使用支持中文的字体
font = pygame.font.Font(None, 60) # 确保 simhei.ttf 存在,或者指定完整路径
text = font.render("2025 Happy New Year!", True, (255, 255, 255))
text_rect = text.get_rect(center=(WIDTH // 2, HEIGHT // 2))
代码解释
pygame.font.Font(None, 60)
:创建一个字体对象,None
表示使用系统默认字体,60
是字体的大小。font.render()
:将指定的文字渲染为一个Surface
对象,True
表示开启抗锯齿,(255, 255, 255)
是文字的颜色(白色)。text.get_rect(center=(WIDTH // 2, HEIGHT // 2))
:获取文字的矩形区域,并将其中心位置设置为窗口的中心。
可改动参数及效果
None
:可以替换为具体的字体文件路径(如'simhei.ttf'
),以使用特定的字体。60
:修改字体的大小。"2025 Happy New Year!"
:修改显示的文字内容。(255, 255, 255)
:修改文字的颜色。
4. Particle
类部分
class Particle:
def __init__(self, x, y, color, speed, angle, life_decay=0.1):
self.x = x
self.y = y
self.color = color
self.speed = speed
self.angle = angle
self.lifetime = random.randint(20, 40)
self.life_decay = life_decay
self.size = random.randint(1, 3) # 随机粒子大小
self.remove = False
def update(self):
self.x += self.speed * math.cos(self.angle)
self.y += self.speed * math.sin(self.angle)
self.speed *= 0.9
self.lifetime -= self.life_decay
if self.lifetime <= 0 or self.x < 0 or self.x > WIDTH or self.y < 0 or self.y > HEIGHT:
self.remove = True
def draw(self, screen):
if self.lifetime > 0:
alpha = int((self.lifetime / 40) * 255)
s = pygame.Surface((self.size * 2, self.size * 2), pygame.SRCALPHA)
pygame.draw.circle(s, (*self.color, alpha), (self.size, self.size), self.size)
screen.blit(s, (int(self.x - self.size), int(self.y - self.size)))
代码解释
__init__
方法
- 初始化粒子的属性,包括位置
(x, y)
、颜色color
、速度speed
、角度angle
、生命周期lifetime
、生命周期衰减率life_decay
、大小size
以及是否移除的标志remove
。
update
方法
- 根据速度和角度更新粒子的位置。
- 速度按一定比例衰减。
- 生命周期按
life_decay
递减。 - 如果生命周期小于等于 0 或者粒子超出窗口范围,将
remove
标志设为True
,表示需要移除该粒子。
draw
方法
- 如果粒子的生命周期大于 0,计算其透明度
alpha
。 - 创建一个透明的
Surface
对象,并在其上绘制一个圆形表示粒子。 - 将该
Surface
对象绘制到屏幕上。
可改动参数及效果
life_decay
:修改粒子生命周期的衰减率。增大该值,粒子会更快地消失;减小该值,粒子会存在更长时间。random.randint(20, 40)
:修改粒子的初始生命周期范围。增大范围,粒子会存在更长时间;减小范围,粒子会更快消失。random.randint(1, 3)
:修改粒子的大小范围。增大范围,粒子会更大;减小范围,粒子会更小。
5. Firework
类部分
class Firework:
def __init__(self):
self.x = random.randint(0, WIDTH)
self.y = HEIGHT
self.color = random.choice(COLORS)
self.speed = random.randint(8, 12)
self.exploded = False
self.particles = []
self.trail = []
self.trail_length = 10
self.remove = False
def update(self):
if not self.exploded:
self.trail.append((self.x, self.y))
if len(self.trail) > self.trail_length:
self.trail.pop(0)
self.y -= self.speed
self.speed -= 0.1
if self.speed <= 0:
self.explode()
elif self.y < 0:
self.remove = True
else:
for particle in self.particles[:]:
particle.update()
if particle.remove:
self.particles.remove(particle)
if len(self.particles) == 0:
self.remove = True
def explode(self):
self.exploded = True
num_particles = random.randint(150, 250)
# 随机决定是花瓣状还是圆形
is_petal = random.random() < 0.5
if is_petal:
num_petals = random.randint(3, 48) # 随机花瓣数量
petal_angle = 2 * math.pi / num_petals
for i in range(num_particles):
petal_index = i % num_petals
base_angle = petal_index * petal_angle
angle_variation = random.uniform(-math.pi / 12, math.pi / 12)
angle = base_angle + angle_variation
speed = random.uniform(1, 5)
color = random.choice(COLORS)
particle = Particle(self.x, self.y, color, speed, angle)
self.particles.append(particle)
else:
for _ in range(num_particles):
angle = random.uniform(0, 2 * math.pi)
speed = random.uniform(1, 5)
color = random.choice(COLORS)
particle = Particle(self.x, self.y, color, speed, angle)
self.particles.append(particle)
def draw(self, screen):
if not self.exploded:
for i, (tx, ty) in enumerate(self.trail):
alpha = int((i + 1) / len(self.trail) * 255)
s = pygame.Surface((2, 2), pygame.SRCALPHA)
pygame.draw.circle(s, (*self.color, alpha), (1, 1), 1)
screen.blit(s, (int(tx - 1), int(ty - 1)))
pygame.draw.circle(screen, self.color, (int(self.x), int(self.y)), 2)
else:
for particle in self.particles:
particle.draw(screen)
代码解释
__init__
方法
- 初始化烟花的属性,包括位置
(x, y)
、颜色color
、速度speed
、是否爆炸的标志exploded
、粒子列表particles
、轨迹列表trail
、轨迹长度trail_length
以及是否移除的标志remove
。
update
方法
- 如果烟花还未爆炸,更新其位置和速度,添加轨迹点,并根据速度判断是否爆炸。如果烟花超出窗口顶部,将
remove
标志设为True
。 - 如果烟花已经爆炸,更新每个粒子的状态,并移除生命周期结束的粒子。如果所有粒子都已移除,将
remove
标志设为True
。
explode
方法
- 将
exploded
标志设为True
,表示烟花爆炸。 - 随机生成一定数量的粒子(150 - 250 个)。
- 随机决定烟花爆炸的形状是花瓣状还是圆形,并根据不同形状生成相应的粒子。
draw
方法
- 如果烟花还未爆炸,绘制烟花的轨迹和烟花本身。
- 如果烟花已经爆炸,绘制所有的粒子。
可改动参数及效果
random.randint(8, 12)
:修改烟花上升的初始速度范围。增大范围,烟花会上升得更快;减小范围,烟花会上升得更慢。trail_length
:修改烟花轨迹的长度。增大该值,轨迹会更长;减小该值,轨迹会更短。random.randint(150, 250)
:修改烟花爆炸时生成的粒子数量范围。增大范围,烟花爆炸会更绚丽;减小范围,烟花爆炸会相对暗淡。random.randint(3, 48)
:在花瓣状爆炸时,修改花瓣的数量范围。增大范围,花瓣会更多;减小范围,花瓣会更少。
6. 主循环部分
# 创建烟花列表
fireworks = []
clock = pygame.time.Clock()
running = True
while running:
for event in pygame.event.get():
if event.type == pygame.QUIT:
running = False
# 每帧有一定概率生成新的烟花
if random.random() < 0.04:
fireworks.append(Firework())
if background:
screen.blit(background, (0, 0))
else:
screen.fill((0, 0, 0))
screen.blit(text, text_rect) # 绘制文字
for firework in fireworks[:]:
firework.update()
firework.draw(screen)
if firework.remove:
fireworks.remove(firework)
pygame.display.flip()
clock.tick(60)
pygame.quit()
代码解释
fireworks = []
:创建一个空列表,用于存储所有的烟花对象。clock = pygame.time.Clock()
:创建一个时钟对象,用于控制游戏的帧率。while running
循环:主循环,不断更新和绘制画面,直到用户关闭窗口。pygame.event.get()
:获取所有的事件,检查是否有用户关闭窗口的事件。if random.random() < 0.04
:每帧有 4% 的概率生成一个新的烟花。screen.blit(background, (0, 0))
或screen.fill((0, 0, 0))
:绘制背景图或填充黑色背景。screen.blit(text, text_rect)
:绘制文字。- 遍历
fireworks
列表,更新和绘制每个烟花,并移除需要移除的烟花。 pygame.display.flip()
:更新整个屏幕的显示。clock.tick(60)
:控制游戏的帧率为 60 帧每秒。pygame.quit()
:退出 Pygame。
可改动参数及效果
0.04
:修改生成新烟花的概率。增大该值,会更频繁地生成新烟花;减小该值,生成新烟花的频率会降低。60
:修改游戏的帧率。增大该值,画面会更流畅,但可能会增加 CPU 负担;减小该值,画面会变得卡顿。
如果你没有合适的背景,可以用下面的背景来欣赏这一场电子烟花秀。
通过以上的详细解释,相信你对这个新年烟花代码有了更深入的理解。你可以根据自己的需求修改参数,创造出独一无二的烟花效果。快去试试吧!