Qt5官方demo解析集11——Qt Quick Particles Examples - Affectors

2023-05-16

本系列所有文章可以在这里查看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取余。叶子生命周期越长,这个数会越大,也就更容易被“减速”
                    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影响粒子运动(位置、速度、加速度)的方法。


代码很简单,我们大致看一下好了,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还可以进一步作用于位置和加速度。


可以看到在飘落的雪花背景中,有三个按钮分别用来选择位置,速度,以及加速度。通过点击这些按钮,可以改变这些雪花在x方向上的不同运动效果。这些按钮是在另一个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;
        }
    }
}





本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)

Qt5官方demo解析集11——Qt Quick Particles Examples - Affectors 的相关文章

随机推荐

  • COCO Dataset person_keypoints.json 解析

    DataSet COCO json person keypointsperson keypoins json 结构one imageperson keypoint jsonkeypointDisplay above image ID 61
  • Spring Validation 入参校验(自定义)

    背景 xff1a 1 Java API规范 JSR303 定义了Bean校验的标准validation api xff0c 但没有提供实现 hibernate validation是对这个规范的实现 xff0c 并增加了校验注解如 64 E
  • 第2章 梅西法 阅读

    梅西法可以用于任何对象集合的排名 xff0c 但是一定要预先定义好成对比较数据 比如乒乓球赛 xff0c 成对比较数据就是两个人PK的结果 xff1b 网页排序 xff0c 成对比较数据可以是两个网页的流量 梅西法的主要思路是构造一个最小二
  • 我与算法的缘分

    六年前 xff0c 我完全不知道算法是什么东西 六年后 xff0c 我看到算法就两眼放光 六年的时间让我从算法小菜鸟蜕变成算法爱好者 大一上学期 xff0c 我对算法一点概念都没有 xff0c 当时老师让我们用伪代码写算法 xff0c 我基
  • EM算法 实例讲解

    第一次接触EM算法 xff0c 是在完成半隐马尔科夫算法大作业时 我先在网上下载了两份Baum Welch算法的代码 xff0c 通过复制粘贴 xff0c 修修补补 xff0c 用java实现了HMM算法 xff08 应用是韦小宝掷两种骰子
  • 机器学习漫谈

    机器学习漫谈 数据挖掘 机器学习项目一般包括四个关键部分 xff0c 分别是 xff0c 数据分析 xff0c 特征工程 xff0c 建立模型 xff0c 验证 1 数据分析 从广义上讲 xff0c 数据分析包括数据收集 xff0c 数据处
  • 2015年机器学习/数据挖掘面试总结

    2015年机器学习 数据挖掘面试总结 明年硕士毕业 xff0c 今年开始找工作 在北方呆的太久 xff0c 想回湿润的南方 第一站 xff08 3月份 xff09 xff0c 阿里数据挖掘实习生面试 个人觉得 xff0c 阿里的面试是最人性
  • 研究生和本科有什么不同?

    本科学了4年计算机 xff0c 研究生又学了2年计算机 xff0c 感觉两个阶段的生活学习还是挺不一样的 一 在同学间的交流方面 xff0c 大学生比研究生交流更频繁 xff0c 交友更广泛 初中高中时 xff0c 大家座位固定 xff0c
  • 网易游戏2016校园招聘数据挖掘研究员在线笔试题和答案

    刚做完网易在线笔试题 xff0c 感触最深的地方是 xff0c 虽然题目形式和ACM题目相似 xff0c 但是内容更偏向于实际应用 总共有四个题目 xff0c 第一个题目属于字符串匹配类型 xff0c 难度较低 xff0c 第二个题目是模拟
  • C++程序设计陷阱读书笔记(一)

    差不多有三年没系统地温习C 43 43 程序设计了 xff0c 但面试官问起这方面的知识时 xff0c 我有种恍然隔世的感觉 年龄越大越难静下心来读完一本专业书 xff0c 越学心越浮躁 将所看所思记录下来能稍微提高一下学习效率 下面是我读
  • ubuntu开启SSH服务

    SSH分客户端openssh client和openssh server 如果你只是想登陆别的机器的SSH只需要安装openssh client xff08 ubuntu有默认安装 xff0c 如果没有则sudo apt get insta
  • python 使用paramiko模块访问远程文件

    代码 xff1a span class hljs keyword import span paramiko client 61 paramiko SSHClient span class hljs keyword try span clie
  • FeignClient服务间调用 Openfeign ErrorDecoder caused java.io.IOException: stream is closed

    通过feign去服务间调用时 xff0c 如果包含流数据 xff0c 不要试图打印 response 里边的数据 xff0c 会导致IO异常 Don 39 t use any of the above feature before get
  • IDEA配置+Spring框架

    1 IDEA环境配置 ctrl 43 F12查看方法结构ctrl 43 H 查看类图ctrl 43 D向下复制一行Alt 43 Insert 提供构造 get set to StringCtel 43 Alt 43 T 选中代码快速try
  • Qt5官方Demo解析集1——Fortune Server/Client

    本系列所有文章可以在这里查看http blog csdn net cloud castle article category 2123873 最近发觉学习Qt官方demo的好处越来越多 xff0c 除了了解特定模块与类的用法 xff0c 更
  • QML官方系列教程——QML Applications

    附网址 xff1a http qt project org doc qt 5 qmlapplications html 如果你对Qt的官方demo感兴趣 xff0c 可以参考本博客的另一个系列 Qt5官方demo解析集 每个绿色字体均是一篇
  • QML官方系列教程——Qt QML

    附网址 xff1a http qt project org doc qt 5 qtqml index html Qt QML Qt QML模块为基于QML language 开发应用程序和库提供了语言和基础建设 它定义并实现了QML的语法和
  • QML官方系列教程——First Steps with QML

    附网址 xff1a http qt project org doc qt 5 qmlfirststeps html Creating a QML Document 创建一个QML文件 一个QML文件定义了对象的层次结构 xff1a 具有高度
  • QML官方系列教程——Using Qt Quick Designer

    附网址 xff1a http qt project org doc qtcreator 3 1 creator using qt quick designer html Using Qt Quick Designer 使用Qt Quick设
  • Qt5官方demo解析集11——Qt Quick Particles Examples - Affectors

    本系列所有文章可以在这里查看 http blog csdn net cloud castle article category 2123873 接上文 Qt5官方demo解析集10 Qt Quick Particles Examples E