您的位置:首页 > 产品设计 > UI/UE

Qt5官方demo解析集14——Qt Quick Particles Examples - System

2014-07-01 15:22 609 查看
本系列所有文章可以在这里查看http://blog.csdn.net/cloud_castle/article/category/2123873

接上文Qt5官方demo解析集13——Qt
Quick Particles Examples - Image Particles

一转眼就到了我们粒子系列的最后一个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中的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"
}
}
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: