粒子组(Particle Group)

在本章开始时,我们已经介绍过粒子组了,默认下,粒子都属于空组("")。使用GroupGoal控制器可以改变粒子组。为了实现可视化,我们创建了一个烟花示例,火箭进入,在空中爆炸形成壮观的烟火。

这个例子分为两部分。第一部分叫做“发射时间(Launch Time)”连接场景,加入粒子组,第二部分叫做“爆炸烟花(Let there be firework)”,专注于粒子组的变化。

让我们看看这两部分。

发射时间(Launch Time)

首先我们创建一个典型的黑色场景:

import QtQuick 2.0
import QtQuick.Particles 2.0

Rectangle {
    id: root
    width: 480; height: 240
    color: "#1F1F1F"
    property bool tracer: false
}

tracer使用被用作场景追踪的开关,然后定义我们的粒子系统:

ParticleSystem {
    id: particleSystem
}

我们添加两种粒子图片画笔(一个用于火箭,一个用于火箭喷射烟雾):

ImageParticle {
    id: smokePainter
    system: particleSystem
    groups: ['smoke']
    source: "assets/particle.png"
    alpha: 0.3
    entryEffect: ImageParticle.None
}

ImageParticle {
    id: rocketPainter
    system: particleSystem
    groups: ['rocket']
    source: "assets/rocket.png"
    entryEffect: ImageParticle.None
}

你可以看到在这些画笔定义中,它们使用groups属性来定义粒子的归属。只需要定义一个名字,Qt Quick将会隐式的创建这个分组。

现在我们需要将这些火箭发射到空中。我们在场景底部创建一个粒子发射器,将速度设置为朝上的方向。为了模拟重力,我们设置一个向下的加速度:

Emitter {
    id: rocketEmitter
    anchors.bottom: parent.bottom
    width: parent.width; height: 40
    system: particleSystem
    group: 'rocket'
    emitRate: 2
    maximumEmitted: 4
    lifeSpan: 4800
    lifeSpanVariation: 400
    size: 32
    velocity: AngleDirection { angle: 270; magnitude: 150; magnitudeVariation: 10 }
    acceleration: AngleDirection { angle: 90; magnitude: 50 }
    Tracer { color: 'red'; visible: root.tracer }
}

发射器属于'rocket'粒子组,与我们的火箭粒子画笔相同。通过粒子组将它们联系在一起。发射器将粒子发射到'rocket'粒子组中,火箭画笔将会绘制它们。

对于烟雾,我们使用一个追踪发射器,它将会跟在火箭的后面。我们定义'smoke'组,并且它会跟在'rocket'粒子组后面:

TrailEmitter {
    id: smokeEmitter
    system: particleSystem
    emitHeight: 1
    emitWidth: 4
    group: 'smoke'
    follow: 'rocket'
    emitRatePerParticle: 96
    velocity: AngleDirection { angle: 90; magnitude: 100; angleVariation: 5 }
    lifeSpan: 200
    size: 16
    sizeVariation: 4
    endSize: 0
}

向下模拟从火箭里面喷射出的烟。emitHeight与emitWidth指定了围绕跟随在烟雾粒子发射后的粒子。如果不指定这个值,跟随的粒子将会被拿掉,但是对于这个例子,我们想要提升显示效果,粒子流从一个接近于火箭尾部的中间点发射出。

如果你运行这个例子,你会发现一些火箭正常飞起,一些火箭却飞出场景。这不是我们想要的,我们需要在它们离开场景前让他们慢下来,这里可以使用摩擦控制器来设置一个最小阈值:

Friction {
    groups: ['rocket']
    anchors.top: parent.top
    width: parent.width; height: 80
    system: particleSystem
    threshold: 5
    factor: 0.9
}

在摩擦控制器中,你也需要定义哪个粒子组受控制器影响。当火箭经过从顶部向下80像素的区域时,所有的火箭将会以0.9的factor减慢(你可以试试100,你会发现它们立即停止了),直到它们的速度达到每秒5个像素。随着火箭粒子向下的加速度继续生效,火箭开始向地面下沉,直到它们的生命周期结束。

由于在空气中向上运动是非常困难的,并且非常不稳定,我们在火箭上升时模拟一些紊流:

Turbulence {
    groups: ['rocket']
    anchors.bottom: parent.bottom
    width: parent.width; height: 160
    system: particleSystem
    strength: 25
    Tracer { color: 'green'; visible: root.tracer }
}

当然,紊流控制器也需要定义它会影响哪些粒子组。紊流控制器的区域从底部向上160像素(直到摩擦控制器边界上),它们也可以相互覆盖。

当你运行程序时,你可以看到火箭开始上升,然后在摩擦控制器区域开始减速,向下的加速度仍然生效,火箭开始后退。下一步我们开始制作爆炸烟花。

注意

使用tracers跟踪区域可以显示场景中的不同区域。火箭粒子发射的红色区域,蓝色区域是紊流控制器区域,最后在绿色的摩擦控制器区域减速,并且再次下降是由于向下的加速度仍然生效。

爆炸烟花(Let there be fireworks)

让火箭变成美丽的烟花,我们需要添加一个粒子组来封装这个变化:

ParticleGroup {
    name: 'explosion'
    system: particleSystem
}

我们使用GroupGoal控制器来改变粒子组。这个组控制器被放置在屏幕中间垂直线附近,它将会影响'rocket'粒子组。使用groupGoal属性,我们设置目标组改变为我们之前定义的'explosion'组:

