前言
粒子系统用于模拟一些特定的模糊效果,如爆炸、烟火、雪花、水流等。使用传统的渲染技术实现粒子效果比较困难,但是使用QML粒子系统能十分方便的实现各种粒子效果,使你的界面更加炫酷,动感。
QML中的粒子系统
QML粒子系统的核心是ParticleSystem,用于控制共享时间线。一个场景可以有多个粒子系统,每一个都有自己独立的时间线。粒子由粒子发射器(Emitter)元素发射,使用粒子画笔(ParticlePainter)进行可视化显示,它可以是一张图片、一个QML项或者一个着色器程序。Emitter还使用向量空间定义了粒子方向。粒子一旦发射,就脱离了Emitter的控制。粒子模块提供了粒子控制器(Affector),它可以控制已发射粒子的参数。系统中的粒子可以通过粒子组(ParticleGroup)共享时间变换,默认情况下,粒子都属于空组。
粒子系统主要类如下:
- 粒子系统(ParticleSystem):管理发射器共享时间线。
- 粒子发射器(Emiiter):向系统中发射逻辑粒子。
- 粒子画笔(ParticlePainter):使用粒子画笔绘制粒子。
- 方向(Direction):已发出粒子使用的空间向量。
- 粒子组(ParticleGroup):每个粒子都是一个粒子组的成员。
- 粒子控制器(Affector):控制已发射的粒子。
粒子系统ParticleSystem
ParticleSystem用于控制共享时间线,它将ParticlePainter,Emitter,Affector等元素联合起来实现粒子效果,也就是说其他类型想要交互的话就必须在同一个ParticleSystem中。
导入粒子系统模块:import QtQuick.Particles 2.0
要使用粒子系统第一步要创建一个粒子系统:
ParticleSystem { id: particleSystem }
ParticleSystem包含以下属性:
- empty: 表示系统中是否还有活着的粒子;
- particleStates:粒子动画状态,你可以给每一个粒子组的每一个粒子使用一个动画精灵;
- paused: 粒子系统是否暂停;
- running:粒子系统是否在这运行。
ParticleSystem提供以下方法:
- void pause():暂停粒子系统;
- void reset():丢弃所有已经粒子;
- void restart():停止所有粒子系统,并重新启动;
- void resume():当粒子系统处于暂停状态时,恢复粒子系统运行;
- void start():启动粒子系统;
- void stop():关闭粒子系统。
粒子发射器Emitter
Emitter向粒子系统中发射逻辑粒子,这些粒子都有自己的轨迹和生命周期,但他们是不可见的,想要可见的话就要用到ParticlePainter了。Emitter定义了粒子的发射区域以及相关发射参数并使用system属性将自己与一个粒子系统关联起来。
使用粒子系统的第二步就是要创建一个粒子发射器:
Emitter { id: emitter; system: particleSystem }
Emitter包含以下属性:
- acceleration : 粒子的起始加速度
- emitRate : 发射速率(每秒发射多少个粒子,默认值为10)
- enabled : 发射器是否可用(默认值为true)
- endSize : 粒子生命周期结束时大小(默认值为-1)
- group : 逻辑粒子群(默认为“”空字符串)
- lifeSpan : 粒子生命周期,单位毫秒(默认值为1000)
- lifeSpanVariation : 粒子生命周期误差区间
- maximumEmitted : 粒子最大发射量
- shape : 粒子的发射区域(默认值为Emitter的填充矩形)
- size : 粒子初始大小
- sizeVariation : 粒子初始大小误差区间
- startTime : 延迟发射时间
- system : 粒子系统
- velocity : 粒子发射速率
- velocityFromMovement : 粒子的发射速率会叠加Emitter运动的速率
Emitter提供以下方法:
void burst(int count, int x, int y):立即向xy点发射count个粒子
void burst(int count):立即发射count个粒子
void pulse(int duration):当发射器未启用时,临时起动发射器duration毫秒
QML还提供一个叫TrailEmitter的发射器,该发射器的特点是起始位置是基于其他粒子的,他会跟随一个粒子移动。例如你需要创建一个跟随一团火焰的烟,你可能就需要用到这个发射器了。
粒子画笔ParticlePainter
使用Emitter发射的只是逻辑粒子,我们还需要使用粒子画笔ParticlePainter对粒子进行可视化渲染。ParticlePainter是个基本类型,本身并不渲染任何东西,我们需要使用ParticlePainter的子类进行渲染。ParticlePainter的子类包括:ItemParticle、ImageParticle和CustomParticle。
1.ItemParticle
ItemParticle使用QML的Item来渲染粒子,我们可以给ItemParticle的delegate(代理)属性设置一个Item,这样每个粒子都会使用该Item进行渲染。同样,我们也需要使用ItemParticle的system属性将ParticleSystem与ItemParticle关联起来。
ItemParticle { system: particleSystem; delegate: Rectangle { } }
ItemParticle包含以下属性:
- delegate : 将为每个逻辑粒子创建委托实例,并随之移动;
- fade : 粒子是否在生命周期结束时自动淡出或淡出
ItemParticle提供以下接口:
- void freeze(Item item):暂停逻辑粒子代表的时间流,允许您控制它的移动;
- void give(Item item):获取Item的控制权,该Item将断开和逻辑粒子的关联;
- void take(Item item, bool prioritize)
- void unfreeze(Item item):重新启动项目表示的逻辑粒子的时间流,允许它再次被粒子系统移动。
2.ImageParticle
ImageParticle使用图像来渲染逻辑粒子,图像能够上色、旋转、变形或者添加精灵动画。
ImageParticle函数以下属性:
alpha : 图像的alpha值(0-1.0)
alphaVariation : 图像的alpha值误差区间
autoRotation : 自动旋转
blueVariation : 图像蓝色通道变化(0-1)
color : 图像颜色
colorTable : 纹理图像,原图像将与纹理图像混合
colorVariation : 图像颜色误差区间
entryEffect : 粒子创建和消失时的特效
greenVariation : 图像绿色通道变化
opacityTable : 纹理图像,图像的opacity属性将与纹理图像混合
redVariation : 图像红色通道变化
rotation : 图像旋转角度
rotationVariation : 图像旋转角度误差区间
rotationVelocity : 图像旋转速率
rotationVelocityVariation : 图像旋转速率误差区间
sizeTable : 纹理图像
source : 图像
sprites : 精灵动画
spritesInterpolate :平滑精灵动画
status : 加载图像的状态
xVector : StochasticDirection
yVector : StochasticDirection
3.CustomParticle
除了ItemParticle和ImageParticle还有基于着色器的粒子CustomParticle。CustomPartile使用OpenGL着色器语言GLSL定义。
CustomParticle提供以下属性:
- fragmentShader : 片段着色器
- vertexShader : 顶点着色器
一个简单的例子
我们从一个简单的例子开始,在该例子中我们创建ParticleSystem和Emitter并用ItemParticle渲染粒子,然后将他们联系起来。代码如下:
import QtQuick 2.0
import QtQuick.Particles 2.0 Item { ParticleSystem {
id: particleSystem
} Emitter {
id: emitter
anchors.centerIn: parent
width: 160
height: 80
system: particleSystem
emitRate: 10
lifeSpan: 2000
lifeSpanVariation: 500
size: 16
endSize: 32
} ItemParticle {
system: particleSystem
delegate: Rectangle {
id: particleRect
width: 10
height: 10
color: "red"
radius: 10
}
}
}
首先我们创建ParticleSystem组件。
然后创建Emitter组件,并设定Emitter粒子发射区域以及相关发射参数。Emitter使用system属性将自己与一个粒子系统关联起来。在这个例子中发射器定义了一个160*80的发射区域,该区域每秒发射10个粒子(emitRate:10),每个粒子的生命周期是1000ms(lifeSpan:1000),生命周期变动区间为500ms(lifeSpanVariation:500),每个粒子发出时起始大小为16px(size:16),消失时大小为32px(endSize:32)。
最后创建ParticlePainter子类型组件。发射器发射的只是逻辑上的粒子,每个粒子都要通过ParticlePainter绘制出来,实现可视化显示。在这个例子中,我们使用了ItemParticle类型。ItemParticle是ParticlePainter的一个子类,它可以设置一个代理(delegate)用于渲染每个粒子。我们同样使用system属性将ItemParticle和ParticleSystem关联起来。
属性参数说明:
- emitRate:每秒发射出的粒子数(默认为10);
- lifeSpan:粒子生命周期毫秒数(默认为1000);
- lifeSpanVariation:粒子生命周期变动区毫秒数(在lifeSpan设置的值上下浮动多少);
- size:粒子初始化大小(默认为16);
- endSize:粒子消失时大小(默认为16)。
除了ItemParticle,我们还可以使用ImageParticle。ImageParticle使用图片进行渲染,我们可以设置其source属性指定图片,如如下代码:
ImageParticle {
system: particleSystem
source: "qrc:/star.png"
}
运行效果如下:
我们还可以给粒子设置颜色,代码如下:
ImageParticle {
system: particleSystem
source: "qrc:/star.png"
color: "#FFD700"
colorVariation: 0.6
}
我们设置粒子颜色为"#FFD700"金色,且允许有60%的误差。
显示效果如下:
为了让星星更生动,我们还可以旋转粒子,代码如下:
ImageParticle {
system: particleSystem
source: "qrc:/star.png"
color: "#FFD700"
colorVariation: 0.6
rotation: 15
rotationVariation: 5
rotationVelocity: 45
rotationVelocityVariation: 15
}
将每个粒子顺时针旋转15°,另外有一个5°的误差范围。然后将这些粒子继续以每秒45°的速度旋转,也会有一个15°的误差范围。
我们还可以修改粒子进入场景的效果。当粒子生命周期开始时,就会应用这个效果。在这个例子中,我们添加一个缩放效果:
ImageParticle {
system: particleSystem
source: "qrc:/star.png"
color: "#FFD700"
colorVariation: 0.6
rotation: 15
rotationVariation: 5
rotationVelocity: 45
rotationVelocityVariation: 15
entryEffect: ImageParticle.Scale
}
效果如下:
粒子方向
我们还可以设置粒子轨迹的方向。轨迹取决于一个指定的向量空间,该向量定义了粒子的速度和加速度,以及一个随机的方向。QML提供了三种不同的向量空间,用于定义粒子的速度和加速度:
- 点方向(PointDirection):使用x和y值定义的方向。
- 角度方向(AngleDirection):使用角度定义方向。
- 目标方向(TargetDirection):使用一个目标点坐标定义方向。
1.AngleDirection
要使用AngleDirection,我们需要将其赋值给Emitter的velocity属性:
velocity: AngleDirection { }
粒子发射角度使用angle属性定义。angle属性的取值范围是[0, 360],0为水平向右。
在我们例子中我们希望粒子水平向右发射,因此,angle: 0,粒子发射误差范围则是15度。
velocity: AngleDirection {
angle: 0
angleVariation: 15
}
现在我们设置好了粒子方向,下面继续设置粒子速度。粒子速度由magnitude属性决定。magnitude属性单位是像素/秒。如果我们的场景宽度是640px,那么将magnitude设置为100还不错,这就意味着,粒子平均要消耗6.4秒从场景一段移动到另一端。为了让粒子速度更有趣,我们还要设置magnitudeVariation属性,这会为该速度设置一个可变的范围区间:
velocity: AngleDirection {
magnitude: 100
magnitude: 50
}
下面是Emitter完整代码:
Emitter {
id: emitter
anchors.left: parent.left
anchors.verticalCenter: parent.verticalCenter
width: 1
height: 1
system: particleSystem
emitRate: 10
lifeSpan: 6400
lifeSpanVariation: 400
size: 32
velocity: AngleDirection {
angle: 0
angleVariation: 15
magnitude: 100
magnitudeVariation: 50
}
}
我们将magnitude设置为100,因此粒子的平均生命周期为6.4秒。另外,我们将发射器的长宽都设置为1px,意味着所有粒子都将从同一位置发射,也就是相同的轨迹起点。
运行效果如下:
接下来我们看加速度。加速度为每一个粒子添加一个加速度向量,该向量会随着时间流逝而改变速度。例如,我们创建一个类似星轨的轨迹,为了达到这一目的,我们将方向修改为-45°,并且移除速度变量区间:
velocity: AngleDirection {
angle: -45
magnitude: 100
}
加速度方向为90°向下,数值为25:
acceleration: AngleDirection {
angle: 90
magnitude: 25
}
Emitter代码如下:
Emitter {
id: emitter
anchors.left: parent.left
anchors.verticalCenter: parent.verticalCenter
width: 1
height: 1
system: particleSystem
emitRate: 10
lifeSpan: 6400
lifeSpanVariation: 400
size: 32
velocity: AngleDirection {
angle: -45
magnitude: 100
}
acceleration: AngleDirection {
angle: 90
magnitude: 25
}
}
运行效果如下图:
2.PointDirection
PointDirection使用x、y值导出向量空间。例如,你想让粒子轨迹沿着45°角方向,那么就要将xy值设置成相同的值。例如,我们希望粒子轨迹从左向右,形成一个15°的角,为了设置粒子轨迹,我们首先需要将PointDirection赋值给Emitter的velocity属性:
velocity: PointDirection { }
指定粒子速度为100px/s,我们将x的值设置为100,15°是90°的1/6,因此,我们将y的变化范围设置(yVariation)为100/6:
代码如下:
Emitter {
id: emitter
anchors.left: parent.left
anchors.verticalCenter: parent.verticalCenter
width: 1
height: 1
system: particleSystem
emitRate: 10
lifeSpan: 6400
lifeSpanVariation: 400
size: 32
velocity: PointDirection {
x: 100
y: 0
xVariation: 0
yVariation: 100 / 6
}
}
运行效果如下:
3.TargetDirection
TargetDirection使用某个项目指定一个目标点,targetItem指定目标位置关联Item,Item的中心会成为目标点。
首先我们在界面右侧中心设置一个目标点:
Rectangle {
id: targetPoint
anchors.right: parent.right
anchors.verticalCenter: parent.verticalCenter
width: 10
height: 10
radius: 10
color: "red"
}
我们将TargetDirection的targetItem属性设置为targetPoint,targetVariation的值为100/6,这大约会形成一个15°的角。代码如下:
Emitter {
id: emitter
anchors.left: parent.left
anchors.verticalCenter: parent.verticalCenter
width: 1
height: 1
system: particleSystem
emitRate: 10
lifeSpan: 6400
lifeSpanVariation: 400
size: 32
velocity: TargetDirection {
targetItem: targetPoint
targetVariation: 100 / 6
magnitude: 100
}
}
运行效果如下:
粒子控制
粒子由发射器发射,一旦粒子发射出来,发射器的任务也就完成了,不会再对粒子有任何影响。如果我们需要控制已发射的粒子,需要使用粒子控制器(Affector)。
QML提供如下控制器:
- Age:改变粒子生命周期,一般用于提前结束粒子的生命周期;
- Attractor:将粒子吸引到一个指定的点;
- Friction:按比例降低粒子的当前速率;
- Gravity:添加一个一定角度的加速度;
- Turbulence:为粒子添加一个图像噪音;
- Wander:随机改变粒子轨迹;
- GroupGoal:改变粒子组状态;
- SpriteGoal:改变精灵粒子状态。
1.Age
Age可以改变粒子生命周期,lifeLeft属性指定粒子还能存活多少时间,代码如下:
Age {
anchors.centerIn: parent
width: 140
height: 120
system: particleSystem
advancePosition: true
lifeLeft: 3200
once: true Rectangle {
anchors.fill: parent
color: "transparent"
border.color: "green"
border.width: 2
}
}
我们利用Age控制器将粒子生命周期缩短到3200ms。当粒子进入控制器范围时,其生命周期只剩下3200ms。将advancePosition设置为true,我们会看到一旦粒子的生命周期只剩下3200ms,粒子又会在其预期的位置从新出现。
运行效果如下:
2.Attractor
Attractor将粒子将粒子吸引到pointX和pointY指定的点,strength属性指定吸引强度,代码如下:
Attractor {
anchors.centerIn: parent
width: 160
height: 70
system: particleSystem
pointX: 0
pointY: 0
strength: 1.0 Rectangle {
anchors.fill: parent
color: "transparent"
border.color: "green"
border.width: 2
}
}
我们的控制器位于界面中间,当粒子进入控制器返回时,控制器会影响粒子向控制器0,0点方向移动。效果如下:
3.Friction
Friction会按一定比列降低粒子速度,代码如下:
Friction {
anchors.centerIn: parent
width: 160
height: 70
system: particleSystem
factor: 0.8
threshold: 25 Rectangle {
anchors.fill: parent
color: "transparent"
border.color: "green"
border.width: 2
}
}
粒子会以factor:0.8的比例降低粒子速度,直到降低到threshold:25px/s。
运行效果如下:
4.Gravity
Gravity为粒子添加一个加速度,代码如下:
Gravity {
anchors.centerIn: parent
width: 160
height: 70
system: particleSystem
magnitude: 50
angle: 90 Rectangle {
anchors.fill: parent
color: "transparent"
border.color: "green"
border.width: 2
}
}
所有进入控制器范围的粒子都会被添加一个向下90°的加速度,值为50.
运行效果如下:
5.Turbulence
Trubulence为粒子添加一个力向量,每个粒子获得的力向量都是随机的,这是由一个噪音图像决定的,使用noiseSource可以自定义噪音图像。strength定义了力向量强度。
代码如下:
Turbulence {
anchors.centerIn: parent
width: 160
height: 70
system: particleSystem
strength: 100 Rectangle {
anchors.fill: parent
color: "transparent"
border.color: "green"
border.width: 2
}
}
运行效果如下:
粒子一旦进入控制器范围,就会发疯一样乱串,而不是按照从左向右的方向保持一个大致轨迹。
6.Wander
Wander控制轨迹。affectedParameter属性指定控制哪一属性(速度/位置/加速度),pace属性指定每秒该属性变化的最大值,xVariance和yVariance指定粒子轨迹x和y坐标的浮动区间。代码如下:
Wander {
anchors.centerIn: parent
width: 160
height: 70
system: particleSystem
affectedParameter: Wander.Position
pace: 200
yVariance: 240 Rectangle {
anchors.fill: parent
color: "transparent"
border.color: "green"
border.width: 2
}
}
控制器作用于粒子轨迹的位置属性,轨迹会以每秒200次的频率在y轴方向上随机振动。
运行效果如下:
粒子组
每一个粒子都是粒子组(ParticleGroup)的成员,通过其name属性区分,当没有指定粒子所属的粒子组时,这些粒子默认也都在一个粒子组,不过name属性值是个空字符串,粒子组主要是为了方便地控制粒子行为状态。
以下代码演示了粒子组的使用方法:
import QtQuick 2.0
import QtQuick.Particles 2.0 Rectangle {
color: "black" ParticleSystem { id: particleSystem } Rectangle {
id: targetPoint
anchors.right: parent.right
anchors.verticalCenter: parent.verticalCenter
width:
height:
radius:
color: "red"
} Emitter {
id: emitter
anchors.left: parent.left
anchors.verticalCenter: parent.verticalCenter
group: "A"
width:
height:
system: particleSystem
emitRate:
lifeSpan:
lifeSpanVariation:
size:
velocity: TargetDirection {
targetItem: targetPoint
targetVariation: /
magnitude:
}
} Emitter {
id: emitter2
anchors.left: parent.left
anchors.bottom: parent.bottom
anchors.bottomMargin:
system: particleSystem
group: "B"
width:
height:
emitRate:
lifeSpan:
lifeSpanVariation:
size:
velocity: AngleDirection {
angle:
magnitude:
}
} Turbulence {
anchors.centerIn: parent
width:
height: parent.height
groups: ["A","B"]
strength:
system: particleSystem
} ImageParticle {
system: particleSystem
groups: "A"
source: "qrc:/star.png"
color: "#FFD700"
colorVariation: 0.6
rotation:
rotationVariation:
rotationVelocity:
rotationVelocityVariation:
entryEffect: ImageParticle.Scale
} ItemParticle {
system: particleSystem
groups: "B"
delegate: Rectangle {
color: "red"
width:
height:
radius:
}
}
}
首先我们将粒子系统里的元素分为AB两组,其中A组由emitter发射,ImageParticle渲染;B组由emitter2发射,ItemParticle渲染,AB两组都使用Tubulence进行控制,随机添加一个力向量,运行效果如下如:
可以看到AB两组元素分别发射并渲染粒子,且同时获得了"扩散"效果。
总结
粒子系统用于模拟很多自然现象,比如云、烟、火、雪花等强有力的工具。QML的粒子系统让我们能轻松完成这些工作。适当的粒子往往会成为用户界面上最吸引人的部分。