欢迎光临
我们一直在努力

Qt Quick 粒子系统(七):扩散方向模型

目录

    • 一、粒子往哪里飞
    • 二、开发环境与版本说明
    • 三、原理分析:三种方向模型
      • 3.1 Direction 的角色
      • 3.2 AngleDirection:极坐标模型
      • 3.3 PointDirection:直角坐标模型
      • 3.4 TargetDirection:目标点模型
      • 3.5 CumulativeDirection:组合模型
      • 3.6 选型决策
      • 3.7 velocity 与 acceleration 的配合
    • 四、代码实现
      • 4.1 Direction_AngleSpread.qml:水平扩散
      • 4.2 Direction_Parabolic.qml:抛物线轨迹
      • 4.3 Direction_ConeSpread.qml:锥形扩散
      • 4.4 Direction_TargetSpread.qml:目标扩散
      • 4.5 Direction_Cumulative.qml:组合方向
      • 4.6 同一个效果,不同实现——模型对比
      • 4.7 参数调优指南
    • 五、运行效果
    • 六、适用边界与限制条件
    • 七、总结

一、粒子往哪里飞

上一篇解决了"粒子从哪里出生"的问题,这一篇解决"粒子往哪里飞"。粒子的运动方向由 Emitter 的 velocity(速度)和 acceleration(加速度)属性控制,两者的值类型都是 Direction(实际为 StochasticDirection)。

Qt Quick 粒子系统提供了三种基础 Direction 模型和一种组合模型:AngleDirection(极坐标模型)、PointDirection(直角坐标模型)、TargetDirection(目标点模型)、CumulativeDirection(组合模型)。每种模型对应不同的参数输入方式,选错模型会导致参数难以理解或效果不符合预期。

本文围绕"你有什么参数就用什么模型"展开,先讲数学本质,再通过五个示例演示参数调优。


二、开发环境与版本说明

本文所有代码基于以下环境验证(验证日期:2026-06-10):

  • Qt 版本:6.8.2
  • 编译器:MinGW 64-bit
  • 操作系统:Windows 11
  • 构建工具:CMake 3.29

Direction 的 API 从 Qt 5 起稳定,Qt 6.5+ 均可直接运行本文代码。


三、原理分析:三种方向模型

3.1 Direction 的角色

Direction 是 Emitter 的 velocity 和 acceleration 属性的值类型(实际类型为 StochasticDirection,Direction 是其基类)。它定义了一个速度向量或加速度向量:

  • velocity:粒子诞生时的初始速度,决定粒子往哪飞、飞多快
  • acceleration:粒子受到的持续加速度,决定粒子的运动轨迹如何变化

一个粒子的运动可以用物理公式描述:

位置 = 初始位置 + velocity × t + 0.5 × acceleration × t²

两者可以使用不同的 Direction 模型。

3.2 AngleDirection:极坐标模型

输入参数:角度(angle)+ 速度大小(magnitude)

数学本质:将速度分解为直角坐标分量

vx = magnitude × cos(angle)
vy = magnitude × sin(angle)

关键属性:

属性含义默认值
angle 发射角度(度) 0
angleVariation 角度随机偏移范围 0
magnitude 速度大小(像素/秒) 0
magnitudeVariation 速度大小随机偏移范围 0

角度参考系:Qt Quick 的角度以向右为 0 度,顺时针递增:

