Qt Quick 粒子系统(五):粒子组与状态管理
目录
一、为什么需要粒子组
前四篇的粒子都有一个共同特征:从诞生到消亡,外观和行为一成不变。但真实世界的粒子效果往往是多阶段的——烟花火箭上升时是一个明亮的光点,到达最高点后变成数百个扩散的火花;雪花飘落时是白色晶体,落到地面后变成透明的水渍。
要实现这种"一阶段变另一阶段"的效果,就需要 ParticleGroup(粒子组)机制。它让粒子在生命周期内切换组,每个组可以有不同的渲染器和影响器,从而实现状态转换。
本文的目标是理解 ParticleGroup 的分组机制和 GroupGoal 的触发原理,掌握"先定义组 → 再定义转换条件 → 最后关联渲染器"的设计模式。
二、开发环境与版本说明
本文所有代码基于以下环境验证(验证日期:2026-06-09):
- Qt 版本:6.8.2
- 编译器:MinGW 64-bit
- 操作系统:Windows 11
- 构建工具:CMake 3.29
ParticleGroup 和 GroupGoal 的 API 从 Qt 5 起稳定,Qt 6.5+ 均可直接运行本文代码。
本文中的相关代码都只展示了一部分,为的是方便讲解,完整代码见文章结尾处的【资源下载】。
三、原理分析:粒子组状态机
3.1 ParticleGroup 的角色
ParticleGroup 定义了一组粒子的行为规则。它的核心属性:
| 属性 | 类型 | 含义 |
|---|---|---|
name |
string | 组名,Emitter 和 Painter 通过此名称关联 |
duration |
int | 粒子在该组停留的时间(毫秒) |
durationVariation |
int | 停留时间的随机偏移量(毫秒),默认 0 |
to |
ParticleSystem | 加权的组间跳转列表,定义从当前组可以跳转到哪些组 |
ParticleGroup 本身不决定粒子长什么样(那是 ParticlePainter 的事),也不决定粒子从哪来(那是 Emitter 的事)。它只定义"粒子在这个组里待多久"和"可以跳转到哪些组"。
此外,ParticleGroup 有一个隐式关联机制:将 Emitter、Affectors 或 ParticlePainter 作为 ParticleGroup 的直接子元素时,它们会自动关联到该组,无需手动设置 groups / group 属性。这在组数量较多时能减少样板代码。
3.2 GroupGoal 的触发机制
GroupGoal 继承自 Affector(影响器),是触发粒子从一个组跳转到另一个组的"传送门"。它的核心属性:
| 属性 | 类型 | 含义 |
|---|---|---|
groups |
string[] | 影响哪些粒子组(继承自 Affector) |
goalState |
string | 目标组名 |
jump |
bool | 是否立即跳转,默认 false |
x / y / width / height |
real | 传送门的区域范围(继承自 Affector) |
jump: true 的行为:粒子进入 GroupGoal 区域时,立即跳转到 goalState 指定的组,忽略 ParticleGroup 的 duration 设置,直接从目标组的起始状态开始。
jump: false 的行为:粒子不会立即跳转。GroupGoal 会引导粒子沿最短有效路径向 goalState 移动,但粒子仍需在每个中间组待满各自的 duration。如果只有一个组且没有定义 to 跳转列表,jump: false 的 GroupGoal 不会产生可见效果。
3.3 状态转换流程
用一个完整的状态图来表示粒子组的生命周期:
- default 组:ItemParticle 渲染(青色小圆),
duration: 1000ms,被jump: true覆盖 - attracted 组:ImageParticle 渲染(黄色星形),
duration: 1000ms,到期后消亡
3.4 设计模式:三步构建粒子组
构建粒子组效果的标准流程:
- 定义组:声明 ParticleGroup,指定组名和停留时间
- 定义转换条件:声明 GroupGoal(空间触发)或在 ParticleGroup 的
to属性中定义加权跳转列表(时间触发) - 关联渲染器:用 ParticlePainter 的
groups属性指定每个渲染器负责哪些组,用 Emitter 的group属性指定发射到哪个组
四、代码实现:Concept_ParticleGroup.qml 逐段解析
4.1 两个粒子组的定义
ParticleGroup {
name: "default"
duration: 1000
}
ParticleGroup {
name: "attracted"
duration: 1000
}
两个组都设了 duration: 1000,表示粒子在每个组停留 1 秒。但由于 GroupGoal 使用了 jump: true,default 组的粒子进入传送门区域后会立即跳转,不会等满 1 秒。
注意这里没有定义 to 属性——to 是 ParticleGroup 的加权跳转列表,用于定义"从当前组可以跳转到哪些组以及权重"。本例的跳转完全由 GroupGoal 的空间触发控制,不需要 to 列表。如果需要基于时间的自动跳转(而非空间触发),则必须定义 to。
4.2 两个渲染器分别渲染不同组
ItemParticle {
groups: ["default"]
delegate: Rectangle {
width: 6; height: 6; radius: 3
color: "#4ECDC4"
}
}
ImageParticle {
groups: ["attracted"]
source: "qrc:/images/star.png"
color: "#FFE66D"
}
groups 属性是关联的关键——ItemParticle 只渲染 default 组的粒子(6×6 青色小圆点),ImageParticle 只渲染 attracted 组的粒子(黄色星形图片)。同一个粒子在切换组后,视觉外观会瞬间改变。
这里展示了一个典型的混搭用法:default 组用 ItemParticle(简单的圆形 delegate),attracted 组用 ImageParticle(星形图片)。在实际项目中,不同组可以使用完全不同的渲染策略。
4.3 Emitter 发射到 default 组
Emitter {
id: emitter
anchors.bottom: parent.bottom
anchors.horizontalCenter: parent.horizontalCenter
width: parent.width
height: 1
emitRate: 50
lifeSpan: 4000
size: 12
group: "default"
velocity: PointDirection { y: -100; yVariation: 20 }
acceleration: PointDirection { y: -20 }
}
group: "default" ——Emitter 将所有粒子发射到 default 组。粒子诞生后以 default 组的渲染器(青色小圆点)显示。
发射区域:width: parent.width, height: 1,沿底部边缘线性发射。velocity: PointDirection { y: -100 } 让粒子向上运动,acceleration: PointDirection { y: -20 } 给粒子一个微弱的向上加速度(减速后加速)。
4.4 GroupGoal 触发状态转换
GroupGoal {
id: groupGoal
system: particleSystem
groups: ["default"]
goalState: "attracted"
x: 0
y: parent.height / 2
width: parent.width
height: 20
jump: true
}
GroupGoal 定义了一个横跨页面宽度的"传送带",位于页面中间(y: parent.height / 2)。当 default 组的粒子向上运动穿过这个区域时,立即跳转到 attracted 组,外观从青色小圆点变为黄色星形。
jump: true ——这是关键设置。设为 true 时,粒子一进入区域就立即跳转;设为 false 时,粒子必须在 default 组待满 duration: 1000 才会跳转。对于这个"穿过区域变色"的效果,必须用 jump: true。
可视化的传送门区域:
Rectangle {
x: groupGoal.x; y: groupGoal.y
width: groupGoal.width; height: groupGoal.height
border.color: "#aaa"
color: "black"
}
这个 Rectangle 只是视觉参考,让玩家看到传送门在哪里。它不影响粒子行为——真正控制转换的是 GroupGoal 组件。
4.5 底部的状态说明
Row {
spacing: 20
anchors.horizontalCenter: parent.horizontalCenter
Text { color: "#4ECDC4"; text: "● default" }
Text { color: "#FFE66D"; text: "● attracted" }
}
Text {
color: "#aaa"
text: "default粒子组穿过区域后转换为attracted粒子组"
}
颜色图例帮助用户理解两种颜色分别对应哪个组,文字说明解释了转换规则。
五、运行效果
运行项目后,点击左侧导航栏的「粒子组」进入本示例页面。

