美文网首页Qt QML 杂记
QML Book 第八章 粒子模拟 2

QML Book 第八章 粒子模拟 2

作者: 赵者也 | 来源:发表于2017-07-11 07:47 被阅读28次

    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 秒。粒子自动进出。

    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 毫秒时,我们会看到颗粒再次出现在位置上。

    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。

    attractor

    Friction(摩擦)

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

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

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

    friction

    Gravity(重力)

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

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

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

    gravity

    Turbulence(湍流)

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

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

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

    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 次。

    wander

    8.7 粒子群(Particle Groups)

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

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

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

    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 }
    }
    

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

    下面就是最后的结果。

    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

        本文链接:https://www.haomeiwen.com/subject/cdebcxtx.html