本系列所有文章可以在這里查看[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屬性的應用,然后是粒子群組等十分實用的技術。
來看看我們熟悉的選擇框:

不多說,進主題~
(1)Dynamic Comparison
左邊是由我們的粒子系統產生的1000個粒子,右邊是我們使用Image模擬的粒子,在運行時動態創建,尺寸為32X32,數量也是1000個。這個小例子名為動態比較,因為這里的ImageParticle和Image都是動態創建的,但是性能差異卻很大。

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屬性。

點擊左鍵我們可以 停止/重新開始 渲染,點擊右鍵我們可以 暫停/繼續 渲染。
代碼也很簡短,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 -> ""
(3)Timed group changes
這個例子主要展示了ParticleGroup的用法,還記不記得我們曾經在[Affectors](http://blog.csdn.net/cloud_castle/article/details/33723715)中的GroupGoal例子中接觸過這個ParticleGroup。
這個例子展示了升起的煙花:

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。

當我們點擊屏幕時,會有幾束粒子向四周發散。當然我們可以使用多個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的情況。

該例子最先使用一張黑框里面帶光點的粒子作為圖像粒子展示,接著使用純黑框加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