#mermaid-svg-jEHt3hMbfmwKuLQg{font-family:\”trebuchet ms\”,verdana,arial,sans-serif;font-size:16px;fill:#333;}@keyframes edge-animation-frame{from{stroke-dashoffset:0;}}@keyframes dash{to{stroke-dashoffset:0;}}#mermaid-svg-jEHt3hMbfmwKuLQg .edge-animation-slow{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 50s linear infinite;stroke-linecap:round;}#mermaid-svg-jEHt3hMbfmwKuLQg .edge-animation-fast{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 20s linear infinite;stroke-linecap:round;}#mermaid-svg-jEHt3hMbfmwKuLQg .error-icon{fill:#552222;}#mermaid-svg-jEHt3hMbfmwKuLQg .error-text{fill:#552222;stroke:#552222;}#mermaid-svg-jEHt3hMbfmwKuLQg .edge-thickness-normal{stroke-width:1px;}#mermaid-svg-jEHt3hMbfmwKuLQg .edge-thickness-thick{stroke-width:3.5px;}#mermaid-svg-jEHt3hMbfmwKuLQg .edge-pattern-solid{stroke-dasharray:0;}#mermaid-svg-jEHt3hMbfmwKuLQg .edge-thickness-invisible{stroke-width:0;fill:none;}#mermaid-svg-jEHt3hMbfmwKuLQg .edge-pattern-dashed{stroke-dasharray:3;}#mermaid-svg-jEHt3hMbfmwKuLQg .edge-pattern-dotted{stroke-dasharray:2;}#mermaid-svg-jEHt3hMbfmwKuLQg .marker{fill:#333333;stroke:#333333;}#mermaid-svg-jEHt3hMbfmwKuLQg .marker.cross{stroke:#333333;}#mermaid-svg-jEHt3hMbfmwKuLQg svg{font-family:\”trebuchet ms\”,verdana,arial,sans-serif;font-size:16px;}#mermaid-svg-jEHt3hMbfmwKuLQg p{margin:0;}#mermaid-svg-jEHt3hMbfmwKuLQg .label{font-family:\”trebuchet ms\”,verdana,arial,sans-serif;color:#333;}#mermaid-svg-jEHt3hMbfmwKuLQg .cluster-label text{fill:#333;}#mermaid-svg-jEHt3hMbfmwKuLQg .cluster-label span{color:#333;}#mermaid-svg-jEHt3hMbfmwKuLQg .cluster-label span p{background-color:transparent;}#mermaid-svg-jEHt3hMbfmwKuLQg .label text,#mermaid-svg-jEHt3hMbfmwKuLQg span{fill:#333;color:#333;}#mermaid-svg-jEHt3hMbfmwKuLQg .node rect,#mermaid-svg-jEHt3hMbfmwKuLQg .node circle,#mermaid-svg-jEHt3hMbfmwKuLQg .node ellipse,#mermaid-svg-jEHt3hMbfmwKuLQg .node polygon,#mermaid-svg-jEHt3hMbfmwKuLQg .node path{fill:#ECECFF;stroke:#9370DB;stroke-width:1px;}#mermaid-svg-jEHt3hMbfmwKuLQg .rough-node .label text,#mermaid-svg-jEHt3hMbfmwKuLQg .node .label text,#mermaid-svg-jEHt3hMbfmwKuLQg .image-shape .label,#mermaid-svg-jEHt3hMbfmwKuLQg .icon-shape .label{text-anchor:middle;}#mermaid-svg-jEHt3hMbfmwKuLQg .node .katex path{fill:#000;stroke:#000;stroke-width:1px;}#mermaid-svg-jEHt3hMbfmwKuLQg .rough-node .label,#mermaid-svg-jEHt3hMbfmwKuLQg .node .label,#mermaid-svg-jEHt3hMbfmwKuLQg .image-shape .label,#mermaid-svg-jEHt3hMbfmwKuLQg .icon-shape .label{text-align:center;}#mermaid-svg-jEHt3hMbfmwKuLQg .node.clickable{cursor:pointer;}#mermaid-svg-jEHt3hMbfmwKuLQg .root .anchor path{fill:#333333!important;stroke-width:0;stroke:#333333;}#mermaid-svg-jEHt3hMbfmwKuLQg .arrowheadPath{fill:#333333;}#mermaid-svg-jEHt3hMbfmwKuLQg .edgePath .path{stroke:#333333;stroke-width:2.0px;}#mermaid-svg-jEHt3hMbfmwKuLQg .flowchart-link{stroke:#333333;fill:none;}#mermaid-svg-jEHt3hMbfmwKuLQg .edgeLabel{background-color:rgba(232,232,232, 0.8);text-align:center;}#mermaid-svg-jEHt3hMbfmwKuLQg .edgeLabel p{background-color:rgba(232,232,232, 0.8);}#mermaid-svg-jEHt3hMbfmwKuLQg .edgeLabel rect{opacity:0.5;background-color:rgba(232,232,232, 0.8);fill:rgba(232,232,232, 0.8);}#mermaid-svg-jEHt3hMbfmwKuLQg .labelBkg{background-color:rgba(232, 232, 232, 0.5);}#mermaid-svg-jEHt3hMbfmwKuLQg .cluster rect{fill:#ffffde;stroke:#aaaa33;stroke-width:1px;}#mermaid-svg-jEHt3hMbfmwKuLQg .cluster text{fill:#333;}#mermaid-svg-jEHt3hMbfmwKuLQg .cluster span{color:#333;}#mermaid-svg-jEHt3hMbfmwKuLQg div.mermaidTooltip{position:absolute;text-align:center;max-width:200px;padding:2px;font-family:\”trebuchet ms\”,verdana,arial,sans-serif;font-size:12px;background:hsl(80, 100%, 96.2745098039%);border:1px solid #aaaa33;border-radius:2px;pointer-events:none;z-index:100;}#mermaid-svg-jEHt3hMbfmwKuLQg .flowchartTitleText{text-anchor:middle;font-size:18px;fill:#333;}#mermaid-svg-jEHt3hMbfmwKuLQg rect.text{fill:none;stroke-width:0;}#mermaid-svg-jEHt3hMbfmwKuLQg .icon-shape,#mermaid-svg-jEHt3hMbfmwKuLQg .image-shape{background-color:rgba(232,232,232, 0.8);text-align:center;}#mermaid-svg-jEHt3hMbfmwKuLQg .icon-shape p,#mermaid-svg-jEHt3hMbfmwKuLQg .image-shape p{background-color:rgba(232,232,232, 0.8);padding:2px;}#mermaid-svg-jEHt3hMbfmwKuLQg .icon-shape .label rect,#mermaid-svg-jEHt3hMbfmwKuLQg .image-shape .label rect{opacity:0.5;background-color:rgba(232,232,232, 0.8);fill:rgba(232,232,232, 0.8);}#mermaid-svg-jEHt3hMbfmwKuLQg .label-icon{display:inline-block;height:1em;overflow:visible;vertical-align:-0.125em;}#mermaid-svg-jEHt3hMbfmwKuLQg .node .label-icon path{fill:currentColor;stroke:revert;stroke-width:revert;}#mermaid-svg-jEHt3hMbfmwKuLQg :root{–mermaid-font-family:\”trebuchet ms\”,verdana,arial,sans-serif;}

