QML Book 第八章 粒子模拟 2

8.5 粒子绘制器(Particle Painters)

到目前为止,我们只使用基于图像的粒子绘制器来显示粒子。Qt 还有其他的粒子绘制器:

  • ItemParticle —— 基于粒子绘制器的代理
  • CustomParticle —— 基于粒子绘制器的着色器

ItemParticle 可用于将 QML 元素作为粒子发出。为此,我们需要指定自己的代理粒子。

    ItemParticle {
        id: particle
        system: particleSystem
        delegate: itemDelegate
    }

在这种情况下,我们的代理是一个随机图像(使用Math.random()),可视化为白色边框和随机大小。

    Component {
        id: itemDelegate
        Item {
            id: container
            width: 32*Math.ceil(Math.random()*3); height: width
            Image {
                anchors.fill: parent
                anchors.margins: 4
                source: 'assets/'+images[Math.floor(Math.random()*9)]
            }
        }
    }

我们每秒发射 4 张图像,每张 4 秒。粒子自动进出。

QML Book 第八章 粒子模拟 2_第1张图片
itemparticle

对于更多的动态情况,也可以自己创建一个元素,让粒子通过 take(item, priority) 来控制它。通过这个粒子模拟可以控制我们的粒子,像普通粒子那样处理物体。我们可以通过使用 give(item) 来获取对元素的控制。我们可以通过使用 freeze(item) 停止其生命进程来影响元素粒子,并使用 unfreeze(item) 恢复元素粒子的生命进程。

8.6 影响粒子(Affecting Particles)

发射器发射了粒子。发射粒子后,发射器不能再发生变化。影响器可以让我们在发射后对其造成影响。

每种类型的影响器以不同的方式影响粒子:

  • Age(生命周期) —— 改变粒子在其生命周期中的位置
  • Attractor(吸引器) —— 将粒子吸引到特定点
  • Friction(摩擦) —— 减慢与粒子当前速度成比例的运动
  • Gravity(重力) —— 设置一个角度的加速度
  • Turbulence(湍流) —— 强制基于噪声图像的方式流动
  • Wander(游移) —— 随机变化轨迹
  • GroupGoal(目标集) —— 改变一组粒子的状态
  • SpriteGoal(精灵目标) —— 改变精灵粒子的状态

Age(生命周期)

加速粒子的消失。lifeLeft 属性指定粒子剩余的显示时间。

    Age {
        anchors.horizontalCenter: parent.horizontalCenter
        width: 240; height: 120
        system: particleSystem
        advancePosition: true
        lifeLeft: 1200
        once: true
        Tracer {}
    }

在这个例子中,我们缩短上层颗粒的寿命,当他们到达时间为 1200 毫秒时。由于我们将 advancePosition 设置为 true,所以当粒子剩下 1200 毫秒时,我们会看到颗粒再次出现在位置上。

QML Book 第八章 粒子模拟 2_第2张图片
age

Attractor(引力)

吸引器将粒子吸引到特定点。该点使用 pointX 和 pointY 指定,它与吸引器的大小相关。力量规定了吸引力的值。在我们的例子中,我们让粒子从左到右。吸引器放置在顶部,一半的颗粒通过吸引子行进。吸引器只影响粒子在它们的边界框中。这个区分允许我们同时看到正常流和受影响的流。

    Attractor {
        anchors.horizontalCenter: parent.horizontalCenter
        width: 160; height: 120
        system: particleSystem
        pointX: 0
        pointY: 0
        strength: 1.0
        Tracer {}
    }

很容易看出,上半部分的颗粒受到吸引到顶部的影响。吸引点设置为吸引器的左上(0/0点),力为1.0。

QML Book 第八章 粒子模拟 2_第3张图片
attractor

Friction(摩擦)

摩擦影响器是将粒子减慢的一个因素,直到达到一定的阈值。

    Friction {
        anchors.horizontalCenter: parent.horizontalCenter
        width: 240; height: 120
        system: particleSystem
        factor : 0.8
        threshold: 25
        Tracer {}
    }

在上部摩擦区域,粒子减速了0.8倍,直到粒子达到每秒 25 像素的速度。阈值行为就像一个过滤器。行程超过阈值速度的颗粒会减慢给定的因子。

QML Book 第八章 粒子模拟 2_第4张图片
friction

Gravity(重力)

重力影响器应用加速度在本例中,我们使用角度方向将粒子从底部流向顶部。右侧不受影响,左侧应用重力影响。重力倾斜到 90 度(底部方向),大小为 50。

    Gravity {
        width: 240; height: 240
        system: particleSystem
        magnitude: 50
        angle: 90
        Tracer {}
    }

