本系列所有文章可以在這里查看[http://blog.csdn.net/cloud_castle/article/category/2123873](http://blog.csdn.net/cloud_castle/article/category/2123873)
接上文[Qt5官方demo解析集10——Qt Quick Particles Examples - Emitters](http://blog.csdn.net/cloud_castle/article/details/33306133)
Affectors是Qt官方粒子系統demo中的第二個例程,它是在Emitters上的進一步擴展。我們將看到,通過使用Affectors,我們能夠創造更加靈活的粒子顯示以及交互行為。
首先還是看下介紹:This is a collection of small QML examples relating to using Affectors in the particle system. Each example is a small QML file emphasizing a particular type or feature.
很簡短,告訴我們這個demo依然是由多個使用Affectors的小例子構成。運行后是同樣的選擇框:

一共有10個例子,我們還是從第一個開始:
(1)Age
來看看是個什么效果:進入圖中矩形框的雪花都變小并逐漸消失了。

來看看這個小例子是怎么寫的吧~ age.qml:
~~~
import QtQuick 2.0
import QtQuick.Particles 2.0
Rectangle {
id: root
width: 360
height: 600
color: "white"
ParticleSystem { id: particles }
ImageParticle { // 這里向我們展示另外一種圖像粒子的設置
system: particles
sprites: Sprite { // sprites屬性用來定義一組幀圖片來作為粒子,這樣粒子可以像GIF一樣擁有自己的動畫
name: "snow"
source: "../../images/snowflake.png" // 這是一張具有51幀的雪花圖形
frameCount: 51
frameDuration: 40 // 幀動畫的基本設置
frameDurationVariation: 8
}
}
Emitter {
system: particles
emitRate: 20
lifeSpan: 8000
velocity: PointDirection { y:80; yVariation: 40; } // 加速度下落
acceleration: PointDirection { y: 4 }
size: 36
endSize: 12
sizeVariation: 8
width: parent.width
height: 100
}
MouseArea {
id: ma
anchors.fill: parent
hoverEnabled: true
}
Rectangle { // 這里使用Rectangle作為Age的父類,當然Age可以定義自己的坐標以及區域,但是加入Rectangle可視化效果更好
color: "#803333AA" // 半透明的湛藍色
border.color: "black"
x: ma.mouseX - 36 // 這里用到屬性綁定使得該矩形可以跟隨鼠標的移動,并以鼠標為中心點
y: ma.mouseY - 36
width: 72
height: 72
//! [0]
Age { // Age繼承自Affector,其實在上一篇Emitters中我們就接觸了一個Affector:Turbulence,它可以提供一個氣流的效果,而這里的Age則允許我們改變粒子的生命周期。
anchors.fill: parent
system: particles
once: true // 每個粒子只影響一次
lifeLeft: 1200 // 粒子剩下的時間
advancePosition: false // 退化是否影響位置、速度、和加速度
}
//! [0]
}
}
~~~
雪花圖太長,截一部分好了:

(2)Attractor
這個小例子使用Affector中的Attractor(吸引者)向我們展示了如何使用粒子系統模擬一個黑洞。

可以看到圖中心有一個“黑洞”,靠近黑洞的粒子會被改變運行軌跡,太近的粒子會被吸進去。如果需要我們自己來寫這種速度改變的代碼可能會相當繁瑣,好在QtQuick給我們提供了Attractor這個Affector,來看看它怎么使用的~attractor.qml
~~~
import QtQuick 2.0
import QtQuick.Particles 2.0
Rectangle {
id: root
width: 360
height: 540
color: "black"
Image {
source: "qrc:/images/finalfrontier.png"
anchors.centerIn:parent
}
ParticleSystem {
id: particles
anchors.fill: parent
Emitter { // 星星發射器
group: "stars"
emitRate: 40
lifeSpan: 4000
enabled: true
size: 30
sizeVariation: 10
velocity: PointDirection { x: 220; xVariation: 40 }
height: parent.height // height定義了發射發射區域的高度,否則粒子從(0,0)發出
}
Emitter { // 隕石發射器
group: "roids"
emitRate: 10
lifeSpan: 4000
enabled: true
size: 30
sizeVariation: 10
velocity: PointDirection { x: 220; xVariation: 40 }
height: parent.height
}
ImageParticle { // 星星
id: stars
groups: ["stars"]
source: "qrc:///particleresources/star.png"
color: "white"
colorVariation: 0.5
alpha: 0
}
ImageParticle { // 隕石
id: roids
groups: ["roids"]
sprites: Sprite { // 這里再次使用了幀動畫,由于沒有定義frameDurationVariation,所有隕石的旋轉速度都是相同的
id: spinState
name: "spinning"
source: "qrc:/images/meteor.png"
frameCount: 35
frameDuration: 60
}
}
ImageParticle { // 飛船子彈
id: shot
groups: ["shot"]
source: "qrc:///particleresources/star.png"
color: "#0FF06600"
colorVariation: 0.3
}
ImageParticle { // 尾氣
id: engine
groups: ["engine"]
source: "qrc:///particleresources/fuzzydot.png"
color: "orange"
SequentialAnimation on color { // 屬性動畫
loops: Animation.Infinite
ColorAnimation {
from: "red"
to: "cyan"
duration: 1000
}
ColorAnimation {
from: "cyan"
to: "red"
duration: 1000
}
}
colorVariation: 0.2
}
//! [0]
Attractor { // Affector家族中的一員,可以形成吸引其他粒子的效果
id: gs; pointX: root.width/2; pointY: root.height/2; strength: 4000000; // pointX,pointY是其作為目標點,同其他Affector一樣,設置其x,y,height,weidth改變的是其影響區域
affectedParameter: Attractor.Acceleration // 設置為影響加速度
proportionalToDistance: Attractor.InverseQuadratic // 影響效果與距離的比例關系
}
//! [0]
Age { // 在Attractor周圍再安裝一個Age,因為這里沒有設置lifeLeft,粒子進入該區域變消失了
x: gs.pointX - 8; // Age的影響區域
y: gs.pointY - 8;
width: 16
height: 16
}
Rectangle { // 用矩形畫圓的方法
color: "black"
width: 8
height: 8
radius: 4
x: gs.pointX - 4
y: gs.pointY - 4
}
Image { // 飛行器
source:"qrc:/images/rocket2.png"
id: ship
width: 45
height: 22
//Automatic movement
SequentialAnimation on x { // 屬性動畫,這里使用了彈線軌跡
loops: -1
NumberAnimation{to: root.width-45; easing.type: Easing.InOutQuad; duration: 2000}
NumberAnimation{to: 0; easing.type: Easing.OutInQuad; duration: 6000}
}
SequentialAnimation on y {
loops: -1
NumberAnimation{to: root.height-22; easing.type: Easing.OutInQuad; duration: 6000}
NumberAnimation{to: 0; easing.type: Easing.InOutQuad; duration: 2000}
}
}
Emitter { // 尾氣粒子
group: "engine"
emitRate: 200
lifeSpan: 1000
size: 10
endSize: 4
sizeVariation: 4
velocity: PointDirection { x: -128; xVariation: 32 }
height: ship.height
y: ship.y
x: ship.x
width: 20
}
Emitter { // 子彈粒子
group: "shot"
emitRate: 32
lifeSpan: 1000
enabled: true
size: 40
velocity: PointDirection { x: 256; }
x: ship.x + ship.width
y: ship.y + ship.height/2
}
}
}
~~~
(3)Custom Affector
在這個例子中我們將了解到如何實現一個自定義的Affector,以及通過這個Affector實現落葉飄落的效果。當Affector的子類都不能滿足我們的需求的時候,這種方式就顯得尤為重要了。

直接上代碼,由于我會調試這些代碼因此其圖片的路徑被我改成了資源路徑,希望沒有影響到大家。customaffector.qml:
~~~
import QtQuick 2.0
import QtQuick.Particles 2.0
Item { // 如果這個文件作為一個組件,Image作為根項目將使使用這個組件的人可以對其任意修改
width: 360
height: 600
Image { // 因此不推薦將Image作為根目錄,而是以Item作為替代,而嵌套Image
source: "qrc:/images/backgroundLeaves.jpg"
anchors.fill: parent
}
ParticleSystem {
anchors.fill: parent
Emitter {
width: parent.width // 粒子出現的點為(0,0)到(360,0)
emitRate: 4
lifeSpan: 14000
size: 80
velocity: PointDirection { y: 60 } // 初始速度
}
Wander { // 一個系統自帶的Affector:Wander(漫步者),它可以用來提供隨機的粒子軌跡,這樣形成了左右晃動的葉子
anchors.fill: parent
anchors.bottomMargin: 100 // 與設置Affector的height類似,確定Wander的影響區域
xVariance: 60 // x方向上的變化率
pace: 60 // 最大步長
}
//! [0]
Affector { // 基本的Affector類不會改變粒子任何屬性,但我們可以在合適的時候發出信號來做出相應的處理
property real coefficient: 0.1 // 自定義屬性“同步系數”和“速度”
property real velocity: 1.5
width: parent.width
height: parent.height - 100 // 底部100像素不再產生影響
onAffectParticles: { // 只要有粒子被該Affector影響,這個handler就被觸發。通過它我們可以定義自己的Affector行為。類似onEmitterParticles,由于使用了javaScript數組以及計算,我們同樣不推薦在包含大量粒子的系統中使用它。
//Linear movement // 這一段是在源碼中被注釋的,它提供了線性搖動的計算
// if (particle.r == 0) {
// particle.r = Math.random() > 0.5 ? -1 : 1;
// } else if (particle.r == 1) {
// particle.rotation += velocity * dt; // 不知道這個dt是什么,只知道是一個比較小的小數...
// if (particle.rotation >= maxAngle)
// particle.r = -1;
// } else if (particle.r == -1) {
// particle.rotation -= velocity * dt;
// if (particle.rotation <= -1 * maxAngle)
// particle.r = 1;
// }
//Wobbly movement
for (var i=0; i<particles.length; i++) { // 這是一個搖擺算法,相對上面的代碼而言更加精妙
var particle = particles[i];
if (particle.r == 0.0) { // 在QML中我們可以將參數定義與賦值放在一起
particle.r = Math.random() + 0.01; // 將 0.01 定義為particle.r的最小值
}
particle.rotation += velocity * particle.r * dt; // 隨機的particle.r保證每片葉子的旋轉角度都是隨機的
particle.r -= particle.rotation * coefficient; // 然后這里通過“同步系數”適當改變particle.r,系數越大,葉片晃動越劇烈。根據QML屬性綁定的原則,當particle.r被改變,particle.rotation隨之改變。正向的旋轉角度使particle.r變小,導致particle.rotation變小,葉片方向旋轉,反之亦然,得到晃動效果
if (particle.r == 0.0) // 如果為0給其一個改變量
particle.r -= particle.rotation * 0.000001;
particle.update = 1;
}
}
}
//! [0]
//! [1]
Affector { // 定義“地面”的摩擦減速效果
x: -60
width: parent.width + 120
height: 100
anchors.bottom: parent.bottom
onAffectParticles: {
for (var i=0; i<particles.length; i++) {
var particle = particles[i];
var pseudoRand = (Math.floor(particle.t*1327) % 10) + 1; // Math.floor取到一個整數,并對10取余。葉子生命周期越長,這個數會越大,也就更容易被“減速”
var yslow = dt * pseudoRand * 0.5 + 1;
var xslow = dt * pseudoRand * 0.05 + 1;
if (particle.vy < 1) // 速度低于 1 則停止
particle.vy = 0;
else
particle.vy = (particle.vy / yslow); // 否則除以摩擦系數
if (particle.vx < 1)
particle.vx = 0;
else
particle.vx = (particle.vx / xslow);
particle.update = true;
}
}
}
//! [1]
ImageParticle {
anchors.fill: parent
id: particles
sprites: [Sprite { // 將多個png賦予圖像粒子的方法
source: "qrc:/images/realLeaf1.png"
frameCount: 1
frameDuration: 1 // 類似“導入者”,其生命周期很短,1ms后它將變成后面的圖像
to: {"a":1, "b":1, "c":1, "d":1} // 有1/4的概率變成"a",1/4的概率變成"b"...后面類似
}, Sprite { // 當該圖像沒有可轉變的內容,它將重復播放自己
name: "a"
source: "qrc:/images/realLeaf1.png"
frameCount: 1 // 我們的單幀靜態圖也就是僅有一幀的連續圖
frameDuration: 10000
},
Sprite {
name: "b"
source: "qrc:/images/realLeaf2.png"
frameCount: 1
frameDuration: 10000
},
Sprite {
name: "c"
source: "qrc:/images/realLeaf3.png"
frameCount: 1
frameDuration: 10000
},
Sprite {
name: "d"
source: "qrc:/images/realLeaf4.png"
frameCount: 1
frameDuration: 10000
}
]
z:4 // 在圖像中的層次
}
}
}
~~~
(4)Friction
在上面的例子中我們看到了如何使用代碼來模擬一個摩擦效果,但是Qt Quick已經為我們提供了一個模擬摩擦效果的Affector,它就是Friction。
這個例子與上面的例子類似:

代碼十分簡練。friction.qml:
~~~
import QtQuick 2.0
import QtQuick.Particles 2.0
Item {
width: 360
height: 600
Image {
source: "qrc:/images/backgroundLeaves.jpg"
anchors.fill: parent
}
ParticleSystem {
anchors.fill: parent
Emitter {
width: parent.width
emitRate: 4
lifeSpan: 14000
size: 80
velocity: PointDirection { y: 160; yVariation: 80; xVariation: 20 } // xVariation給了葉子水平方向上移動的能力,但是達不到wander的“擺動”效果
}
ImageParticle { // 圖像粒子同上
anchors.fill: parent
id: particles
sprites: [Sprite {
source: "qrc:/images/realLeaf1.png"
frameCount: 1
frameDuration: 1
to: {"a":1, "b":1, "c":1, "d":1}
}, Sprite {
name: "a"
source: "qrc:/images/realLeaf1.png"
frameCount: 1
frameDuration: 10000
},
Sprite {
name: "b"
source: "qrc:/images/realLeaf2.png"
frameCount: 1
frameDuration: 10000
},
Sprite {
name: "c"
source: "qrc:/images/realLeaf3.png"
frameCount: 1
frameDuration: 10000
},
Sprite {
name: "d"
source: "qrc:/images/realLeaf4.png"
frameCount: 1
frameDuration: 10000
}
]
width: 100
height: 100
x: 20
y: 20
z:4
}
//! [0]
Friction { // Friction為粒子帶來摩擦效果,我們可以設置一個閾值,使Friction只影響速度大于該閾值的粒子。該閾值默認為0
anchors.fill: parent
anchors.margins: -40
factor: 0.4 // 摩擦系數,值越大摩擦力越大
}
//! [0]
}
}
~~~
(5)Gravity
類似的,除了摩擦力,我們還有一個Affector用來模擬萬有引力。它展示了葉片向地面加速飄落的效果。

圖中的綠色是"地面",可以拖動它360度旋轉,葉片始終向“地面”的中心加速下落。gravity.aml:
~~~
import QtQuick 2.0
import QtQuick.Particles 2.0
Item {
id: window
width: 320; height: 480
Rectangle {
id: sky
anchors.fill: parent // 藍色的背景覆蓋了整個矩形范圍
gradient: Gradient {
GradientStop {
position: 0.0
color: "DeepSkyBlue"
}
GradientStop {
position: 1.0
color: "SkyBlue"
}
}
}
Rectangle { // 因為在后面要實現“地面”的旋轉,所以設置了較大的尺寸
id: ground
width: parent.height * 2
height: parent.height
y: parent.height/2
x: parent.width/2 - parent.height
transformOrigin: Item.Top // 用來設置旋轉和縮放的中心點
rotation: 0
gradient: Gradient {
GradientStop { position: 0.0; color: "ForestGreen"; }
GradientStop { position: 1.0; color: "white"; }
}
}
MouseArea {
anchors.fill: parent
onPositionChanged: { // 該信號在鼠標按下并移動位置時放出,如果不需要按下鼠標,可設置hoverEnabled為true
var rot = Math.atan2(mouseY - window.height/2,mouseX - window.width/2) * 180/Math.PI; // 返回當前鼠標方向矢量與X 軸正方向的夾角
ground.rotation = rot; // 以該角度旋轉
}
}
ParticleSystem { id: sys }
//! [0]
Gravity { // 當使用Gravity時,要注意它對整個場景的吸引力都是相同的,如果角度和加速度恒定,最好直接在Emitter中設置
system: sys // 但在此例中如果直接設置Emitter,角度的計算會比較復雜
magnitude: 32 // 強度
angle: ground.rotation + 90 // 運動方向
}
//! [0]
Emitter {
system: sys
anchors.centerIn: parent
emitRate: 1
lifeSpan: 10000
size: 64
}
ImageParticle {
anchors.fill: parent
system: sys
source: "qrc:/images/realLeaf1.png"
}
}
~~~
(6)GroupGoal
在前面我們學習到我們可以設置ImageParticle的groups屬性,從而讓不同的Emitter發送不同的粒子。更進一步,使用ParticleGroup和GroupGoal可以實現粒子在特定狀態下的跳變。

可以看到,這些紅色的小光點在經過藍色火焰后被點燃成火苗,同時被鼠標滑過的也將被點燃。界面的右上角還有個數字用來記錄被點燃的火苗數。
代碼如下,groupgoal.qml:
~~~
import QtQuick 2.0
import QtQuick.Particles 2.0
Rectangle {
id: root
width: 360
height: 600
color: "black"
property int score: 0 // 設置一個屬性用來記錄分數
Text {
color: "white"
anchors.right: parent.right
text: score
}
ParticleSystem {
id: particles
anchors.fill: parent
// ![unlit]
ParticleGroup { // 這個元素有點類似狀態機的概念,它將一種狀態下的粒子群以打包的形式放在一起,然后通過name跳轉
name: "unlit"
duration: 1000 // 1s后進入下一個狀態
to: {"lighting":1, "unlit":99} // 設置百分之一的光球可以自燃
ImageParticle {
source: "qrc:/images/particleA.png" // 資源文件中的一個光點,有點類似常用的glowdot,但是更大一些
colorVariation: 0.1
color: "#2060160f" // 光點的顏色為紅褐色
}
GroupGoal { // 繼承自Affector,提供特定條件滿足下的狀態跳轉
whenCollidingWith: ["lit"] // 當碰撞到正在燃燒的火苗時
goalState: "lighting" // 跳轉為"lighting"狀態
jump: true // 設置為立刻跳轉
}
}
// ![unlit]
// ![lighting]
ParticleGroup { // 一個過渡狀態,正在被點亮的狀態
name: "lighting"
duration: 100 // 0.1秒后跳轉到"lit"
to: {"lit":1}
}
// ![lighting]
// ![lit]
ParticleGroup { // 被點亮狀態
name: "lit"
duration: 10000 // 終態粒子的生命周期
onEntered: score++; // 分數加一
TrailEmitter { // 使用TrailEmitter構建尾部火焰
id: fireballFlame
group: "flame" // 粒子"flame"是基于下方定義的ImageParticle
emitRatePerParticle: 48 // 每個lit后跟隨48玫"火焰"
lifeSpan: 200 // 生命周期與焰尾長度成正比
emitWidth: 8
emitHeight: 8
size: 24
sizeVariation: 8
endSize: 4 // 尾部體積更小
}
TrailEmitter { // 另一個TrailEmitter用來構建煙霧
id: fireballSmoke
group: "smoke" // smoke在下方定義
// ![lit]
emitRatePerParticle: 120
lifeSpan: 2000 // 較長的生命周期用來進行自己的動畫
emitWidth: 16
emitHeight: 16
velocity: PointDirection {yVariation: 16; xVariation: 16}
acceleration: PointDirection {y: -16} // 煙霧首先向下運動,隨之向上升騰
size: 24
sizeVariation: 8
endSize: 8
}
}
ImageParticle { // 灰色煙霧粒子
id: smoke
anchors.fill: parent
groups: ["smoke"]
source: "qrc:///particleresources/glowdot.png"
colorVariation: 0
color: "#00111111"
}
ImageParticle { // 藍色閆焰苗粒子
id: pilot
anchors.fill: parent
groups: ["pilot"]
source: "qrc:///particleresources/glowdot.png"
redVariation: 0.01
blueVariation: 0.4 // 設置RGB中藍色的變化率
color: "#0010004f"
}
ImageParticle { // 紅色火焰粒子
id: flame
anchors.fill: parent
groups: ["flame", "lit", "lighting"]
source: "qrc:/images/particleA.png"
colorVariation: 0.1
color: "#00ff400f"
}
Emitter { // 用來發射易燃小球
height: parent.height/2
emitRate: 4
lifeSpan: 4000//TODO: Infinite & kill zone // demo中的注釋,TODO表示還要做的事,FIXME表示代碼待修改,XXX表示有待商榷
size: 24
sizeVariation: 4
velocity: PointDirection {x:120; xVariation: 80; yVariation: 50}
acceleration: PointDirection {y:120}
group: "unlit"
}
Emitter { // 用來構建焰苗
id: flamer
x: 100
y: 300
group: "pilot"
emitRate: 80
lifeSpan: 600
size: 24
sizeVariation: 2
endSize: 0
velocity: PointDirection { y:-100; yVariation: 4; xVariation: 4 } // 粒子向上移動形成焰苗的升騰感
// ![groupgoal-pilot]
GroupGoal {
groups: ["unlit"] // 設置被影響的粒子群
goalState: "lit"
jump: true // 直接跳轉,否則默認為過渡時間結束后再跳轉
system: particles
x: -15
y: -55
height: 75
width: 30
shape: MaskShape {source: "qrc:/images/matchmask.png"} // 這張圖片是一個焰苗的圖形,使用它可以使Affector影響一個非矩形區域
}
// ![groupgoal-pilot]
}
// ![groupgoal-ma]
//Click to enflame
GroupGoal {
groups: ["unlit"] // 設置其可以影響的粒子群
goalState: "lighting" // 目標狀態
jump: true
enabled: ma.pressed // 按下事件使能
width: 18 // 作用區域
height: 18
x: ma.mouseX - width/2
y: ma.mouseY - height/2
}
// ![groupgoal-ma]
MouseArea {
id: ma
anchors.fill: parent
}
}
}
~~~
(7)Move
這個例子展示了直接使用Affector影響粒子運動(位置、速度、加速度)的方法。

代碼很簡單,我們大致看一下好了,move.qml:
~~~
import QtQuick 2.0
import QtQuick.Particles 2.0
Rectangle {
width: 360
height: 540
color: "black"
ParticleSystem { // 第一束紅色粒子
anchors.fill: parent
ImageParticle {
groups: ["A"]
anchors.fill: parent
source: "qrc:///particleresources/star.png"
color:"#FF1010"
redVariation: 0.8 // 形成"明紅"到"暗紅"的顏色差異
}
Emitter {
group: "A"
emitRate: 100
lifeSpan: 2800
size: 32
sizeVariation: 8
velocity: PointDirection{ x: 66; xVariation: 20 }
width: 80 // 產生粒子的區域是(0,0)到(80,80)的矩形范圍
height: 80
}
//! [A]
Affector {
groups: ["A"] // Affector作用于A
x: 120 // 影響區域
width: 80
height: 80
once: true
position: PointDirection { x: 120; } // x 增加120
}
//! [A]
ImageParticle { // 第二束綠色粒子
groups: ["B"]
anchors.fill: parent
source: "qrc:///particleresources/star.png"
color:"#10FF10"
greenVariation: 0.8
}
Emitter {
group: "B"
emitRate: 100
lifeSpan: 2800
size: 32
sizeVariation: 8
velocity: PointDirection{ x: 240; xVariation: 60 }
y: 260
width: 10
height: 10
}
//! [B]
Affector {
groups: ["B"]
x: 120
y: 240
width: 80
height: 80
once: true
velocity: AngleDirection { angleVariation:360; magnitude: 72 } // 角度變化范圍和強度
}
//! [B]
ImageParticle { // 第三束藍色粒子
groups: ["C"]
anchors.fill: parent
source: "qrc:///particleresources/star.png"
color:"#1010FF"
blueVariation: 0.8
}
Emitter {
group: "C"
y: 400
emitRate: 100
lifeSpan: 2800
size: 32
sizeVariation: 8
velocity: PointDirection{ x: 80; xVariation: 10 }
acceleration: PointDirection { y: 10; x: 20; }
width: 80
height: 80
}
//! [C]
Affector {
groups: ["C"]
x: 120
y: 400
width: 80
height: 120
once: true
relative: false
acceleration: PointDirection { y: -80; } // 在y方向的加速度下降80
}
//! [C]
}
}
~~~
(8)SpriteGoal
這個例子向我們展示了如何對使用sprites的ImageParticle做特殊的處理,使其在我們想要它改變時進行狀態的跳轉。
如圖是“星際迷航”中的飛船,它將撞毀其接觸到的隕石。

spritegoal.qml:
~~~
import QtQuick 2.0
import QtQuick.Particles 2.0
Item {
id: root
width: 360
height: 540
MouseArea {
id: ma
anchors.fill: parent
}
ParticleSystem { id: sys }
Image {
source: "qrc:/images/finalfrontier.png" // 星際迷航
transformOrigin: Item.Center // 以中心點旋轉,一共有9個點可選,四個邊角,四個邊線中心,以及中心點
anchors.centerIn: parent
NumberAnimation on rotation { // 背景緩慢旋轉
from: 0
to: 360
duration: 200000
loops: Animation.Infinite
}
}
ImageParticle { // 星星粒子
system: sys
groups: ["starfield"]
source: "qrc:///particleresources/star.png"
colorVariation: 0.3
color: "white"
}
Emitter {
id: starField
system: sys
group: "starfield"
emitRate: 80
lifeSpan: 2500
anchors.centerIn: parent
//acceleration: AngleDirection {angleVariation: 360; magnitude: 200}//Is this a better effect, more consistent velocity?
acceleration: PointDirection { xVariation: 200; yVariation: 200; } // 上面是源碼中的注釋,作者留給我們一個問題,這個從中心點向外散射的粒子,是使用AngleDirection還是PointDirection?筆者想了下,以第一行代碼發射的話,所有粒子的速度都將是相同的,而第二行代碼則具有更大的隨機性。以星星的散射而言,第二行代碼更合理。
size: 0
endSize: 80
sizeVariation: 10
}
Emitter { // 隕石的發射器
system: sys
group: "meteor"
emitRate: 12
lifeSpan: 5000
acceleration: PointDirection { xVariation: 80; yVariation: 80; } // 與星星的發射類似
size: 15
endSize: 300 // 增大的endSize形成由遠及進感
anchors.centerIn: parent
}
ImageParticle { // 隕石粒子,由sprites的多幀圖像構成
system: sys
groups: ["meteor"]
sprites:[Sprite {
id: spinState // 自旋隕石
name: "spinning"
source: "qrc:/images/meteor.png"
frameCount: 35
frameDuration: 40
randomStart: true // 從隨意的一幀開始
to: {"explode":0, "spinning":1} // 由于"explode"為0,因此spinning實際上是無限循環。"explode": 0可以不寫。但為了邏輯清楚,加上更好
},Sprite { // 碎裂隕石
name: "explode"
source: "qrc:/images/_explo.png"
frameCount: 22
frameDuration: 40
to: {"nullFrame":1} // 去到一個空白圖像
},Sprite {//Not sure if this is needed, but seemed easiest // 作者稱不確定這個空白圖像是否需要,但是帶上它似乎更好
name: "nullFrame"
source: "qrc:/images/nullRock.png"
frameCount: 1
frameDuration: 1000
}
]
}
//! [0]
SpriteGoal { // 這就是Affector中的SpriteGoal了
groups: ["meteor"] // 與groupGoal不同,GroupGoal影響的ParticleGroup,而SpriteGoal影響的是這里使用Sprites的粒子
system: sys
goalState: "explode" // 目標狀態
jump: true // 立刻跳轉
anchors.fill: rocketShip // 作用范圍跟隨飛船
width: 60
height: 60
}
//! [0]
Image { // 企業號飛船,因為要使飛船繞一個固定的中心點旋轉,坐標與旋轉的計算全部放在Image中比較麻煩,我們可以使用兩個Item來進行邏輯上的圓周計算
id: rocketShip
source: "qrc:/images/rocket.png"
anchors.centerIn: holder
rotation: (circle.percent+0.25) * 360 // 隨著所在圓周位置的不同對自身進行旋轉。由于原圖飛船是向上的,因此將其初始旋轉90度
z: 2
}
Item { // 通過下面的圓心和連續變化的百分比,這個Item用來得到實際的坐標
id: holder
x: circle.x - Math.sin(circle.percent * 6.28316530714)*200 // 百分比乘以2π,200為半徑
y: circle.y + Math.cos(circle.percent * 6.28316530714)*200
z: 1
}
Item {
id: circle
x: root.width / 1.2 // 圓心的位置
y: root.height / 1.7
property real percent: 0 // 定義一個百分比屬性
SequentialAnimation on percent { // 4秒的1到0循環
id: circleAnim1
loops: Animation.Infinite
running: true
NumberAnimation {
duration: 4000
from: 1
to: 0
}
}
}
ImageParticle { // 飛船的尾氣粒子
z:0 // 其z值比飛船小,這樣這些粒子不會覆蓋在飛船上面
system: sys
groups: ["exhaust"]
source: "qrc:///particleresources/fuzzydot.png"
color: "orange"
SequentialAnimation on color {
loops: Animation.Infinite
ColorAnimation {
from: "red"
to: "cyan"
duration: 1000
}
ColorAnimation {
from: "cyan"
to: "red"
duration: 1000
}
}
colorVariation: 0.2
}
Emitter { // 噴氣粒子發射器
id: trailsNormal2
system: sys
group: "exhaust"
emitRate: 300
lifeSpan: 500
y: holder.y
x: holder.x
velocity: PointDirection { xVariation: 40; yVariation: 40; }
velocityFromMovement: 16
acceleration: PointDirection { xVariation: 10; yVariation: 10; }
size: 4
sizeVariation: 4
}
}
~~~
(9)Turbulence
在上篇博文的最后一個小例子——飛翔的火焰 中我們其實已經接觸到了Turbulence,它用來為粒子提供一個氣流的效果。在這個例子中我們可以更清晰地看到它的用法。
可以看到Turbulence為火苗和煙霧帶來的效果:

Turbulence.qml:
~~~
import QtQuick 2.0
import QtQuick.Particles 2.0
Rectangle {
width: 320
height: 480
color: "#222222"
id: root
Image {
source: "qrc:/images/candle.png" // 一根空白的蠟燭
anchors.bottom: parent.bottom
anchors.horizontalCenter: parent.horizontalCenter
anchors.bottomMargin: -60 // 這張圖下面有一段空白
anchors.horizontalCenterOffset: 2 // 水平中心向右平移2個像素
}
ParticleSystem {
anchors.fill: parent
MouseArea { // 點擊后關閉/打開Turbulence效果
anchors.fill: parent
onClicked: turb.enabled = !turb.enabled
}
//! [0]
Turbulence {
id: turb
enabled: true
height: (parent.height / 2) - 4
width: parent.width
x: parent. width / 4
anchors.fill: parent
strength: 32 // 可以為strength添加一個NumberAnimation,然后通過設置Easing,可以達到更逼近現實的氣流效果
NumberAnimation on strength{from: 16; to: 64; easing.type: Easing.InOutBounce; duration: 1800; loops: -1}
}
//! [0]
ImageParticle { // 煙霧
groups: ["smoke"]
source: "qrc:///particleresources/glowdot.png"
color: "#11111111"
colorVariation: 0
}
ImageParticle { // 火苗
groups: ["flame"]
source: "qrc:///particleresources/glowdot.png"
color: "#11ff400f"
colorVariation: 0.1
}
Emitter { // 火苗粒子由窗口中心發出
anchors.centerIn: parent
group: "flame"
emitRate: 120
lifeSpan: 1200
size: 20
endSize: 10
sizeVariation: 10
acceleration: PointDirection { y: -40 }
velocity: AngleDirection { angle: 270; magnitude: 20; angleVariation: 22; magnitudeVariation: 5 }
}
TrailEmitter {
id: smoke1
width: root.width
height: root.height/2
group: "smoke"
follow: "flame"
emitRatePerParticle: 1
lifeSpan: 2400
lifeSpanVariation: 400
size: 16
endSize: 8
sizeVariation: 8
acceleration: PointDirection { y: -40 }
velocity: AngleDirection { angle: 270; magnitude: 40; angleVariation: 22; magnitudeVariation: 5 }
}
TrailEmitter { // 第二個TrailEmitter用來在更高一點的地方釋放出更濃郁的煙霧
id: smoke2
width: root.width
height: root.height/2 - 20
group: "smoke"
follow: "flame"
emitRatePerParticle: 4
lifeSpan: 2400
size: 36
endSize: 24
sizeVariation: 12
acceleration: PointDirection { y: -40 }
velocity: AngleDirection { angle: 270; magnitude: 40; angleVariation: 22; magnitudeVariation: 5 }
}
}
}
~~~
(10)Wander
同樣我們在本文第三個小例子中已經接觸過wander了,在那我們使用wander為落葉添加了搖擺的飄落效果。在這個例子中我們將了解到,除了速度,wander還可以進一步作用于位置和加速度。

可以看到在飄落的雪花背景中,有三個按鈕分別用來選擇位置,速度,以及加速度。通過點擊這些按鈕,可以改變這些雪花在x方向上的不同運動效果。這些按鈕是在另一個Qml文件中定義的,代碼比較簡單,貼在下面,就不一句句介紹了。
GreyButton.qml:
~~~
import QtQuick 2.0
Item {
id: container
property string text: "Button"
property string subText: ""
signal clicked
width: buttonLabel.width + 20; height: col.height + 12
MouseArea {
id: mouseArea;
anchors.fill: parent;
onClicked: container.clicked();
onPressed: background.color = Qt.darker("lightgrey");
onReleased: background.color="lightgrey";
}
Rectangle {
id: background
anchors.fill: parent
color: "lightgrey"
radius: 4
border.width: 1
border.color: Qt.darker(color)
}
Column {
spacing: 2
id: col
x: 10
y: 6
Text {
id: buttonLabel; text: container.text; color: "black"; font.pixelSize: 24
}
Text {
id: buttonLabel2; text: container.subText; color: "black"; font.pixelSize: 12
}
}
}
~~~
wander.qml:
~~~
import QtQuick 2.0
import QtQuick.Particles 2.0
Rectangle {
width: 360
height: 540
ParticleSystem { id: particles }
ImageParticle { // 雪花粒子
system: particles
sprites: Sprite {
name: "snow"
source: "../../images/snowflake.png"
frameCount: 51
frameDuration: 40
frameDurationVariation: 8
}
}
//! [0]
Wander { // wander
id: wanderer
system: particles
anchors.fill: parent
xVariance: 360/(wanderer.affectedParameter+1); // xVariance與pace必須都定義,由于沒有定義yVariance因此不會影響y方向的運動
pace: 100*(wanderer.affectedParameter+1); // 這里wanderer.affectedParameter實際等于0,不太懂這里的意思
}
//! [0]
Emitter {
system: particles
emitRate: 20
lifeSpan: 7000
velocity: PointDirection { y:80; yVariation: 40; }
acceleration: PointDirection { y: 4 }
size: 20
sizeVariation: 10
width: parent.width
height: 100
}
Row { // 這里使用了一個布局器
anchors.bottom: parent.bottom
anchors.horizontalCenter: parent.horizontalCenter
spacing: 4
GreyButton {
text:"dx/dt"
onClicked: wanderer.affectedParameter = Wander.Position; // 點擊改變Wander的影響屬性
}
GreyButton {
text:"dv/dt"
onClicked: wanderer.affectedParameter = Wander.Velocity;
}
GreyButton {
text:"da/dt"
onClicked: wanderer.affectedParameter = Wander.Acceleration;
}
}
}
~~~
- 前言
- 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