角度参考系

0° → 向右

90° → 向下

180° → 向左

270° → 向上

适用场景:均匀扩散、锥形喷射、定向发射。当你知道"往哪个角度飞、飞多快"时,用 AngleDirection。

3.3 PointDirection:直角坐标模型

输入参数:x 方向速度 + y 方向速度

数学本质:直接指定速度的 x/y 分量,不做坐标转换

vx = x + random(-xVariation, +xVariation)
vy = y + random(-yVariation, +yVariation)

关键属性:

属性含义默认值
x x 方向速度分量 0
xVariation x 方向随机偏移 0
y y 方向速度分量 0
yVariation y 方向随机偏移 0

适用场景:抛物线轨迹(配合 acceleration)、定向流动、锥形扩散。当你知道"x 方向飞多快、y 方向飞多快"时,用 PointDirection。

3.4 TargetDirection:目标点模型

输入参数:目标点坐标(targetX, targetY)+ 速度大小(magnitude)

数学本质:速度方向指向目标点,大小由 magnitude 决定

dx = targetX – particle.x
dy = targetY – particle.y
angle = atan2(dy, dx)
vx = magnitude × cos(angle)
vy = magnitude × sin(angle)

关键属性:

属性含义默认值
targetX 目标点 x 坐标(相对于 Emitter) 0
targetY 目标点 y 坐标(相对于 Emitter) 0
targetItem 目标 Item(优先级高于 targetX/targetY)
targetVariation 目标点随机偏移范围 0
magnitude 速度大小(像素/秒) 0
magnitudeVariation 速度大小随机偏移 0
proportionalMagnitude 是否按距离比例计算速度 false

targetItem:可以直接绑定一个 QML Item 作为目标,优先级高于 targetX/targetY。无需手动计算坐标偏移。

proportionalMagnitude:设为 true 时,magnitude 被解释为"距目标点距离的倍数/秒"——粒子离目标远时速度快,接近时自动减速,产生自然的减速效果。默认 false,magnitude 为固定像素/秒。

适用场景:粒子汇聚、吸引效果、指向性发射。当你知道"飞向哪个点"时,用 TargetDirection。

3.5 CumulativeDirection:组合模型