GroupGoal {
    id: rocketChanger
    anchors.top: parent.top
    width: parent.width; height: 80
    system: particleSystem
    groups: ['rocket']
    goalState: 'explosion'
    jump: true
    Tracer { color: 'blue'; visible: root.tracer }
}

jump属性定义了粒子组的变化是立即变化而不是在某个时间段后变化。

注意

在Qt5的alpha发布版中,粒子组的持续改变无法工作,有好的建议吗?

由于火箭粒子变为我们的爆炸粒子,当火箭粒子进入GroupGoal控制器区域时,我们需要在粒子组中添加一个烟花:

// inside particle group
TrailEmitter {
    id: explosionEmitter
    anchors.fill: parent
    group: 'sparkle'
    follow: 'rocket'
    lifeSpan: 750
    emitRatePerParticle: 200
    size: 32
    velocity: AngleDirection { angle: -90; angleVariation: 180; magnitude: 50 }
}

爆炸释放粒子到'sparkle'粒子组。我们稍后会定义这个组的粒子画笔。轨迹发射器跟随火箭粒子每秒发射200个火箭爆炸粒子。粒子的方向向上,并改变180度。

由于向'sparkle'粒子组发射粒子,我们需要定义一个粒子画笔用于绘制这个组的粒子:

ImageParticle {
    id: sparklePainter
    system: particleSystem
    groups: ['sparkle']
    color: 'red'
    colorVariation: 0.6
    source: "assets/star.png"
    alpha: 0.3
}

闪烁的烟花是红色的星星,使用接近透明的颜色来渲染出发光的效果。

为了使烟花更加壮观,我们也需要添加给我们的粒子组添加第二个轨迹发射器,它向下发射锥形粒子:

// inside particle group
TrailEmitter {
    id: explosion2Emitter
    anchors.fill: parent
    group: 'sparkle'
    follow: 'rocket'
    lifeSpan: 250
    emitRatePerParticle: 100
    size: 32
    velocity: AngleDirection { angle: 90; angleVariation: 15; magnitude: 400 }
}

其它的爆炸轨迹发射器与这个设置类似,就这样。

下面是最终结果。

下面是火箭烟花的所有代码。

import QtQuick 2.0
import QtQuick.Particles 2.0

Rectangle {
    id: root
    width: 480; height: 240
    color: "#1F1F1F"
    property bool tracer: false

    ParticleSystem {
        id: particleSystem
    }

    ImageParticle {
        id: smokePainter
        system: particleSystem
        groups: ['smoke']
        source: "assets/particle.png"
        alpha: 0.3
    }

    ImageParticle {
        id: rocketPainter
        system: particleSystem
        groups: ['rocket']
        source: "assets/rocket.png"
        entryEffect: ImageParticle.Fade
    }

    Emitter {
        id: rocketEmitter
        anchors.bottom: parent.bottom
        width: parent.width; height: 40
        system: particleSystem
        group: 'rocket'
        emitRate: 2
        maximumEmitted: 8
        lifeSpan: 4800
        lifeSpanVariation: 400
        size: 32
        velocity: AngleDirection { angle: 270; magnitude: 150; magnitudeVariation: 10 }
        acceleration: AngleDirection { angle: 90; magnitude: 50 }
        Tracer { color: 'red'; visible: root.tracer }
    }

    TrailEmitter {
        id: smokeEmitter
        system: particleSystem
        group: 'smoke'
        follow: 'rocket'
        size: 16
        sizeVariation: 8
        emitRatePerParticle: 16
        velocity: AngleDirection { angle: 90; magnitude: 100; angleVariation: 15 }
        lifeSpan: 200
        Tracer { color: 'blue'; visible: root.tracer }
    }

    Friction {
        groups: ['rocket']
        anchors.top: parent.top
        width: parent.width; height: 80
        system: particleSystem
        threshold: 5
        factor: 0.9

    }

    Turbulence {
        groups: ['rocket']
        anchors.bottom: parent.bottom
        width: parent.width; height: 160
        system: particleSystem
        strength:25
        Tracer { color: 'green'; visible: root.tracer }
    }


    ImageParticle {
        id: sparklePainter
        system: particleSystem
        groups: ['sparkle']
        color: 'red'
        colorVariation: 0.6
        source: "assets/star.png"
        alpha: 0.3
    }

    GroupGoal {
        id: rocketChanger
        anchors.top: parent.top
        width: parent.width; height: 80
        system: particleSystem
        groups: ['rocket']
        goalState: 'explosion'
        jump: true
        Tracer { color: 'blue'; visible: root.tracer }
    }

    ParticleGroup {
        name: 'explosion'
        system: particleSystem

        TrailEmitter {
            id: explosionEmitter
            anchors.fill: parent
            group: 'sparkle'
            follow: 'rocket'
            lifeSpan: 750
            emitRatePerParticle: 200
            size: 32
            velocity: AngleDirection { angle: -90; angleVariation: 180; magnitude: 50 }
        }

        TrailEmitter {
            id: explosion2Emitter
            anchors.fill: parent
            group: 'sparkle'
            follow: 'rocket'
            lifeSpan: 250
            emitRatePerParticle: 100
            size: 32
            velocity: AngleDirection { angle: 90; angleVariation: 15; magnitude: 400 }
        }
    }
}