Python实现烟花模拟器:从粒子系统到实时调度,揭秘数字烟花背后的技术逻辑 30款热门AI模型一站整合DeepSeek/GLM/Qwen 随心用限时 5 折。 点击领海量免费额度1. 这篇文章真正要解决的问题作为一名开发者你是否曾想过那些在夜空中绽放的、令人惊叹的烟花秀其背后的核心驱动力是什么是艺术家的灵感还是某种精密的控制系统当我在浏阳连续体验了两场截然不同的烟花盛宴后一个清晰的答案浮现出来现代烟花表演的本质是一场由软件、硬件和网络协议共同驱动的“实时分布式系统”演出。这篇文章要解决的正是技术人如何理解并动手实现一个简化版的“数字烟花”模拟器从而窥见大型烟花秀背后的技术逻辑。你可能会觉得烟花和编程毫无关系。但仔细想想一场大型烟花秀面临的挑战与一个高并发在线系统何其相似成千上万个发射点节点需要在毫秒级精度下被触发调度形成预定的图案业务逻辑并要应对风速、湿度等环境变量外部依赖。传统上这依赖于昂贵的专用硬件和封闭式控制系统。而现在我们可以用更通用的技术栈来模拟其核心思想。本文不会教你制作实体烟花那是化工和特种机械的领域而是将带你从开发者的视角出发使用Python和简单的网络通信构建一个可在电脑屏幕上运行的“烟花模拟与编排系统”。通过这个过程你将理解事件调度与定时如何精确控制多个“爆炸”事件在时间轴上的序列。粒子系统与物理模拟如何用程序模拟烟花爆炸后粒子的运动、衰减。状态同步与协调如果是多机模拟如何让不同节点协同工作。从数据到视觉如何将抽象的指令何时、何地、何种颜色转化为绚丽的画面。读完本文你将获得一套完整的、可运行的代码不仅能生成随机烟花更能通过编写“剧本”JSON配置文件来编排一场有节奏、有图案的迷你烟花秀。这对于学习游戏开发、动画原理、实时系统甚至物联网设备协同都是一个有趣且直观的切入点。2. 基础概念与核心原理烟花秀的“技术栈”在深入代码之前我们需要将一场烟花秀解构成几个可计算、可编程的技术模块。核心组件拆解发射单元对应程序中的一个“发射器”对象或函数。它负责在特定时间、于特定屏幕坐标或三维坐标生成一个初始的“主弹头”粒子。弹头与爆炸“主弹头”沿抛物线上升模拟物理在预定时间或高度“爆炸”。爆炸本身不是一个画面而是一个事件。这个事件会瞬间创建出数十上百个新的“子粒子”。粒子系统这是模拟烟花乃至许多自然现象火焰、烟雾、水流的核心技术。每个子粒子拥有独立的属性位置初始位置为爆炸中心。速度包含大小和方向决定了粒子飞散的轨迹。加速度通常模拟重力让粒子最终下落。生命周期从诞生到消失的时间通常伴随着颜色、大小的变化如亮起后衰减。颜色可以固定也可以随时间渐变。编排脚本一场秀不是随机乱放。我们需要一个“导演”或“剧本”。这可以是一个数据结构如列表其中每条指令定义了{“time”: 1.5, “x”: 300, “y”: 500, “color”: “red”, “type”: “peony”}。程序的核心调度器按时间顺序执行这些指令。渲染引擎将内存中的粒子状态位置、颜色每秒数十次地绘制到屏幕上。我们将使用Pygame这个轻量级的游戏库来完成。关键原理游戏循环与状态更新所有动态模拟都基于一个核心循环while running: # 1. 处理事件如退出、按键 # 2. 更新逻辑根据当前时间判断是否需要发射新烟花更新所有现存粒子的位置、速度、生命周期。 # 3. 渲染画面清空画布绘制所有粒子。 # 4. 控制帧率进入下一帧。每一帧粒子根据其速度更新位置速度根据重力加速度更新。生命周期递减当生命值为0时粒子被移除。这种基于帧的连续更新在视觉上形成了流畅的动画。3. 环境准备与前置条件我们将使用 Python 作为开发语言因为它语法简洁库生态丰富非常适合快速原型和可视化模拟。1. 基础环境操作系统Windows 10/11, macOS, 或 Linux 均可。Python 版本建议使用 Python 3.8 及以上版本。你可以通过命令行输入python --version或python3 --version来检查。2. 安装必备库我们主要依赖Pygame用于图形渲染和窗口管理。使用 pip 包管理器安装。 打开你的终端Windows 上是 CMD 或 PowerShellmacOS/Linux 上是 Terminal执行以下命令# 安装 Pygame pip install pygame # 如果你系统上有多个Python版本或者遇到权限问题可以尝试 pip3 install pygame # 或 python -m pip install pygame安装完成后可以通过运行python -m pygame.examples.aliens来测试是否安装成功会出现一个示例游戏窗口。3. 开发工具任何文本编辑器或 IDE 都可以例如 VS Code、PyCharm、Sublime Text甚至记事本。建议使用具备代码高亮和语法提示功能的编辑器。项目结构预览我们将创建以下文件fireworks_simulator/ ├── main.py # 主程序入口包含游戏循环和主要逻辑 ├── particle.py # 粒子类定义 ├── firework.py # 烟花发射器类定义 ├── launcher.py # 编排脚本加载与调度器 └── show_script.json # JSON格式的烟花表演脚本4. 核心流程拆解让我们把构建烟花模拟器的过程分解为五个清晰的步骤每一步都对应一个可验证的模块。步骤一定义粒子——烟火的最小单元目标是创建一个Particle类。它负责管理自己的位置、速度、颜色和生命周期。每一帧它根据物理规则更新自己的状态并提供一个方法供主程序绘制它。这是整个系统的基础。步骤二定义烟花——从发射到爆炸Firework类代表一次完整的烟花绽放。它包含两个主要阶段上升阶段一个作为“弹头”的粒子从发射点以一定初速度向上运动。爆炸阶段当弹头到达顶点或计时器触发时销毁弹头并在其位置生成一大批新的、随机的Particle对象形成花型。步骤三实现调度器——表演的导演Launcher或调度器是系统的大脑。它有两个核心职责加载剧本从一个外部文件如 JSON读取烟花发射指令列表。时序控制在主游戏循环中持续检查当前时间。当某条指令的预设发射时间到达时就创建一个对应的Firework对象并将其加入活动烟花列表。步骤四构建主循环——让世界运转起来在main.py中我们将创建 Pygame 窗口并建立经典的游戏循环。在循环中我们按顺序处理退出事件。调用调度器检查并发射新烟花。更新所有活跃烟花的状态包括其内部的粒子。在屏幕上绘制所有可见的粒子。控制循环速度保持帧率稳定。步骤五设计剧本——从代码到艺术最后我们将编写一个show_script.json文件。通过编辑这个文件你可以自由地设计一场烟花秀第一秒在左边放一个红色的第三秒在中间放一个蓝色的第五秒同时放三个形成图案……无需修改代码只需修改数据。5. 完整示例与代码实现现在让我们将上述步骤转化为具体的代码。请按照顺序创建以下文件。文件 1particle.py- 粒子类import pygame import random import math class Particle: def __init__(self, x, y, vx, vy, color, lifetime90, size3, gravity0.1, decay0.97): 初始化一个粒子。 :param x, y: 初始位置 :param vx, vy: 初始速度向量 :param color: 颜色格式为 (R, G, B) :param lifetime: 生命周期帧数 :param size: 粒子绘制大小 :param gravity: 重力加速度y轴正方向 :param decay: 速度衰减因子模拟空气阻力 self.x x self.y y self.vx vx self.vy vy self.color color self.initial_lifetime lifetime self.lifetime lifetime self.size size self.gravity gravity self.decay decay def update(self): 更新粒子状态位置、速度、生命周期。 # 应用速度衰减空气阻力 self.vx * self.decay self.vy * self.decay # 应用重力 self.vy self.gravity # 更新位置 self.x self.vx self.y self.vy # 生命周期减少 self.lifetime - 1 def is_alive(self): 判断粒子是否还‘活着’。 return self.lifetime 0 def draw(self, screen): 在给定的Pygame屏幕上绘制粒子。 # 根据生命周期计算透明度可选更逼真 alpha int(255 * (self.lifetime / self.initial_lifetime)) if alpha 0: alpha 0 # 创建一个临时surface来绘制带透明度的圆 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))) # 简单绘制方式无透明度 # pygame.draw.circle(screen, self.color, (int(self.x), int(self.y)), self.size)文件 2firework.py- 烟花类import random import math from particle import Particle class Firework: def __init__(self, x, y, color(255, 255, 255), explosion_heightNone, particle_count100): 初始化一个烟花。 :param x, y: 发射起始位置屏幕底部 :param color: 爆炸粒子的主色调 :param explosion_height: 爆炸高度y坐标None则为随机 :param particle_count: 爆炸产生的粒子数量 # 弹头上升阶段 self.rocket Particle(x, y, vxrandom.uniform(-0.5, 0.5), # 轻微随机水平速度 vyrandom.uniform(-12, -10), # 向上的初速度 colorcolor, lifetime1000, # 足够长直到爆炸 size2, gravity0.2, # 上升阶段重力 decay0.99) self.exploded False self.particles [] # 爆炸后的粒子列表 self.color color self.explosion_height explosion_height if explosion_height else random.randint(100, 300) self.particle_count particle_count def update(self): 更新烟花状态。 if not self.exploded: # 更新弹头 self.rocket.update() # 检查是否到达爆炸高度 if self.rocket.y self.explosion_height: self.explode() else: # 更新所有爆炸粒子并移除死亡的粒子 for particle in self.particles[:]: particle.update() if not particle.is_alive(): self.particles.remove(particle) def explode(self): 执行爆炸创建大量新粒子。 self.exploded True # 可以在这里释放弹头粒子或者直接忽略 for _ in range(self.particle_count): # 随机角度和速度形成圆形爆炸 angle random.uniform(0, math.pi * 2) speed random.uniform(2, 8) vx math.cos(angle) * speed vy math.sin(angle) * speed # 为粒子颜色添加一些随机性使爆炸更生动 color_variation ( min(255, max(0, self.color[0] random.randint(-30, 30))), min(255, max(0, self.color[1] random.randint(-30, 30))), min(255, max(0, self.color[2] random.randint(-30, 30))) ) self.particles.append( Particle(self.rocket.x, self.rocket.y, vx, vy, colorcolor_variation, lifetimerandom.randint(40, 80), # 粒子寿命不同 sizerandom.randint(2, 4), gravity0.15, decay0.96) ) def is_done(self): 判断烟花是否完全结束弹头已炸且所有粒子消失。 return self.exploded and len(self.particles) 0 def draw(self, screen): 绘制烟花弹头或爆炸粒子。 if not self.exploded: self.rocket.draw(screen) else: for particle in self.particles: particle.draw(screen)文件 3launcher.py- 调度器类import json import time from firework import Firework class Launcher: def __init__(self, script_fileshow_script.json): 初始化调度器。 :param script_file: 烟花表演脚本的JSON文件路径。 self.script self.load_script(script_file) self.fireworks [] # 当前活跃的烟花列表 self.start_time time.time() # 记录表演开始时间 self.script_index 0 # 当前执行到脚本的第几条指令 def load_script(self, filepath): 加载并解析JSON脚本。 try: with open(filepath, r) as f: data json.load(f) # 确保脚本是按时间排序的 data.sort(keylambda x: x.get(time, 0)) return data except FileNotFoundError: print(f警告脚本文件 {filepath} 未找到将使用空脚本。) return [] except json.JSONDecodeError: print(f错误脚本文件 {filepath} JSON格式错误。) return [] def update(self, current_time): 根据当前时间更新调度器状态发射该发射的烟花。 :param current_time: 从表演开始经过的秒数。 # 遍历脚本发射所有时间已到的烟花 while self.script_index len(self.script): cmd self.script[self.script_index] cmd_time cmd.get(time, 0) if current_time cmd_time: # 发射烟花 x cmd.get(x, 400) y cmd.get(y, 550) # 默认从屏幕底部附近发射 color cmd.get(color, (255, 255, 255)) # 颜色可能以字符串或列表形式提供 if isinstance(color, str): # 简单颜色映射可扩展 color_map { red: (255, 50, 50), green: (50, 255, 50), blue: (50, 100, 255), yellow: (255, 255, 50), purple: (200, 50, 200), white: (255, 255, 255) } color color_map.get(color.lower(), (255, 255, 255)) elif isinstance(color, list) and len(color) 3: color tuple(color) count cmd.get(count, 100) fw Firework(x, y, colorcolor, particle_countcount) self.fireworks.append(fw) self.script_index 1 else: # 时间未到跳出循环 break # 更新所有活跃烟花并移除已结束的 for fw in self.fireworks[:]: fw.update() if fw.is_done(): self.fireworks.remove(fw) def get_active_fireworks(self): 获取当前所有活跃的烟花对象。 return self.fireworks文件 4show_script.json- 表演剧本[ {time: 0.5, x: 200, y: 550, color: red, count: 120}, {time: 1.2, x: 400, y: 550, color: blue, count: 150}, {time: 1.9, x: 600, y: 550, color: green, count: 120}, {time: 3.0, x: 300, y: 550, color: yellow, count: 200}, {time: 3.0, x: 500, y: 550, color: purple, count: 200}, {time: 4.5, x: 400, y: 550, color: [255, 150, 50], count: 250}, {time: 6.0, x: 150, y: 550, color: white, count: 80}, {time: 6.2, x: 250, y: 550, color: white, count: 80}, {time: 6.4, x: 350, y: 550, color: white, count: 80}, {time: 6.6, x: 450, y: 550, color: white, count: 80}, {time: 6.8, x: 550, y: 550, color: white, count: 80}, {time: 7.0, x: 650, y: 550, color: white, count: 80} ]这个脚本定义了一场约7秒的表演先是左中右三发单色烟花然后是中间两发齐放接着是一发大型橙色烟花最后是一排白色烟花快速连续发射模拟“排炮”效果。文件 5main.py- 主程序入口import pygame import sys import time from launcher import Launcher # 初始化 Pygame pygame.init() # 屏幕尺寸 WIDTH, HEIGHT 800, 600 screen pygame.display.set_mode((WIDTH, HEIGHT)) pygame.display.set_caption(浏阳烟花模拟器 - 技术实现版) clock pygame.time.Clock() # 创建调度器加载脚本 launcher Launcher(show_script.json) program_start_time time.time() # 主游戏循环 running True while running: # 计算从程序开始到现在的“表演时间” current_show_time time.time() - program_start_time # 1. 处理事件 for event in pygame.event.get(): if event.type pygame.QUIT: running False elif event.type pygame.KEYDOWN: if event.key pygame.K_ESCAPE: running False # 按空格键可以重新开始表演可选功能 elif event.key pygame.K_SPACE: program_start_time time.time() launcher Launcher(show_script.json) # 重置调度器 # 2. 更新逻辑 launcher.update(current_show_time) # 调度器检查并发射烟花更新所有烟花状态 # 3. 渲染画面 screen.fill((5, 5, 20)) # 深蓝色背景模拟夜空 # 绘制所有活跃烟花 for fw in launcher.get_active_fireworks(): fw.draw(screen) # 显示当前时间和指令进度调试信息 font pygame.font.SysFont(None, 24) time_text font.render(fTime: {current_show_time:.2f}s, True, (200, 200, 200)) screen.blit(time_text, (10, 10)) script_progress font.render(fCommand: {launcher.script_index}/{len(launcher.script)}, True, (200, 200, 200)) screen.blit(script_progress, (10, 40)) pygame.display.flip() # 更新整个屏幕 # 4. 控制帧率 clock.tick(60) # 每秒60帧 pygame.quit() sys.exit()6. 运行结果与效果验证如何运行确保所有五个文件main.py,particle.py,firework.py,launcher.py,show_script.json保存在同一个文件夹下。打开终端导航到该文件夹。运行主程序python main.py或python3 main.py预期输出程序将打开一个 800x600 像素的窗口背景为深蓝色。你将看到约0.5秒后屏幕左侧x200发射一枚红色烟花升至一定高度后爆炸散开。随后中间和右侧依次发射蓝色和绿色烟花。第3秒时中间偏左和偏右同时发射两枚烟花。第4.5秒屏幕中心发射一枚大型橙色烟花。第6秒开始从左至右快速连续发射一排白色烟花。所有烟花粒子会遵循物理规则运动、衰减并最终消失。窗口左上角会显示当前表演已进行的时间秒和已执行的脚本指令数。如何判断成功基础成功窗口正常打开没有报错且能看到至少第一个红色烟花成功发射并爆炸。功能成功烟花按JSON脚本中定义的时间、位置、颜色依次发射。模拟成功爆炸后的粒子运动自然有重力下落和扩散效果颜色和大小会随时间变化衰减。如果运行失败第一步应该看哪里检查终端错误信息Python会打印详细的错误信息Traceback。最常见的错误是ModuleNotFoundError: No module named pygame- 说明Pygame未安装请返回第3节执行安装命令。FileNotFoundError: [Errno 2] No such file or directory: show_script.json- 确保JSON文件与main.py在同一目录且文件名拼写正确。json.decoder.JSONDecodeError- JSON文件格式有误检查是否有多余的逗号、引号不匹配等。检查代码缩进Python对缩进极其敏感请确保从本文复制的代码缩进一致建议使用4个空格。检查导入路径确保main.py中导入Launcher的语句正确from launcher import Launcher且launcher.py等文件存在。7. 常见问题与排查思路在开发和运行过程中你可能会遇到以下典型问题。下表提供了排查思路和解决方案。问题现象可能原因排查方式解决方案窗口一闪而过或立即关闭1. 代码存在语法错误导致程序崩溃。2. Pygame初始化失败。在终端运行程序查看完整的错误输出。根据终端报错信息修正语法或解决库导入问题。窗口打开但一片漆黑什么都没有1. 背景色与烟花颜色太接近但我们的背景是深蓝。2. 烟花发射坐标在屏幕外。3. 游戏循环内的update或draw逻辑未被正确调用。1. 在main.py的绘制循环开始添加一句print(“Drawing frame…”)看循环是否在执行。2. 检查launcher.update(current_show_time)是否被调用。3. 检查Firework和Particle的draw方法是否被调用。1. 确保主循环while running内的更新和绘制代码块没有因为条件判断被跳过。2. 确认发射坐标(x, y)在屏幕范围内0 x 800, 0 y 600。烟花能发射但爆炸后没有粒子或粒子立即消失1.Firework.explode()方法未被调用或调用时机不对。2. 爆炸生成的粒子生命周期(lifetime)设置过短。3. 粒子在update后因条件被立即移除。1. 在Firework.update()中在调用self.explode()前后添加print语句确认爆炸逻辑触发。2. 检查Particle类的__init__中lifetime参数的值。3. 检查Firework.is_done()判断逻辑是否过于严格。1. 确保self.rocket.y self.explosion_height条件能达成。2. 适当增加爆炸粒子的lifetime如从40-80改为60-100。3. 确保is_done只在粒子列表为空时才返回True。粒子运动轨迹不自然直线飞走或不动1. 粒子速度vx, vy初始化不合理。2.update中的物理计算有误如重力方向、衰减因子。1. 打印几个新生成粒子的初始vx, vy值看是否在合理范围如-5到5。2. 检查Particle.update()中self.vy self.gravity这行重力应为正数使粒子下落。1. 在Firework.explode()中调整生成粒子速度的随机范围speed和角度范围。2. 确认重力gravity为正值衰减decay小于1但大于0如0.96-0.99。表演不按脚本时间进行或所有烟花同时发射1. 调度器中的时间计算错误。2.current_show_time计算有误。3. JSON脚本中的time值不是数字。1. 在launcher.update()中打印current_time和正在比较的cmd_time。2. 检查program_start_time是否在循环开始前正确初始化。3. 检查JSON文件确保”time”: 0.5而不是”time”: “0.5”。1. 确保time.time()返回的是秒级时间戳相减得到的是浮点数秒。2. 确保脚本加载后按time字段排序。3. 修正JSON文件的数据类型。程序运行越来越卡1. 粒子数量过多没有及时被回收。2. 每帧都在创建新的Surface对象在Particle.draw中。1. 在main.py循环中打印当前活跃粒子总数。2. 检查Particle.draw中是否每帧都创建了新的pygame.Surface。1. 确保Firework.update()中会移除死亡的粒子(lifetime 0)。2. 对于简单的圆形粒子可以考虑使用无透明度的pygame.draw.circle直接绘制性能更好。8. 最佳实践与工程建议将这个玩具项目提升到一个更健壮、可扩展的模拟器可以考虑以下工程化实践1. 性能优化对象池频繁创建和销毁大量Particle对象会产生内存碎片。可以预先创建一批粒子对象放入“池”中需要时激活死亡后重置并放回池中避免频繁的垃圾回收。批量绘制Pygame的draw调用有一定开销。如果粒子数量巨大成千上万可以考虑使用pygame.sprite.Group进行精灵管理或使用更底层的pygame.gfxdraw函数甚至探索硬件加速方案。计算优化在粒子update中避免使用复杂的数学函数如sin,cos。我们的爆炸速度计算用到了math.cos和math.sin如果性能成为瓶颈可以预先计算好速度向量表。2. 架构扩展多种烟花类型目前的Firework只模拟了“牡丹”型球形爆炸。可以创建子类如WillowFirework柳树型粒子下落慢、RingFirework圆环型、HeartFirework心型。在JSON脚本中增加”type”: “willow”字段调度器根据类型创建不同的烟花对象。声音与音乐使用pygame.mixer模块加载背景音乐和爆炸音效在explode()方法中触发音效实现声画同步。交互式控制除了播放预设脚本可以增加鼠标交互。例如点击屏幕任意位置就在该处发射一个随机颜色的烟花。这只需要在事件循环中捕获MOUSEBUTTONDOWN事件并动态创建一个Firework加入launcher.fireworks列表即可。3. 配置与数据驱动参数外部化将重力、衰减因子、粒子数量范围等物理参数也放到JSON配置文件中这样无需修改代码就能调整整体视觉效果。脚本编辑器可以开发一个简单的图形界面通过拖拽和表单来生成JSON脚本降低编排门槛。4. 生产环境思维类比时间同步真实的分布式烟花控制系统各发射节点需要高精度时间同步如使用PTP协议。在我们的模拟中单机时间戳是足够的但如果扩展到网络版就需要一个统一的时间源。错误处理与回滚真实系统中某个发射器故障不能影响整体。模拟器中可以增加“心跳”机制如果某个烟花对象状态异常如粒子数异常增长能将其隔离并记录日志而不是让整个程序崩溃。状态持久化可以将一场精彩的表演脚本和随机种子保存下来实现“录制与回放”功能。9. 总结与后续学习方向通过这个项目我们完成了一次从艺术表现到技术实现的穿越。我们不仅看到了浏阳烟花在夜空中的绚烂更亲手用代码构建了其背后的核心模拟系统基于粒子系统的物理模拟、基于时间线的精确调度、以及数据驱动的表演编排。本文真正讲清楚的几点粒子系统是核心理解了Particle类的状态位置、速度、生命周期和更新逻辑就掌握了模拟一切动态特效的基础。调度是灵魂Launcher类将抽象的“剧本”转化为具体的“发射指令”这是自动化、可编程表演的关键。数据与逻辑分离通过JSON文件控制表演使得艺术设计何时放、放什么与程序引擎怎么放彻底解耦这是软件工程的重要思想。游戏循环是骨架while running循环中的“处理事件-更新逻辑-绘制画面”模式是几乎所有实时交互应用的基础框架。你可以立即进行的实践修改剧本打开show_script.json尝试设计自己的烟花序列。挑战一下设计一个简单的“笑脸”图案。调整物理参数在particle.py和firework.py的__init__方法中修改gravity、decay、speed等值观察对烟花形态的戏剧性影响。增加新特性为Particle的draw方法实现颜色随生命周期渐变从亮白色渐变为火焰色再消失。值得深入的学习方向游戏开发本项目的粒子系统和游戏循环是游戏开发的基础。可以进一步学习Pygame的精灵、碰撞检测、场景管理迈向2D游戏开发。计算机图形学如果想追求更逼真的视觉效果可以学习OpenGL或WebGL在GPU上实现大规模、带光照和纹理的粒子系统。实时系统与物联网将本项目的调度器思想应用到硬件上。研究如何使用单片机如Arduino、ESP32配合网络协议如MQTT通过一个中心服务器同步控制多个真实的LED灯阵实现一场“数字灯光秀”。这其中的状态同步、指令分发、容错处理与分布式系统开发一脉相承。技术的美妙之处在于它既能创造浏阳夜空那般震撼的感官体验也能被分解为一行行清晰、可推理、可重现的代码。希望这个“烟花模拟器”项目能成为你探索更广阔技术世界的一个有趣起点。建议收藏本文代码它不仅是烟花模拟更是一个可复用的实时可视化程序模板。 30款热门AI模型一站整合DeepSeek/GLM/Qwen 随心用限时 5 折。 点击领海量免费额度