数学本质:多个 Direction 的速度向量叠加

v_total = v_1 + v_2 + … + v_n

CumulativeDirection 内部可以包含任意数量的子 Direction,它们的速度向量会相加。比如一个 AngleDirection(随机扩散)叠加一个 PointDirection(定向偏移),粒子就同时具有扩散和定向运动。

适用场景:需要同时具有多种运动特征的复合效果。

3.6 选型决策

#mermaid-svg-aoAbpZFdrnxHZldj{font-family:\”trebuchet ms\”,verdana,arial,sans-serif;font-size:16px;fill:#333;}@keyframes edge-animation-frame{from{stroke-dashoffset:0;}}@keyframes dash{to{stroke-dashoffset:0;}}#mermaid-svg-aoAbpZFdrnxHZldj .edge-animation-slow{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 50s linear infinite;stroke-linecap:round;}#mermaid-svg-aoAbpZFdrnxHZldj .edge-animation-fast{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 20s linear infinite;stroke-linecap:round;}#mermaid-svg-aoAbpZFdrnxHZldj .error-icon{fill:#552222;}#mermaid-svg-aoAbpZFdrnxHZldj .error-text{fill:#552222;stroke:#552222;}#mermaid-svg-aoAbpZFdrnxHZldj .edge-thickness-normal{stroke-width:1px;}#mermaid-svg-aoAbpZFdrnxHZldj .edge-thickness-thick{stroke-width:3.5px;}#mermaid-svg-aoAbpZFdrnxHZldj .edge-pattern-solid{stroke-dasharray:0;}#mermaid-svg-aoAbpZFdrnxHZldj .edge-thickness-invisible{stroke-width:0;fill:none;}#mermaid-svg-aoAbpZFdrnxHZldj .edge-pattern-dashed{stroke-dasharray:3;}#mermaid-svg-aoAbpZFdrnxHZldj .edge-pattern-dotted{stroke-dasharray:2;}#mermaid-svg-aoAbpZFdrnxHZldj .marker{fill:#333333;stroke:#333333;}#mermaid-svg-aoAbpZFdrnxHZldj .marker.cross{stroke:#333333;}#mermaid-svg-aoAbpZFdrnxHZldj svg{font-family:\”trebuchet ms\”,verdana,arial,sans-serif;font-size:16px;}#mermaid-svg-aoAbpZFdrnxHZldj p{margin:0;}#mermaid-svg-aoAbpZFdrnxHZldj .label{font-family:\”trebuchet ms\”,verdana,arial,sans-serif;color:#333;}#mermaid-svg-aoAbpZFdrnxHZldj .cluster-label text{fill:#333;}#mermaid-svg-aoAbpZFdrnxHZldj .cluster-label span{color:#333;}#mermaid-svg-aoAbpZFdrnxHZldj .cluster-label span p{background-color:transparent;}#mermaid-svg-aoAbpZFdrnxHZldj .label text,#mermaid-svg-aoAbpZFdrnxHZldj span{fill:#333;color:#333;}#mermaid-svg-aoAbpZFdrnxHZldj .node rect,#mermaid-svg-aoAbpZFdrnxHZldj .node circle,#mermaid-svg-aoAbpZFdrnxHZldj .node ellipse,#mermaid-svg-aoAbpZFdrnxHZldj .node polygon,#mermaid-svg-aoAbpZFdrnxHZldj .node path{fill:#ECECFF;stroke:#9370DB;stroke-width:1px;}#mermaid-svg-aoAbpZFdrnxHZldj .rough-node .label text,#mermaid-svg-aoAbpZFdrnxHZldj .node .label text,#mermaid-svg-aoAbpZFdrnxHZldj .image-shape .label,#mermaid-svg-aoAbpZFdrnxHZldj .icon-shape .label{text-anchor:middle;}#mermaid-svg-aoAbpZFdrnxHZldj .node .katex path{fill:#000;stroke:#000;stroke-width:1px;}#mermaid-svg-aoAbpZFdrnxHZldj .rough-node .label,#mermaid-svg-aoAbpZFdrnxHZldj .node .label,#mermaid-svg-aoAbpZFdrnxHZldj .image-shape .label,#mermaid-svg-aoAbpZFdrnxHZldj .icon-shape .label{text-align:center;}#mermaid-svg-aoAbpZFdrnxHZldj .node.clickable{cursor:pointer;}#mermaid-svg-aoAbpZFdrnxHZldj .root .anchor path{fill:#333333!important;stroke-width:0;stroke:#333333;}#mermaid-svg-aoAbpZFdrnxHZldj .arrowheadPath{fill:#333333;}#mermaid-svg-aoAbpZFdrnxHZldj .edgePath .path{stroke:#333333;stroke-width:2.0px;}#mermaid-svg-aoAbpZFdrnxHZldj .flowchart-link{stroke:#333333;fill:none;}#mermaid-svg-aoAbpZFdrnxHZldj .edgeLabel{background-color:rgba(232,232,232, 0.8);text-align:center;}#mermaid-svg-aoAbpZFdrnxHZldj .edgeLabel p{background-color:rgba(232,232,232, 0.8);}#mermaid-svg-aoAbpZFdrnxHZldj .edgeLabel rect{opacity:0.5;background-color:rgba(232,232,232, 0.8);fill:rgba(232,232,232, 0.8);}#mermaid-svg-aoAbpZFdrnxHZldj .labelBkg{background-color:rgba(232, 232, 232, 0.5);}#mermaid-svg-aoAbpZFdrnxHZldj .cluster rect{fill:#ffffde;stroke:#aaaa33;stroke-width:1px;}#mermaid-svg-aoAbpZFdrnxHZldj .cluster text{fill:#333;}#mermaid-svg-aoAbpZFdrnxHZldj .cluster span{color:#333;}#mermaid-svg-aoAbpZFdrnxHZldj div.mermaidTooltip{position:absolute;text-align:center;max-width:200px;padding:2px;font-family:\”trebuchet ms\”,verdana,arial,sans-serif;font-size:12px;background:hsl(80, 100%, 96.2745098039%);border:1px solid #aaaa33;border-radius:2px;pointer-events:none;z-index:100;}#mermaid-svg-aoAbpZFdrnxHZldj .flowchartTitleText{text-anchor:middle;font-size:18px;fill:#333;}#mermaid-svg-aoAbpZFdrnxHZldj rect.text{fill:none;stroke-width:0;}#mermaid-svg-aoAbpZFdrnxHZldj .icon-shape,#mermaid-svg-aoAbpZFdrnxHZldj .image-shape{background-color:rgba(232,232,232, 0.8);text-align:center;}#mermaid-svg-aoAbpZFdrnxHZldj .icon-shape p,#mermaid-svg-aoAbpZFdrnxHZldj .image-shape p{background-color:rgba(232,232,232, 0.8);padding:2px;}#mermaid-svg-aoAbpZFdrnxHZldj .icon-shape .label rect,#mermaid-svg-aoAbpZFdrnxHZldj .image-shape .label rect{opacity:0.5;background-color:rgba(232,232,232, 0.8);fill:rgba(232,232,232, 0.8);}#mermaid-svg-aoAbpZFdrnxHZldj .label-icon{display:inline-block;height:1em;overflow:visible;vertical-align:-0.125em;}#mermaid-svg-aoAbpZFdrnxHZldj .node .label-icon path{fill:currentColor;stroke:revert;stroke-width:revert;}#mermaid-svg-aoAbpZFdrnxHZldj :root{–mermaid-font-family:\”trebuchet ms\”,verdana,arial,sans-serif;}