左侧的颗粒试图爬升,但向底部稳定施加的加速度将它们拖到重力的方向。

QML Book 第八章 粒子模拟 2_第5张图片
gravity

Turbulence(湍流)

湍流影响器将粒子的力矢量的混沌映射应用于该粒子。混沌映射由噪声图像定义,噪声图像可以用 noiseSource 属性定义。强度定义了矢量应用于粒子运动的强度。

    Turbulence {
        anchors.horizontalCenter: parent.horizontalCenter
        width: 240; height: 120
        system: particleSystem
        strength: 100
        Tracer {}
    }

在该示例的上部区域中,颗粒受到湍流的影响。 他们的运动更不稳定。与原始路径不一致的偏差量由强度定义。

QML Book 第八章 粒子模拟 2_第6张图片
turbulence

Wander(游移)

游移操纵轨迹。可以指定属性 affectedParameter,参数(速度,位置或加速度)被游移所覆盖。pace 属性指定每秒属性更改的最大值。 xVariance 和 yVariance 指定了对粒子轨迹的 x 和 y 分量的影响。

    Wander {
        anchors.horizontalCenter: parent.horizontalCenter
        width: 240; height: 120
        system: particleSystem
        affectedParameter: Wander.Position
        pace: 200
        yVariance: 240
        Tracer {}
    }

在顶端的游移影响器中,粒子被随机的轨迹变化围绕着。在这种情况下,位置在 y 方向每秒更改 200 次。

QML Book 第八章 粒子模拟 2_第7张图片
wander

8.7 粒子群(Particle Groups)

在本章开头,我们指出,粒子是分组的,默认情况下是空组('')。 使用 GroupGoal 影响器可以让粒子更改组。为了可视化,我们想创建一个小火焰秀,火箭射向空中,在空中爆炸成一个壮观的烟花。

QML Book 第八章 粒子模拟 2_第8张图片
firework_teaser

该例分为两部分。第一部分称为“发射阶段”,关于设置场景并引入粒子群,第二部分称为“烟花爆破”重点关注群组变化。

现在就我们开始行动吧。

发射阶段

要做到这一点,我们创造一个典型的黑暗场景:

import QtQuick 2.5
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
    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 }
}

发射器在“火箭”组中,与我们的火箭粒子绘制器一样。通过组名,他们绑在一起。 发射器将颗粒发射到“火箭”组中,而火箭粒子绘制器会绘制它们以完成后续工作。

对于排出的气体,我们使用跟踪火箭的跟踪发射器。它声明一个自己的组称为“烟”,并遵循“火箭”组的粒子的轨迹:

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 因子(尝试一下 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 像素向上的湍流,直到其到达摩擦边界。他们也可以重叠。

当我们开始这个例子,你会看到火箭正在爬上,然后会被摩擦减慢,并且依然应用向下的加速度而回落到地面上。接下来的事情就是开始烟火表演了。

QML Book 第八章 粒子模拟 2_第9张图片
firework_rockets

** 注意: **
图像显示了启用示踪器显示不同区域的场景。火箭粒子在红色区域发射,然后受到蓝色区域的湍流的影响。最后,由于稳定的下行加速度,它们被绿色区域的摩擦影响器放慢,并开始下降。

来点烟火秀

为了能够将火箭变成美丽的烟火,我们需要添加一个 ParticleGroup 来封装这些变化:

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 属性表示组的变化应立即执行而不是在一定的持续时间之后。

由于火箭粒子变为我们的爆炸粒子,当火箭粒子进入 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 }
}

否则设置与其他爆炸轨迹发射器相似。这就是整个示例了。

下面就是最后的结果。

QML Book 第八章 粒子模拟 2_第10张图片
firework_final

这是火箭烟花的完整源代码。

import QtQuick 2.5
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: 128
        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 }
        }
    }
}

8.8 总结一下

粒子是一种非常强大而有趣的表达烟雾、烟火、随机的视觉元素等图形和现象的方式。 Qt 5 中扩展的 API 非常强大,我们刚刚演示的只是比较表面和浅显的功能。还有几个元素,比如:我们还没有使用像精灵、大小表或颜色表。此外,粒子看起来非常有趣时,明智地在用户界面中创建一些粒子效果,会使界面具有很大的吸引力。但是,在用户界面中使用过多粒子效果,一定会导致用户觉得这是一个游戏的印象,所以不建议这么做。事实也确实如此,在游戏中粒子才能发挥它们真正实力。

你可能感兴趣的:(QML Book 第八章 粒子模拟 2)