目录
- 一、发射器是粒子系统的源头
- 二、开发环境与版本说明
- 三、原理分析:三种发射模式
- 3.1 emitRate 连续发射
- 3.2 burst(count) 脉冲发射
- 3.3 pulse(duration) 定时脉冲
- 3.4 三种模式对比
- 四、代码实现:Concept_EmitterMethods.qml 逐段解析
- 4.1 左栏:burst 脉冲发射
- 4.2 右栏:pulse 定时脉冲
- 4.3 为什么用两个独立的 ParticleSystem
- 五、运行效果
- 左栏:burst 脉冲发射
- 右栏:pulse 定时脉冲
- 六、适用边界与限制条件
- 七、总结与下篇预告
一、发射器是粒子系统的源头
粒子系统的所有视觉表现都始于 Emitter——它决定了粒子从哪里来、以什么频率出来、出来后往哪飞。如果把 ParticleSystem 比作一个舞台,Emitter 就是演员的入口;入口的大小、位置和开放方式,直接决定了演出的效果。
上一篇示例中展示了连续发射,但实际项目中的发射需求远不止这一种:爆炸效果需要"一次性涌出大量粒子",按钮点击反馈需要"点击后短暂发射",背景粒子需要"持续稳定输出"。这三种需求分别对应 Emitter 的三种发射模式:burst()脉冲、pulse()定时脉冲、emitRate连续发射。
本文的目标是彻底理解这三种模式的行为差异、参数含义和适用场景,让你在面对任何发射需求时都能选择正确的方案。
二、开发环境与版本说明
本文所有代码基于以下环境验证(验证日期:2026-06-08):
- Qt 版本:6.8.2(最低要求 Qt 6.5,参见 CMakeLists.txt 中
qt_standard_project_setup(REQUIRES 6.5)) - 编译器:MinGW 64-bit
- 操作系统:Windows 11
- 构建工具:CMake 3.29
三、原理分析:三种发射模式
3.1 emitRate 连续发射
emitRate是 Emitter 最基础的属性,表示每秒发射的粒子数量。它的行为是:只要enabled为true且 ParticleSystem 处于running状态,就按照固定频率持续发射粒子。
Emitter { emitRate: 80 // 每秒发射 80 个粒子 lifeSpan: 2000 // 每个粒子存活 2 秒 size: 12 }稳态粒子数的估算:当发射和消亡达到平衡时,屏幕上的活跃粒子数约为emitRate × lifeSpan / 1000。上面的例子中,稳态粒子数约为80 × 2000 / 1000 = 160个。这个公式在调参时非常有用——如果你想让屏幕上有 500 个粒子,可以设emitRate: 100, lifeSpan: 5000或emitRate: 250, lifeSpan: 2000。
emitRate为 0 时:Emitter 不会自动发射任何粒子,但仍可通过burst()手动触发。这在"只在交互时发射"的场景中很常见。
3.2 burst(count) 脉冲发射
burst(count)在调用瞬间一次性发射指定数量的粒子,不依赖emitRate。它适合需要"爆发感"的场景:爆炸、点击光效、烟花绽放。
Emitter { id: emitter emitRate: 0 // 默认不发射 lifeSpan: 2000 size: 12 velocity: AngleDirection { angle: 0 angleVariation: 360 // 全方向扩散 magnitude: 100 } } // 用户点击时触发 MouseArea { onClicked: emitter.burst(50) // 一次性发射 50 个粒子 }burst 的关键特征:
- 瞬时性:所有粒子在同一帧诞生,视觉上是"同时涌出"
- 不干扰 emitRate:
burst()和emitRate互不影响,可以在连续发射的同时触发脉冲 - 可重复调用:每次调用都追加新粒子,不会清除之前的粒子
3.3 pulse(duration) 定时脉冲
pulse(duration)在 Emitter 未启用时,将其启用指定毫秒数,到期后自动关闭。它是emitRate和burst()之间的折中方案——既有持续性,又有时间限制。注意:pulse()只在 Emitter 当前未启用(enabled: false)时有效,如果 Emitter 已经启用,调用pulse()不会有额外效果。
Emitter { id: emitter emitRate: 200 // 脉冲期间每秒发射 200 个 enabled: false // 默认禁用 lifeSpan: 2000 size: 12 } // 用户点击时触发 MouseArea { onClicked: emitter.pulse(500) // 启用 500ms,期间按 emitRate: 200 发射 }pulse 的关键特征:
- 渐进性:粒子在
duration毫秒内陆续出现,不像burst()那样同时涌出 - 自动关闭:
pulse()结束后,Emitter 自动恢复为enabled: false - 受 emitRate 影响:脉冲期间的发射量 =
emitRate × duration / 1000
pulse()的效果由emitRate和duration共同决定。emitRate控制粒子密度,duration控制持续时间。调用pulse(500)时,Emitter 在 500ms 内按emitRate发射,到期后自动禁用。duration太短(< 200ms)效果接近burst(),太长(> 2000ms)就失去了"脉冲"的感觉。一般建议 300-1000ms。
3.4 三种模式对比
| 维度 | emitRate 连续 | burst(count) | pulse(duration) |
|---|---|---|---|
| 发射方式 | 持续均匀 | 瞬时爆发 | 限时持续 |
| 粒子出现节奏 | 陆续出现 | 同时涌出 | 陆续出现 |
| 是否受 emitRate 影响 | 是 | 否 | 是 |
| 是否自动停止 | 否(除非 disable) | 是(一次完成) | 是(duration 到期) |
| 典型场景 | 背景粒子、持续效果 | 爆炸、点击反馈 | 短暂喷射、呼吸效果 |
用一张决策流程图来总结:
四、代码实现:Concept_EmitterMethods.qml 逐段解析
项目中Concept_EmitterMethods.qml并排展示了burst()和pulse()两种方法的效果对比。页面分为左右两栏,每栏各有一个粒子系统和一个触发按钮。
4.1 左栏:burst 脉冲发射
ParticleSystem { id: ps1 anchors.fill: parent running: root.isCurrentItem ImageParticle { source: "qrc:/images/star.png" color: "#FF6B6B" alpha: 0.9 } Emitter { id: burstEmitter anchors.centerIn: parent width: 1 height: 1 emitRate: 0 lifeSpan: 2000 size: 12 velocity: AngleDirection { angle: 0 angleVariation: 360 magnitude: 100 } } }emitRate: 0——Emitter 默认不发射任何粒子。只有当用户点击按钮调用burst(50)时才会产生粒子。这是burst()模式的典型写法:把emitRate设为 0,完全由手动触发控制。
width: 1, height: 1——Emitter 的发射区域设为 1×1 像素,近似一个点。配合angleVariation: 360,粒子从中心点向四面八方均匀扩散,形成爆炸般的视觉效果。
触发逻辑:
MouseArea { id: burstArea anchors.fill: parent hoverEnabled: true onClicked: burstEmitter.burst(50) }每次点击调用burst(50),瞬间发射 50 个粒子。由于lifeSpan: 2000,点击后 2 秒内粒子会逐渐消亡。如果在粒子消亡前再次点击,新旧粒子会叠加,形成更密集的效果。
4.2 右栏:pulse 定时脉冲
Emitter { id: pulseEmitter anchors.centerIn: parent width: 1 height: 1 emitRate: 200 enabled: false lifeSpan: 2000 size: 12 velocity: AngleDirection { angle: 0 angleVariation: 360 magnitude: 100 } }emitRate: 200, enabled: false——与 burst 模式的关键区别:这里emitRate设为 200(脉冲期间每秒发射 200 个),但enabled默认为false(不发射)。pulse(500)会在 500ms 内临时启用 Emitter,期间按emitRate: 200发射,500ms 后自动恢复enabled: false。
触发逻辑:
MouseArea { onClicked: pulseEmitter.pulse(500) }pulse(500)意味着每次点击后,Emitter 在 500ms 内发射约200 × 0.5 = 100个粒子。与burst(50)相比,粒子不是同时涌出,而是在半秒内陆续出现,视觉上更柔和。
duration选择 500ms 的理由:太短(如 200ms)效果接近 burst,太长(如 2000ms)就失去了"脉冲"的感觉。500ms 是一个能体现"陆续出现"特性的中间值。
4.3 为什么用两个独立的 ParticleSystem
左右两栏各自拥有独立的ParticleSystem(ps1和ps2),而不是共享一个。这是因为:
- 粒子系统的坐标是相对于 ParticleSystem 容器的,两个独立系统可以各自在自己的区域内发射
- 避免两个 Emitter 的粒子混在一起,影响对比效果
- 更清晰地展示
burst()和pulse()的行为差异
五、运行效果
运行项目后,点击左侧导航栏的「发射器」进入本示例页面。页面分为左右两栏,并排展示burst()和pulse()的效果对比。
左栏:burst 脉冲发射
点击红色圆形按钮触发burst(50)。观察粒子的诞生方式:50 个红色星形粒子在同一帧同时从中心涌出,向四面八方均匀扩散(angleVariation: 360),形成"爆炸"般的瞬间扩散效果。2 秒后(lifeSpan: 2000)粒子逐渐消亡。
验证方式:连续快速点击多次,观察粒子叠加效果。由于burst()每次追加新粒子而不清除旧粒子,多次点击后屏幕上会同时存在多批粒子,密度明显增大。
右栏:pulse 定时脉冲
点击青色圆形按钮触发pulse(500)。观察粒子的诞生方式:与 burst 不同,粒子不是同时涌出,而是在 500ms 内陆续从中心发射出来(emitRate: 200,500ms 内约发射 100 个粒子)。
验证方式:点击后观察粒子的出现节奏。前 500ms 内粒子持续涌出,500ms 后 Emitter 自动关闭(enabled恢复为false),不再有新粒子产生。已发射的粒子按lifeSpan: 2000自然消亡。
burst 与 pulse 的核心区别:
两者的差异体现在粒子的诞生方式上:
- burst:所有粒子在同一帧诞生,视觉上是"同时涌出"
- pulse:粒子在
duration毫秒内陆续诞生,视觉上是"逐渐流出"
六、适用边界与限制条件
burst()和emitRate可以同时使用:burst()是额外追加粒子,不影响emitRate的连续发射。比如设emitRate: 20作为背景粒子,同时在用户点击时burst(50)追加爆发效果。
pulse()的前置条件:pulse()只在 Emitter 当前未启用(enabled: false)时有效。如果 Emitter 已经处于启用状态,调用pulse()不会有额外效果。pulse()结束后,Emitter 自动恢复为enabled: false。
maximumEmitted安全阀:maximumEmitted限制该 Emitter 的最大活跃粒子数。当活跃粒子数达到上限时,新的发射请求会被忽略。默认值为 -1(无限制)。在burst()大量发射时尤其有用,防止性能崩溃。
lifeSpan的边界值:设为 0 时粒子立即消亡,视觉上看不到任何效果;设为 -1 时粒子永生(不会消亡),粒子数会无限增长直到性能崩溃。
emitRate的性能上限:设为极大值(如 10000)时,瞬间发射大量粒子可能导致帧率骤降。如果需要更密集的效果,优先增大lifeSpan而不是emitRate——增大lifeSpan让粒子存活更久,自然积累更多;增大emitRate则会增加每秒的发射开销。
七、总结与下篇预告
本文讲解了 Emitter 的三种发射模式:
| 模式 | 核心特征 | 选择依据 |
|---|---|---|
emitRate连续 | 持续均匀发射 | 需要不间断的粒子效果 |
burst(count) | 瞬时大量发射 | 需要爆发感的一次性效果 |
pulse(duration) | 限时持续发射 | 需要短暂但有持续性的效果 |
选择策略:持续效果用emitRate,交互反馈用burst,定时效果用pulse,混合场景可以组合使用。
下一篇将讲解粒子的外观渲染——ImageParticle 的 GPU 批量渲染能力和 ItemParticle 的 QML 组件渲染能力,以及如何在两者之间做出正确的选型。
资源下载:qml_particlesystem —— 包含完整的、可运行的代码
系列目录:
- 上一篇:Qt Quick 粒子系统(二):系统控制与生命周期管理
- 本文:Qt Quick 粒子系统(三):发射器深度解析
- 下一篇:Qt Quick 粒子系统(四):渲染器对比与选型指南