除了利用HTML、CSS外,Python也可以实现相同的烟花效果。
首先通过以下命令下载依赖:
pip install pygame
接着创建一个Firework.py文件,可自己命名。
导入项目需要的依赖
import pygame as pg
import random as ra
import math
对pygame进行初始化,设置展示窗口的相关信息
# 初始化pygame
pg.init()
# 设置窗口标题
pg.display.set_caption("🎇")
# 获取窗口信息
winScreen = pg.display.Info()
# 获取屏幕宽度
screenWidth = winScreen.current_w
# 获取屏幕高度
screenHeight = winScreen.current_h
# 创建向量
vector = pg.math.Vector2
# 创建颜色列表
trail_colors = [(45, 45, 45), (60, 60, 60), (75, 75, 75), (125, 125, 125), (150, 150, 150)]
1. pg.init():初始化pygame库,这是使用pygame库的第一步。
2. pg.display.set_caption("🎇"):设置窗口标题为"🎇"。
3. winScreen = pg.display.Info():获取窗口信息,并将其存储在`winScreen`变量中。
4. screenWidth = winScreen.current_w:获取屏幕宽度,并将其存储在`screenWidth`变量中。
5. screenHeight = winScreen.current_h:获取屏幕高度,并将其存储在`screenHeight`变量中。
6. vector = pg.math.Vector2:创建一个向量类,用于处理二维向量。
7. trail_colors = [(45, 45, 45), (60, 60, 60), (75, 75, 75), (125, 125, 125), (150, 150, 150)]:创建一个颜色列表,用于存储不同颜色的RGB值。
编写烟花类Firework,对烟花信息进行配置。
class Firework:
def __init__(self):
# 随机生成颜色
self.colour = (ra.randint(0, 255), ra.randint(0, 255), ra.randint(0, 255))
# 随机生成三种颜色
self.colours = (
(ra.randint(0, 255), ra.randint(0, 255), ra.randint(0, 255)),
(ra.randint(0, 255), ra.randint(0, 255), ra.randint(0, 255)),
(ra.randint(0, 255), ra.randint(0, 255), ra.randint(0, 255))
)
# 生成一个表示发射出的火花的粒子对象
self.firework = Particle(ra.randint(0, screenWidth), screenHeight, True, self.colour)
# 初始化爆炸状态为 False
self.exploded = False
self.particles = []
# 爆炸产生的粒子数量范围
self.min_max_particles = vector(666, 999)
def update(self, win):
g = vector(0, ra.uniform(0.15, 0.4))
if not self.exploded:
# 给发射出的火花施加重力
self.firework.apply_force(g)
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(g.x + ra.uniform(-1, 1) / 20, g.y / 2 + (ra.randint(1, 8) / 100)))
particle.move()
for t in particle.trails:
t.show(win)
particle.show(win)
def explode(self):
amount = ra.randint(int(self.min_max_particles.x), int(self.min_max_particles.y))
for i in range(amount):
# 在爆炸位置生成粒子对象并添加到粒子列表中
self.particles.append(Particle(self.firework.pos.x, self.firework.pos.y, False, self.colours))
def show(self, win):
# 绘制发射出的火花
pg.draw.circle(win, self.colour, (int(self.firework.pos.x), int(self.firework.pos.y)), self.firework.size)
def remove(self):
if self.exploded:
for p in self.particles:
if p.remove is True:
self.particles.remove(p)
if len(self.particles) == 0:
return True
else:
return False
Firework类中包含了初始化方法__init__,更新方法update,爆炸方法explode,显示方法show以及移除方法remove。
__init__()方法用于初始化烟花对象的基本属性,如颜色、爆炸状态等。同时,它还生成了一个表示发射出的火花的粒子对象self.firework,并将其添加到烟花对象的粒子列表self.particles中。
update()方法用于更新烟花对象的状态。如果烟花尚未爆炸,它会给发射出的火花施加重力,并移动火花的位置。如果烟花已经爆炸,它会更新爆炸产生的粒子的状态,并移动粒子的位置。
explode()方法用于模拟烟花爆炸的过程。它会随机生成一定数量的粒子对象,并将其添加到烟花对象的粒子列表self.particles中。
show()方法用于在屏幕上绘制烟花对象。如果烟花尚未爆炸,它会绘制发射出的火花;如果烟花已经爆炸,它会绘制爆炸产生的粒子。
remove()方法用于判断烟花对象是否需要被移除。如果烟花已经爆炸,并且所有粒子都被移除了,那么烟花对象就可以被移除了。
由于使用了随机数生成,每次运行结果可能会有所不同。
烟花爆炸效果类Particle
class Particle:
# 初始化函数,传入参数x,y,firework,colour
def __init__(self, x, y, firework, colour):
# 初始化firework属性
self.firework = firework
# 初始化位置属性
self.pos = vector(x, y)
# 初始化原点属性
self.origin = vector(x, y)
# 初始化半径属性
self.radius = 25
# 初始化remove属性
self.remove = False
# 初始化爆炸半径属性
self.explosion_radius = ra.randint(15, 25)
# 初始化生命属性
self.life = 0
# 初始化加速度属性
self.acc = vector(0, 0)
# 初始化 trails 属性
self.trails = []
# 初始化prev_posx属性
self.prev_posx = [-10] * 10
# 初始化prev_posy属性
self.prev_posy = [-10] * 10
# 如果firework属性为真
if self.firework:
# 初始化速度属性
self.vel = vector(0, -ra.randint(17, 20))
# 初始化大小属性
self.size = 5
# 初始化颜色属性
self.colour = colour
# 遍历5次
for i in range(5):
# 将Trail实例添加到trails属性中
self.trails.append(Trail(i, self.size, True))
# 如果firework属性为假
else:
# 初始化速度属性
self.vel = vector(ra.uniform(-1, 1), ra.uniform(-1, 1))
# 乘以随机数
self.vel.x *= ra.randint(7, self.explosion_radius + 2)
self.vel.y *= ra.randint(7, self.explosion_radius + 2)
# 初始化大小属性
self.size = ra.randint(2, 4)
# 从colour属性中随机选择颜色
self.colour = ra.choice(colour)
# 遍历5次
for i in range(5):
# 将Trail实例添加到trails属性中
self.trails.append(Trail(i, self.size, False))
def apply_force(self, force):
# 施加力
self.acc += force
def move(self):
if not self.firework:
# 爆炸产生的粒子减速
self.vel.x *= 0.8
self.vel.y *= 0.8
self.vel += self.acc
self.pos += self.vel
self.acc *= 0
if self.life == 0 and not self.firework:
# 判断是否超出爆炸半径
distance = math.sqrt((self.pos.x - self.origin.x) ** 2 + (self.pos.y - self.origin.y) ** 2)
if distance > self.explosion_radius:
self.remove = True
self.decay()
self.trail_update()
self.life += 1
def show(self, win):
# 绘制粒子
pg.draw.circle(win, (self.colour[0], self.colour[1], self.colour[2], 0), (int(self.pos.x), int(self.pos.y)),
self.size)
def decay(self):
if 50 > self.life > 10:
ran = ra.randint(0, 30)
if ran == 0:
self.remove = True
elif self.life > 50:
ran = ra.randint(0, 5)
if ran == 0:
self.remove = True
# 定义一个函数trail_update,用于更新轨迹
def trail_update(self):
# 将当前位置的x坐标从列表中弹出,并插入到列表的第一个位置
self.prev_posx.pop()
self.prev_posx.insert(0, int(self.pos.x))
# 将当前位置的y坐标从列表中弹出,并插入到列表的第一个位置
self.prev_posy.pop()
self.prev_posy.insert(0, int(self.pos.y))
# 遍历轨迹列表
for n, t in enumerate(self.trails):
# 如果轨迹是动态的,则获取下一个位置的坐标
if t.dynamic:
t.get_pos(self.prev_posx[n + 1], self.prev_posy[n + 1])
# 如果轨迹是静态的,则获取下一个位置的坐标
else:
t.get_pos(self.prev_posx[n + 5], self.prev_posy[n + 5])
这段代码定义了一个名为Particle的类,用于表示烟花模拟中的粒子。粒子可以分为两种类型:一种是烟花爆炸产生的粒子,另一种是烟花爆炸后产生的粒子。
Particle类的__init__方法用于初始化粒子的属性,包括firework、pos、origin、radius、remove、explosion_radius、life、acc、trails、prev_posx和prev_posy。其中,firework表示粒子是否属于烟花爆炸产生的粒子,pos表示粒子的当前位置,origin表示粒子的原点位置,radius表示粒子的半径,remove表示粒子是否应该被移除,explosion_radius表示烟花爆炸的半径,life表示粒子的生命周期,acc表示粒子的加速度,trails表示粒子的轨迹列表,prev_posx和prev_posy分别表示粒子的前10个位置的x和y坐标。
Particle类还定义了apply_force、move、show和decay方法,用于施加力、移动粒子、绘制粒子和衰变粒子。其中,apply_force方法用于施加力,move方法用于移动粒子,show方法用于绘制粒子,decay方法用于衰变粒子。
trail_update方法用于更新粒子的轨迹。如果轨迹是动态的,则获取下一个位置的坐标;如果轨迹是静态的,则获取下一个位置的坐标。
痕迹类Trail :
class Trail:
# 初始化函数,参数n表示第n个元素,size表示元素的大小,dynamic表示元素是否动态
def __init__(self, n, size, dynamic):
# 记录元素在行中的位置
self.pos_in_line = n
# 记录元素的坐标
self.pos = vector(-10, -10)
# 记录元素是否动态
self.dynamic = dynamic
# 如果元素是动态的
if self.dynamic:
# 记录元素的颜色
self.colour = trail_colors[n]
# 记录元素的大小
self.size = int(size - n / 2)
# 如果元素是静态的
else:
# 记录元素的默认颜色
self.colour = (255, 255, 200)
# 记录元素的大小
self.size = size - 2
# 如果元素的大小小于0,则设置元素的大小为0
if self.size < 0:
self.size = 0
def get_pos(self, x, y):
self.pos = vector(x, y)
def show(self, win):
# 绘制痕迹
pg.draw.circle(win, self.colour, (int(self.pos.x), int(self.pos.y)), self.size)
这段代码定义了一个名为`Trail`的类,用于表示游戏中的痕迹。痕迹可以是动态的(如子弹的轨迹),也可以是静态的(如玩家移动的脚印)。
`__init__`函数是类的初始化函数,用于初始化痕迹的属性。它接受三个参数:`n`表示第n个元素,`size`表示元素的大小,`dynamic`表示元素是否动态。根据这些参数,它可以设置痕迹的颜色、大小和位置。
`get_pos`函数用于更新痕迹的坐标。它接受两个参数`x`和`y`,表示新的坐标。
`show`函数用于在游戏窗口中绘制痕迹。它接受一个参数`win`,表示游戏窗口。绘制痕迹时,会使用`pg.draw.circle`函数绘制一个圆形,圆形的位置和颜色由`self.pos`和`self.colour`决定,半径由`self.size`决定。
展示函数fire:
def fire():
# 设置屏幕大小
screen = pg.display.set_mode((screenWidth, screenHeight - 66))
# 设置时钟
clock = pg.time.Clock()
# 创建烟花列表
fireworks = [Firework() for _ in range(2)]
# 设置运行状态
running = True
# 加载字体
font = pg.font.SysFont("comicsansms", 99)
# 渲染文本
# 展示文本
text = ""
text_color = (255, 190, 200) # 字体颜色
rendered_text = font.render(text, True, text_color)
# 循环
while running:
# 设置帧数
clock.tick(99)
# 获取事件
for event in pg.event.get():
if event.type == pg.QUIT:
running = False
# 计算文本位置
text_width = rendered_text.get_width()
text_height = rendered_text.get_height()
text_x = (screenWidth - text_width) // 2
text_y = (screenHeight - text_height) // 2 - 99
# 设置背景色
screen.fill((20, 20, 30))
# 绘制文本
screen.blit(rendered_text, (text_x, text_y))
# 随机添加烟花
if ra.randint(0, 10) == 1:
fireworks.append(Firework())
# 更新烟花
update(screen, fireworks)
# 退出
pg.quit()
quit()
完整代码如下:
import pygame as pg
import random as ra
import math
# 初始化pygame
pg.init()
# 设置窗口标题
pg.display.set_caption("🎇")
# 获取窗口信息
winScreen = pg.display.Info()
# 获取屏幕宽度
screenWidth = winScreen.current_w
# 获取屏幕高度
screenHeight = winScreen.current_h
# 创建向量
vector = pg.math.Vector2
# 创建颜色列表
trail_colors = [(45, 45, 45), (60, 60, 60), (75, 75, 75), (125, 125, 125), (150, 150, 150)]
# 烟花类
class Firework:
def __init__(self):
# 随机生成颜色
self.colour = (ra.randint(0, 255), ra.randint(0, 255), ra.randint(0, 255))
# 随机生成三种颜色
self.colours = (
(ra.randint(0, 255), ra.randint(0, 255), ra.randint(0, 255)),
(ra.randint(0, 255), ra.randint(0, 255), ra.randint(0, 255)),
(ra.randint(0, 255), ra.randint(0, 255), ra.randint(0, 255))
)
# 生成一个表示发射出的火花的粒子对象
self.firework = Particle(ra.randint(0, screenWidth), screenHeight, True, self.colour)
# 初始化爆炸状态为 False
self.exploded = False
self.particles = []
# 爆炸产生的粒子数量范围
self.min_max_particles = vector(666, 999)
def update(self, win):
g = vector(0, ra.uniform(0.15, 0.4))
if not self.exploded:
# 给发射出的火花施加重力
self.firework.apply_force(g)
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(g.x + ra.uniform(-1, 1) / 20, g.y / 2 + (ra.randint(1, 8) / 100)))
particle.move()
for t in particle.trails:
t.show(win)
particle.show(win)
def explode(self):
amount = ra.randint(int(self.min_max_particles.x), int(self.min_max_particles.y))
for i in range(amount):
# 在爆炸位置生成粒子对象并添加到粒子列表中
self.particles.append(Particle(self.firework.pos.x, self.firework.pos.y, False, self.colours))
def show(self, win):
# 绘制发射出的火花
pg.draw.circle(win, self.colour, (int(self.firework.pos.x), int(self.firework.pos.y)), self.firework.size)
def remove(self):
if self.exploded:
for p in self.particles:
if p.remove is True:
self.particles.remove(p)
if len(self.particles) == 0:
return True
else:
return False
# 粒子类
class Particle:
# 初始化函数,传入参数x,y,firework,colour
def __init__(self, x, y, firework, colour):
# 初始化firework属性
self.firework = firework
# 初始化位置属性
self.pos = vector(x, y)
# 初始化原点属性
self.origin = vector(x, y)
# 初始化半径属性
self.radius = 25
# 初始化remove属性
self.remove = False
# 初始化爆炸半径属性
self.explosion_radius = ra.randint(15, 25)
# 初始化生命属性
self.life = 0
# 初始化加速度属性
self.acc = vector(0, 0)
# 初始化 trails 属性
self.trails = []
# 初始化prev_posx属性
self.prev_posx = [-10] * 10
# 初始化prev_posy属性
self.prev_posy = [-10] * 10
# 如果firework属性为真
if self.firework:
# 初始化速度属性
self.vel = vector(0, -ra.randint(17, 20))
# 初始化大小属性
self.size = 5
# 初始化颜色属性
self.colour = colour
# 遍历5次
for i in range(5):
# 将Trail实例添加到trails属性中
self.trails.append(Trail(i, self.size, True))
# 如果firework属性为假
else:
# 初始化速度属性
self.vel = vector(ra.uniform(-1, 1), ra.uniform(-1, 1))
# 乘以随机数
self.vel.x *= ra.randint(7, self.explosion_radius + 2)
self.vel.y *= ra.randint(7, self.explosion_radius + 2)
# 初始化大小属性
self.size = ra.randint(2, 4)
# 从colour属性中随机选择颜色
self.colour = ra.choice(colour)
# 遍历5次
for i in range(5):
# 将Trail实例添加到trails属性中
self.trails.append(Trail(i, self.size, False))
def apply_force(self, force):
# 施加力
self.acc += force
def move(self):
if not self.firework:
# 爆炸产生的粒子减速
self.vel.x *= 0.8
self.vel.y *= 0.8
self.vel += self.acc
self.pos += self.vel
self.acc *= 0
if self.life == 0 and not self.firework:
# 判断是否超出爆炸半径
distance = math.sqrt((self.pos.x - self.origin.x) ** 2 + (self.pos.y - self.origin.y) ** 2)
if distance > self.explosion_radius:
self.remove = True
self.decay()
self.trail_update()
self.life += 1
def show(self, win):
# 绘制粒子
pg.draw.circle(win, (self.colour[0], self.colour[1], self.colour[2], 0), (int(self.pos.x), int(self.pos.y)),
self.size)
def decay(self):
if 50 > self.life > 10:
ran = ra.randint(0, 30)
if ran == 0:
self.remove = True
elif self.life > 50:
ran = ra.randint(0, 5)
if ran == 0:
self.remove = True
# 定义一个函数trail_update,用于更新轨迹
def trail_update(self):
# 将当前位置的x坐标从列表中弹出,并插入到列表的第一个位置
self.prev_posx.pop()
self.prev_posx.insert(0, int(self.pos.x))
# 将当前位置的y坐标从列表中弹出,并插入到列表的第一个位置
self.prev_posy.pop()
self.prev_posy.insert(0, int(self.pos.y))
# 遍历轨迹列表
for n, t in enumerate(self.trails):
# 如果轨迹是动态的,则获取下一个位置的坐标
if t.dynamic:
t.get_pos(self.prev_posx[n + 1], self.prev_posy[n + 1])
# 如果轨迹是静态的,则获取下一个位置的坐标
else:
t.get_pos(self.prev_posx[n + 5], self.prev_posy[n + 5])
# 痕迹类
class Trail:
# 初始化函数,参数n表示第n个元素,size表示元素的大小,dynamic表示元素是否动态
def __init__(self, n, size, dynamic):
# 记录元素在行中的位置
self.pos_in_line = n
# 记录元素的坐标
self.pos = vector(-10, -10)
# 记录元素是否动态
self.dynamic = dynamic
# 如果元素是动态的
if self.dynamic:
# 记录元素的颜色
self.colour = trail_colors[n]
# 记录元素的大小
self.size = int(size - n / 2)
# 如果元素是静态的
else:
# 记录元素的默认颜色
self.colour = (255, 255, 200)
# 记录元素的大小
self.size = size - 2
# 如果元素的大小小于0,则设置元素的大小为0
if self.size < 0:
self.size = 0
def get_pos(self, x, y):
self.pos = vector(x, y)
def show(self, win):
# 绘制痕迹
pg.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)
# 更新窗口
pg.display.update()
# 定义fire函数
def fire():
# 设置屏幕大小
screen = pg.display.set_mode((screenWidth, screenHeight - 66))
# 设置时钟
clock = pg.time.Clock()
# 创建烟花列表
fireworks = [Firework() for _ in range(2)]
# 设置运行状态
running = True
# 加载字体
font = pg.font.SysFont("comicsansms", 99)
# 渲染文本
# 展示文本
text = ""
text_color = (255, 190, 200) # 字体颜色
rendered_text = font.render(text, True, text_color)
# 循环
while running:
# 设置帧数
clock.tick(99)
# 获取事件
for event in pg.event.get():
if event.type == pg.QUIT:
running = False
# 计算文本位置
text_width = rendered_text.get_width()
text_height = rendered_text.get_height()
text_x = (screenWidth - text_width) // 2
text_y = (screenHeight - text_height) // 2 - 99
# 设置背景色
screen.fill((20, 20, 30))
# 绘制文本
screen.blit(rendered_text, (text_x, text_y))
# 随机添加烟花
if ra.randint(0, 10) == 1:
fireworks.append(Firework())
# 更新烟花
update(screen, fireworks)
# 退出
pg.quit()
quit()
# 调试
if __name__ == "__main__":
fire()
效果如下: