在这个系列中的所有文章都可以在这里查看http://blog.csdn.net/cloud_castle/article/category/2123873
接上文Qt5官方demo解析集10——Qt
Quick Particles Examples - Emitters
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
来看看<“杀掉”进入Affector的粒子>是个什么效果:进入图中矩形框的雪花都变小并逐渐消失了。
来看看这个小样例是怎么写的吧~ 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取余。 叶子生命周期越长,这个数会越大。也就更easy被“减速”
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影响粒子运动(位置、速度、加速度)的方法。
代码非常easy,我们大致看一下好了,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还能够进一步作用于位置和加速度。
能够看到在飘落的雪花背景中,有三个button分别用来选择位置,速度,以及加速度。通过点击这些button,能够改变这些雪花在x方向上的不同运动效果。这些button是在还有一个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;
}
}
}
版权声明:本文博主原创文章,博客,未经同意不得转载。