角度 + 速度大小

X/Y 速度分量

目标点坐标

多种方向叠加

你有什么参数?

AngleDirection极坐标模型

PointDirection直角坐标模型

TargetDirection目标点模型

CumulativeDirection组合模型

均匀扩散、锥形喷射

抛物线、定向流动

粒子汇聚、吸引效果

扩散+定向复合运动

3.7 velocity 与 acceleration 的配合

两者的常见搭配模式:

  • 纯 velocity:粒子匀速直线运动
  • velocity + acceleration:粒子变速运动(如抛物线)
  • 纯 acceleration(velocity 为 0):粒子从静止开始加速

最经典的配合是"抛物线":velocity 用 AngleDirection(斜向上),acceleration 用 PointDirection(向下重力)。


四、代码实现

4.1 Direction_AngleSpread.qml:水平扩散

每个示例都由 ParticleSystem、ImageParticle、Emitter 三部分组成,后续只展示 Emitter 核心配置。

Emitter {
anchors.left: parent.left
anchors.verticalCenter: parent.verticalCenter
width: 1; height: 1
emitRate: 100
lifeSpan: 4000
size: 10
velocity: AngleDirection {
angle: 0 // 基础方向向右
angleVariation: 30 // ±30° 随机偏移
magnitude: 150 // 速度 150 像素/秒
magnitudeVariation: 50 // 速度在 [100, 200] 之间随机
}
}

结果是粒子从左侧向右呈扇形扩散,扩散角度 60 度(±30 度)。magnitudeVariation 是双向偏移(150 ± 50),速度不同的粒子形成参差不齐的前沿。

4.2 Direction_Parabolic.qml:抛物线轨迹

Emitter {
anchors.left: parent.left
anchors.verticalCenter: parent.verticalCenter
width: 1; height: 1
emitRate: 60
lifeSpan: 4000
size: 10
velocity: AngleDirection {
angle: 315
angleVariation: 10
magnitude: 200
magnitudeVariation: 30
}
acceleration: PointDirection {
y: 80
}
}

velocity:angle: 315 表示向右上方发射。推导过程:0° 是正右方,90° 是正下方(顺时针递增),270° 是正上方,所以 315° = 270° + 45° = 向上偏右 45°。magnitude: 200 表示初始速度较快。

acceleration:PointDirection { y: 80 } 表示持续向下的加速度,模拟重力。注意 y: 80 是正值——在 Qt Quick 坐标系中,y 轴向下为正,所以正值 y 表示向下的加速度。

两者配合产生了抛物线轨迹:粒子先斜向上飞,速度逐渐被重力抵消,到达最高点后开始下落。

4.3 Direction_ConeSpread.qml:锥形扩散

Emitter {
x: parent.width / 2
y: 80
width: 1; height: 1
emitRate: 100
lifeSpan: 3000
size: 10
velocity: PointDirection {
x: 0
y: 150
xVariation: 60
yVariation: 30
}
}

x: 0, y: 150 表示基础方向向下,xVariation: 60 表示水平方向 ±60 的随机偏移,yVariation: 30 表示垂直方向 ±30 的随机偏移。结果是粒子从顶部向下呈锥形扩散——中心区域粒子密集,边缘稀疏。

这个效果用 AngleDirection 也能实现(angle: 90, angleVariation: 20),但 PointDirection 更直观地表达了"向下为主、左右偏移"的意图。

4.4 Direction_TargetSpread.qml:目标扩散

Emitter {
anchors.left: parent.left
anchors.verticalCenter: parent.verticalCenter
width: 1; height: 1
emitRate: 80
lifeSpan: 4000
size: 10
velocity: TargetDirection {
targetX: targetPoint.x – emitter.x
targetY: targetPoint.y – emitter.y
targetVariation: 80
magnitude: 120
magnitudeVariation: 30
}
}

targetX / targetY:目标点坐标是相对于 Emitter 的偏移量。代码中用 targetPoint.x – emitter.x 和 targetPoint.y – emitter.y 通过 QML 属性绑定动态计算——当目标点或 Emitter 移动时,偏移量会自动更新,粒子始终指向目标点。

实际上 TargetDirection 还提供了 targetItem 属性,可以直接绑定目标 Item,不需要手动计算偏移:targetItem: targetPoint。本例使用手动计算是为了演示相对坐标的含义。

targetVariation: 80:目标点的随机偏移范围。设为 80 意味着粒子不会精确飞向目标点,而是在目标点周围 80 像素的范围内随机偏移,避免"激光"效果。目标点用一个带呼吸动画的绿色圆形标记。

4.5 Direction_Cumulative.qml:组合方向

Emitter {
anchors.centerIn: parent
width: 1; height: 1
emitRate: 100
lifeSpan: 4000
size: 10
velocity: CumulativeDirection {
AngleDirection {
angle: 0
angleVariation: 360
magnitude: 80
}
PointDirection {
x: 50
y: 0
}
}
}

CumulativeDirection 包含两个子 Direction,速度向量逐帧相加:

  • AngleDirection:angleVariation: 360 全方向随机扩散,提供均匀的"圆"
  • PointDirection:x: 50 向右偏移,提供一个向右的"箭头"