初始状态:青色小圆点(ItemParticle 渲染)从页面底部向上发射(velocity: y: -100),形成整齐的"上升粒子流"。
穿过传送门:当粒子到达页面中间的黑色区域(GroupGoal 的 y: parent.height / 2)时,瞬间变为黄色星形图片(ImageParticle 渲染),继续向上运动直到 lifeSpan: 4000 到期消亡。
视觉效果:页面上半部分是黄色星形,下半部分是青色圆点,中间有一条清晰的分界线——这就是 GroupGoal 传送门的位置。
验证方式:将 GroupGoal 的 jump 改为 false,观察粒子是否不再立即变色(因为没有 to 列表,jump: false 时 GroupGoal 不会产生可见效果)。再将 jump 改回 true,将 GroupGoal 的 height 从 20 改为 200,观察传送门区域变宽后的效果。
六、适用边界与限制条件
6.1 组与发射器的关联
Emitter 的 group 属性指定粒子发射到哪个组,ParticleGroup 通过 name 属性定义该组的行为规则。两者通过名称字符串关联。如果 Emitter 的 group 没有对应的 ParticleGroup 定义,粒子会使用默认行为——无 duration 限制,存活到 lifeSpan 结束。
6.2 单粒子单组
同一时间一个粒子只属于一个组。切换组时,粒子的渲染器和影响器会立即切换到新组的配置,不存在过渡状态。这意味着如果两个组使用完全不同的渲染器(如本例的 ItemParticle 和 ImageParticle),切换会在一帧内完成。
6.3 未触发 GroupGoal 的情况
如果粒子从未进入 GroupGoal 的区域,它会在当前组待满 duration 后尝试按 to 列表跳转;如果没有 to 列表,粒子保持当前组直到 lifeSpan 结束。本例中两个组都没有定义 to,所以未穿过传送门的粒子会以 default 组的外观存活 4 秒后消亡。
6.4 duration 与 jump 的交互
duration: 0 时粒子立即尝试跳转,效果类似 jump: true。jump: true 会覆盖 duration 设置,粒子不等时间到期就直接跳转。jump: false 且 duration > 0 时,粒子需待满时间后沿 to 列表的最短路径跳转——如果 to 列表为空,则不跳转。
6.5 性能建议
- 粒子组数量不宜过多,每增加一个组就增加一份渲染器和影响器的开销
- 如果不需要状态转换,不要使用 ParticleGroup——直接用 Emitter 的默认行为即可
- GroupGoal 的区域尽量小,避免不必要的碰撞检测开销
七、总结与下篇预告
本文讲解了 ParticleGroup 的分组机制:
- ParticleGroup 定义组名和停留时间,是状态机的"节点"
- GroupGoal 定义跳转条件和目标组,是状态机的"转换边"
- ParticlePainter 的 groups 属性 将渲染器关联到特定组,实现不同组不同外观
- Emitter 的 group 属性 指定粒子发射到哪个组
设计模式口诀:先定义组 → 再定义转换条件 → 最后关联渲染器。
至此,系列的第一部分"基本概念"全部完成。下一篇进入第二部分"进阶专题",讲解五种发射区域 Shape 的精确控制——如何让粒子从椭圆边界、线条、甚至自定义图形发射出来。
资源下载:qml_particlesystem —— 包含完整的、可运行的代码
系列目录:
- 上一篇:Qt Quick 粒子系统(四):渲染器对比与选型指南
- 本文:Qt Quick 粒子系统(五):粒子组与状态管理
- 下一篇:Qt Quick 粒子系统(六):五种发射区域的精确控制
AtomGit 是由开放原子开源基金会联合 CSDN 等生态伙伴共同推出的新一代开源与人工智能协作平台。平台坚持“开放、中立、公益”的理念,把代码托管、模型共享、数据集托管、智能体开发体验和算力服务整合在一起,为开发者提供从开发、训练到部署的一站式体验。
更多推荐



所有评论(0)