本系列所有文章可以在这里查看[http://blog.csdn.net/cloud_castle/article/category/2123873](http://blog.csdn.net/cloud_castle/article/category/2123873)
接上文[Qt5官方demo解析集13——Qt Quick Particles Examples - Image Particles](http://blog.csdn.net/cloud_castle/article/details/35786691)
一转眼就到了我们粒子系列的最后一个demo了,既然是System,第一个小例子就给我们介绍了“模拟”一个粒子系统的方式,接着又向我们介绍了running属性的应用,然后是粒子群组等十分实用的技术。
来看看我们熟悉的选择框:
![](https://box.kancloud.cn/2016-01-18_569cbd067a81e.jpg)
不多说,进主题~
(1)Dynamic Comparison
左边是由我们的粒子系统产生的1000个粒子,右边是我们使用Image模拟的粒子,在运行时动态创建,尺寸为32X32,数量也是1000个。这个小例子名为动态比较,因为这里的ImageParticle和Image都是动态创建的,但是性能差异却很大。
![](https://box.kancloud.cn/2016-01-18_569cbd068d464.jpg)
dynamiccomparison.qml:
~~~
import QtQuick 2.0
import QtQuick.Particles 2.0
Rectangle {
id: root
color: "black"
width: 640
height: 480
ParticleSystem { // 首先是我们的粒子系统
id: sys
}
ImageParticle { // 图像粒子
system: sys
source: "qrc:///particleresources/glowdot.png"
color: "white"
colorVariation: 1.0 // 多彩化
alpha: 0.1
entryEffect: ImageParticle.None // 默认值为ImageParticle.Fade,即粒子在进入与消失时透明度为0
} // 这里取消了这种默认设置
// 还可以设置为ImageParticle.Scale,使粒子在进入与消失时尺寸为0
Emitter {
id: emitter
system: sys
width: parent.width/2
velocity: PointDirection {y: 72; yVariation: 24}
lifeSpan: 10000
emitRate: 1000
enabled: false // 先关闭这个Emitter使其在我们需要的时候进行发射
size: 32
}
//! [fake]
Item { // 下面使用Item和Component来模拟我们的Emitter与ImageParticle
id: fakeEmitter
function burst(number) { // 使用JavaScript函数拟Emitter
while (number > 0) {
var item = fakeParticle.createObject(root); // 动态创建fakeParticle的实例化对象,并将Rectangle作为其父对象
item.lifeSpan = Math.random() * 5000 + 5000; // 取值范围为 (0.5,1)*10000 的生命周期
item.x = Math.random() * (root.width/2) + (root.width/2); // (root.width/2 - root.width)矩形右半部分
item.y = 0;
number--; // 循环创建number个实例
}
}
Component { // 使用组件模拟粒子
id: fakeParticle
Image {
id: container
property int lifeSpan: 10000 // 为了实现透明度以及下落动画(如果在createObject时带上初始化属性,这里的值可以随便设置)
width: 32
height: 32
source: "qrc:///particleresources/glowdot.png" // 我们使用Image也可以使用这张图
y: 0
PropertyAnimation on y {from: -16; to: root.height-16; duration: container.lifeSpan; running: true} // 实现匀速下落,如果加入缓和曲线可实现更复杂的下落效果
SequentialAnimation on opacity {
running: true
NumberAnimation { from:0; to: 1; duration: 500} // 前0.5秒由透明变得不透明
PauseAnimation { duration: container.lifeSpan - 1000} // 暂停动画
NumberAnimation { from:1; to: 0; duration: 500} // 最后0.5秒由不透明变成透明
ScriptAction { script: container.destroy(); } // 我们可以使用ScriptAction为动画加入一段脚本,这里单纯地释放了这个组件
}
}
}
}
//! [fake]
//Hooked to a timer, but click for extra bursts that really stress performance
Timer { // 按作者的说法,这里使用定时器而不是响应点击是因为那样实在是太伤性能
interval: 10000 // 因此我在这里尝试了一下,如果点击后使用我们自定义的“fakeEmitter”发射1000个粒子,卡顿明显
triggeredOnStart: true // 但是如果使用粒子系统的Emitter来发射,十分流畅
repeat: true // 可以看出Qt在粒子系统性能优化上所做的工作
running: true
onTriggered: {
emitter.burst(1000);
fakeEmitter.burst(1000);
}
}
Text {
anchors.left: parent.left
anchors.bottom: parent.bottom
text: "1000 particles"
color: "white"
MouseArea {
anchors.fill: parent
onClicked: emitter.burst(1000);
}
}
Text {
anchors.right: parent.right
anchors.bottom: parent.bottom
text: "1000 items"
color: "white"
MouseArea {
anchors.fill: parent
onClicked: fakeEmitter.burst(1000);
}
}
}
~~~
(2)StartStop
这个例子向我们展示了ParticleSystem的running与pause属性。
![](https://box.kancloud.cn/2016-01-18_569cbd06aaed7.jpg)
点击左键我们可以 停止/重新开始 渲染,点击右键我们可以 暂停/继续 渲染。
代码也很简短,startstop.qml:
~~~
import QtQuick 2.0
import QtQuick.Particles 2.0
Rectangle {
width: 360
height: 540
color: "black"
Text {
text: "Left click to start/stop\nRight click to pause/unpause"
color: "white"
font.pixelSize: 24
}
MouseArea {
anchors.fill: parent
acceptedButtons: Qt.LeftButton | Qt.RightButton // 由于默认只响应左键,我们需要设置该属性接受2个按键
onClicked: {
if (mouse.button == Qt.LeftButton)
particles.running = !particles.running // running为停止和重新开始
else
particles.paused = !particles.paused; // paused为暂停和继续
}
}
ParticleSystem {
id: particles
running: false // 初始化将ParticleSystem停止
}
ImageParticle {
anchors.fill: parent
system: particles
source: "qrc:///particleresources/star.png"
sizeTable: "qrc:/images/sparkleSize.png" // 这个我们接触过了,使用一维图像的透明度来决定粒子生命周期内的尺寸变化
alpha: 0
colorVariation: 0.6
}
Emitter {
anchors.fill: parent
system: particles
emitRate: 2000
lifeSpan: 2000
size: 30
sizeVariation: 10
}
}
~~~
sparkleSize.png -> "![](https://box.kancloud.cn/2016-01-18_569cbd06da239.jpg)"
(3)Timed group changes
这个例子主要展示了ParticleGroup的用法,还记不记得我们曾经在[Affectors](http://blog.csdn.net/cloud_castle/article/details/33723715)中的GroupGoal例子中接触过这个ParticleGroup。
这个例子展示了升起的烟花:
![](https://box.kancloud.cn/2016-01-18_569cbd06eafec.jpg)
timedgroupchanges.qml:
~~~
import QtQuick 2.0
import QtQuick.Particles 2.0
Rectangle {
width: 360
height: 600
color: "black"
ParticleSystem {
anchors.fill: parent
id: syssy
//! [0]
ParticleGroup { // 将下面定义的图像粒子"fire"添加进一个粒子组
name: "fire"
duration: 2000
durationVariation: 2000 // 经过(0,4)秒后,进入"splode"状态
to: {"splode":1}
}
//! [0]
//! [1]
ParticleGroup {
name: "splode" // "splode"同样在下方定义
duration: 400 // 0.4秒后进入"dead"状态
to: {"dead":1}
TrailEmitter { // 该粒子带有一个TrailEmitter,用来发射"works"粒子以跟随"splode"粒子,形成烟花的尾焰效果
group: "works"
emitRatePerParticle: 100 // 跟随比例
lifeSpan: 1000
maximumEmitted: 1200
size: 8
velocity: AngleDirection {angle: 270; angleVariation: 45; magnitude: 20; magnitudeVariation: 20;}
acceleration: PointDirection {y:100; yVariation: 20} // 向四周扩散并向下飘落
}
}
//! [1]
//! [2]
ParticleGroup { // 在"dead"状态调用worksEmitter向四周发射爆裂的烟花
name: "dead"
duration: 1000
Affector {
once: true
onAffected: worksEmitter.burst(400,x,y) // 这里的x,y是当前这个ParticleGroup的坐标值
}
}
//! [2]
Timer { // 间隔6秒的定时器,用来调用第一个Emitter
interval: 6000
running: true
triggeredOnStart: true
repeat: true
onTriggered:startingEmitter.pulse(100); // burst为一次使能,而pulse为一段时间使能
}
Emitter {
id: startingEmitter // 上升火焰发射器
group: "fire"
width: parent.width
y: parent.height
enabled: false
emitRate: 80
lifeSpan: 6000
velocity: PointDirection {y:-100;}
size: 32
}
Emitter { // 爆裂火焰发射器
id: worksEmitter
group: "works"
enabled: false
emitRate: 100
lifeSpan: 1600
maximumEmitted: 6400
size: 8
velocity: CumulativeDirection {
PointDirection {y:-100}
AngleDirection {angleVariation: 360; magnitudeVariation: 80;}
}
acceleration: PointDirection {y:100; yVariation: 20}
}
ImageParticle {
groups: ["works", "fire", "splode"]
source: "qrc:///particleresources/glowdot.png"
entryEffect: ImageParticle.Scale // 为粒子的进入与消失添加尺寸的变化,进入与消失时尺寸为0
}
}
}
~~~
(4)Dynamic Emitters
当我们的程序运行在条件比较苛刻的平台时,可以将Emitter定义在一个组件中,并在这个组件中加入一个定时器,使得它在工作一段时间后释放掉。另一方面,无论何时当我们觉得QML提供的类型或者属性都不能满足特定需要的时候,我们都可以尝试使用JavaScript进行扩展。这个例子就向我们展示了一个使用JavaScript扩展的Emitter。
![](https://box.kancloud.cn/2016-01-18_569cbd070a86a.jpg)
当我们点击屏幕时,会有几束粒子向四周发散。当然我们可以使用多个Emitter并定义不同的速度方向来达到此效果,不过这样未免繁琐。
dynamicemitters.qml:
~~~
import QtQuick 2.0
import QtQuick.Particles 2.0
Rectangle {
id: root
color: "black"
width: 640
height: 480
ParticleSystem {
id: sys
}
ImageParticle {
system: sys
source: "qrc:///particleresources/glowdot.png"
color: "white"
colorVariation: 1.0
alpha: 0.1
}
Component { // 我们将Emitter定义在一个组件中,以对其进行扩展
id: emitterComp
Emitter { // 这个Emitter为根项目
id: container
Emitter { // 这个Emitter有些类似TrailEmitter,但它不是跟随每个粒子,而是每次父对象触发时被触发一次
id: emitMore // 这样为父对象的每个光束上添加一个散射效果
system: sys // 要注意它的x ,y等基本属性是由父对象传递的
emitRate: 128
lifeSpan: 600
size: 16
endSize: 8
velocity: AngleDirection {angleVariation:360; magnitude: 60}
}
property int life: 2600 // 定义了Emitter的生命周期。注意lifeSpan是粒子的生命周期,别弄混了
property real targetX: 0 // 目标坐标
property real targetY: 0
function go() { // 定义一个函数用来调用该Emitter
xAnim.start();
yAnim.start();
container.enabled = true
}
system: sys // 以下是Emitter的常规属性
emitRate: 32
lifeSpan: 600
size: 24
endSize: 8
NumberAnimation on x { // 为x添加动画,从当前坐标x移动到targetX
id: xAnim;
to: targetX
duration: life
running: false
}
NumberAnimation on y {
id: yAnim;
to: targetY
duration: life
running: false
}
Timer { // 最后添加一个定时器,在Emitter结束生命周期后释放
interval: life
running: true
onTriggered: container.destroy();
}
}
}
function customEmit(x,y) { // 这个JavaScript函数用来对组件的属性赋值,以及目标坐标的计算
//! [0]
for (var i=0; i<8; i++) { // 一共创建了8个Emitter的实例化对象
var obj = emitterComp.createObject(root);
obj.x = x
obj.y = y
obj.targetX = Math.random() * 240 - 120 + obj.x // 目标坐标在以当前坐标为中心的边长为240的矩形内
obj.targetY = Math.random() * 240 - 120 + obj.y
obj.life = Math.round(Math.random() * 2400) + 200 // 给每个Emitter一个相对随机的生命周期
obj.emitRate = Math.round(Math.random() * 32) + 32 // Math.round()四舍五入
obj.go(); // 调用其内部的go()函数
}
//! [0]
}
Timer { // 每10秒在屏幕的任意地方触发一次
interval: 10000
triggeredOnStart: true
running: true
repeat: true
onTriggered: customEmit(Math.random() * 320, Math.random() * 480)
}
MouseArea { // 点击触发,将mouse.x,mouse.y赋值给Emitter的x与y
anchors.fill: parent
onClicked: customEmit(mouse.x, mouse.y);
}
Text {
anchors.horizontalCenter: parent.horizontalCenter
text: "Click Somewhere"
color: "white"
font.pixelSize: 24
}
}
~~~
(5)Multiple Painters
通过这个例子,Qt向我们展示了同一个Emitter发射多个Particles的情况。
![](https://box.kancloud.cn/2016-01-18_569cbd071e662.jpg)
该例子最先使用一张黑框里面带光点的粒子作为图像粒子展示,接着使用纯黑框加glowdot模拟了之前的效果。
multiplepainters.qml:
~~~
import QtQuick 2.0
import QtQuick.Particles 2.0
Rectangle {
id: root
width: 360
height: 600
color: "darkblue"
property bool cloneMode: false // 定义了一个克隆模式的属性
ParticleSystem {
id: sys
}
MouseArea {
anchors.fill: parent
onClicked: cloneMode = !cloneMode; // 点击切换
}
Text {
anchors.horizontalCenter: parent.horizontalCenter
text: "Click to Toggle"
color: "white"
font.pixelSize: 24
}
Emitter {
system: sys
y:root.height + 20 // 由于默认的粒子产生带有一个透明度的变化过程,+ 20就看不到这个效果了
width: root.width // 当然我们设置粒子的entryEffect也是可以实现上面的要求,但这里就需要设置三次
emitRate: 200
lifeSpan: 4000
startTime: 4000
velocity: PointDirection { y: -120; }
}
ImageParticle { // 首先仅显示这个粒子,它是一个黑框中带光点的图片
system: sys
visible: !cloneMode // 初始化设置为可见
source: "qrc:/images/particle2.png"
}
ImageParticle { // 一个纯黑框
system: sys
visible: cloneMode // 初始化不可见
z: 0
source: "qrc:/images/particle3.png"
}
ImageParticle { // 光点,由于这三个ImageParticle都没有指定group,那么Emitter会在每个释放粒子的位置上释放3个粒子
system: sys
clip: true // 特定作用范围的粒子需要设置该属性,实现有无光点的对比
visible: cloneMode // 初始化不可见
y: 120 // 作用范围
height: 240
width: root.width
z: 1 // 为了不被上面的粒子覆盖
source: "qrc:///particleresources/glowdot.png"
}
}
~~~
- 前言
- 1——Fortune Server/Client
- 2——Multicast Sender/Receiverz
- 3——Broadcast Sender/Receiver
- 4——Blocking Fortune Client
- 5——Threaded Fortune Server
- 5(总结)——Fortune例程的各个实现区别
- 6——Loopback Example
- 7——Analog Clock Example
- 8——Shaped Clock Example
- 9——Analog Clock Window Example
- 10——Qt Quick Particles Examples - Emitters
- 11——Qt Quick Particles Examples - Affectors
- 12——Qt Quick Particles Examples - CustomParticles
- 13——Qt Quick Particles Examples - Image Particles
- 14——Qt Quick Particles Examples - System
- 15——Chapter 1: Creating a New Type
- 16——Chapter 2: Connecting to C++ Methods and Signals
- 17——Chapter 3: Adding Property Bindings
- 18——Chapter 4: Using Custom Property Types
- 19——Chapter 5: Using List Property Types
- 20——Chapter 6: Writing an Extension Plugin
- 21——Extending QML - Adding Types Example
- 22——Extending QML - Object and List Property Types Example
- 23——Extending QML - Inheritance and Coercion Example
- 24——Extending QML - Default Property Example
- 25——Extending QML - Methods Example
- 26——Extending QML - Grouped Properties Example
- 27——Extending QML - Attached Properties Example
- 28——Extending QML - Signal Support Example
- 29——Extending QML - Property Value Source Example
- 30——Extending QML - Binding Example
- 31——StocQt
- 32——Qt Quick Examples - Threading
- 33——Qt Quick Examples - Window and Screen
- 34——Concentric Circles Example
- 35——Music Player
- 36——Wiggly Example
- 37——Vector Deformation