叠加后,向右的粒子更快,向左的粒子更慢甚至反向——整体呈现"被风吹动的烟花"效果。

4.6 同一个效果,不同实现——模型对比

以"锥形扩散"(粒子从顶部向下发射,水平随机偏移)为例,两种模型都能实现:

// 方案 A:AngleDirection —— 用角度控制扩散范围
velocity: AngleDirection {
angle: 90; angleVariation: 20; magnitude: 150
}

// 方案 B:PointDirection —— 用 X/Y 分量控制偏移范围
velocity: PointDirection {
x: 0; y: 150; xVariation: 60; yVariation: 30
}

两者的视觉效果接近,但参数直觉不同:AngleDirection 的 angleVariation 控制扇形张角,适合喷射、火焰;PointDirection 的 xVariation 控制左右散布范围,适合雨帘、瀑布。选型依据:角度是核心参数用 AngleDirection,水平/垂直偏移量是核心参数用 PointDirection。

4.7 参数调优指南

模型想要效果调整什么
AngleDirection 扩散更集中/更宽 减小/增大 angleVariation(0~360)
AngleDirection 粒子飞得更远 增大 magnitude
PointDirection 左右散布更宽 增大 xVariation
PointDirection 上下散布更宽 增大 yVariation
抛物线 更陡峭 增大 acceleration.y
抛物线 飞得更高 增大 velocity.magnitude

规律:Variation 控制随机范围,基础值控制主运动方向和强度。


五、运行效果

Direction_AngleSpread.qml:红色星形粒子从页面左侧向右呈扇形扩散,扩散角度约 60 度。 在这里插入图片描述

Direction_Parabolic.qml:青色星形粒子从左侧斜向上发射,在重力作用下画出优美的抛物线轨迹。 在这里插入图片描述

Direction_ConeSpread.qml:黄色星形粒子从顶部向下呈锥形扩散,中心密集、边缘稀疏。 在这里插入图片描述

Direction_TargetSpread.qml:绿色星形粒子从左侧向右上方的目标点汇聚,但不精确——带有自然的发散效果。目标点有呼吸动画标记。 在这里插入图片描述

Direction_Cumulative.qml:青色星形粒子从中心向四面八方扩散,但整体有明显的向右漂移趋势。 在这里插入图片描述


六、适用边界与限制条件

  • velocity 只能设一个 Direction,不能同时写两个。需要多种方向叠加时用 CumulativeDirection,但 velocity 和 acceleration 可以使用不同的模型
  • acceleration 最常用 PointDirection(如重力 y: 80),AngleDirection 和 TargetDirection 作为加速度时语义不直观
  • 角度单位是度(0-360),向右为 0 度,顺时针递增:0° = 右,90° = 下,270° = 上
  • TargetDirection 的 targetX/targetY 是相对坐标(相对于 Emitter)。不想手动计算可以用 targetItem 直接绑定目标 Item
  • magnitude 为 0 时粒子静止,有 acceleration 则从静止开始加速
  • angleVariation 为 0 时所有粒子精确沿 angle 方向发射;为 360 时完全随机扩散

七、总结

回到开头的问题:“粒子往哪里飞?”——答案取决于你手里的参数。

三种模型的核心区别在于参数输入方式:AngleDirection 用极坐标,PointDirection 用直角坐标,TargetDirection 用目标点坐标。同一个效果(如锥形扩散)可以用不同模型实现,选择取决于哪个模型的参数更符合你的直觉。需要多种运动叠加时,CumulativeDirection 将多个向量相加。

velocity 和 acceleration 可以混用不同模型。最经典的配合是"抛物线":velocity 用 AngleDirection 控制发射方向,acceleration 用 PointDirection 模拟重力。

下一篇讲解粒子的随机性参数——位置、速度、角度。


资源下载:qml_particlesystem —— 包含完整的、可运行的代码

系列目录

  • 上一篇:Qt Quick 粒子系统(六):五种发射区域的精确控制
  • 本文:Qt Quick 粒子系统(七):扩散方向模型
  • 下一篇:Qt Quick 粒子系统(八):发射随机性——位置、速度与角度
赞(0)
未经允许不得转载:171主机测评 » Qt Quick 粒子系统(七):扩散方向模型
分享到: 更多 (0)

评论 抢沙发

  • 昵称 (必填)
  • 邮箱 (必填)
  • 网址