Qt Quick Particles 示例 - 影响器

本部分汇集了在 QML 粒子系统中使用 Affectors 的示例。

几个红球从一团蓝色的火焰旁飘过,其中一个球正在燃烧

这是一组关于在粒子系统中使用影响器的简短 QML 示例。每个示例都是一个小型 QML 文件,重点展示特定类型或功能。

“Age”示例演示了如何使用“Age”影响器来提前结束粒子的生命周期。

Age {
    anchors.fill: parent
    system: particles
    once: true
    lifeLeft: 1200
    advancePosition: false
}

当您在屏幕上移动该影响器时,其内部尚未受到影响的粒子会跳转到生命周期临近结束的阶段。这会给它们留出短暂的时间来完成淡出过程,但如果将 `lifeLeft` 设置为 0(默认值),则会导致它们立即达到生命周期的终点。

“Attractor”演示了如何使用“Attractor”影响器模拟黑洞

Attractor {
    id: gs; pointX: root.width/2; pointY: root.height/2; strength: 4000000;
    affectedParameter: Attractor.Acceleration
    proportionalToDistance: Attractor.InverseQuadratic
}

场景中的所有粒子,包括火箭的尾气和弹丸,都会被吸引向黑洞。这种吸引力在靠近黑洞处更强,因此屏幕顶部附近的小行星几乎不受影响,而位于屏幕中部附近的小行星有时会发生剧烈的轨迹偏转。 为了完善该效果,一个“Age”影响器覆盖在黑洞上,用于销毁与黑洞接触的粒子。

自定义影响器(Custom Affector)可直接通过 JavaScript 操控粒子的属性。其中一个影响器用于让落叶在下落时前后摇摆,这样看起来比单纯绕圈旋转更像真实的树叶:

Affector {
    width: parent.width
    height: parent.height - 100
    onAffectParticles: (particles, dt) => {
        //Wobbly movement
        for (var i=0; i<particles.length; i++) {
            var particle = particles[i];
            particle.rotation += particle.vx * 0.15 * dt;
            particle.update = true;
        }
    }
}

另一个则用于在树叶“落地”时施加略有变化的摩擦力,使其看起来更自然:

Affector {//Custom Friction, adds some 'randomness'
    x: -60
    width: parent.width + 120
    height: 100
    anchors.bottom: parent.bottom
    onAffectParticles: (particles, dt) => {
        for (var i=0; i<particles.length; i++) {
            var particle = particles[i];
            var pseudoRand = (Math.floor(particle.t*1327) % 10) + 1;
            var yslow = dt * pseudoRand * 0.5 + 1;
            var xslow = dt * pseudoRand * 0.05 + 1;
            if (particle.vy < 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;
        }
    }
}

“摩擦”效果与自定义影响器中的落叶效果类似,不同之处在于它使用的是从上到下全程恒定的摩擦力,而非自定义影响器。

Friction {
    anchors.fill: parent
    anchors.margins: -40
    factor: 0.4
}

重力(Gravity)是一个便捷的效应器,用于对其内部的粒子施加恒定加速度

Gravity {
    system: sys
    magnitude: 32
    angle: ground.rotation + 90
}

GroupGoal 设置了两个粒子组,分别用于燃烧球和非燃烧球,并提供了多种在它们之间切换的方式。

ParticleGroup {
    name: "unlit"
    duration: 1000
    to: {"lighting":1, "unlit":99}
    ImageParticle {
        source: "images/particleA.png"
        colorVariation: 0.1
        color: "#2060160f"
    }
    GroupGoal {
        whenCollidingWith: ["lit"]
        goalState: "lighting"
        jump: true
    }
}

非火焰球每秒有百分之一的机会自行点燃,但整个组还设置了“GroupGoal ”。当未点燃组中的粒子与点燃组中的粒子发生碰撞时,该影响器会作用于未点燃组的所有粒子,并促使它们移动到点燃组。

ParticleGroup {
    name: "lighting"
    duration: 100
    to: {"lit":1}
}

“点亮”组作为过渡组,用于让光晕逐渐增强,使过渡效果更自然。因此,粒子在100毫秒后会自动移入“点亮”组。

ParticleGroup {
    name: "lit"
    duration: 10000
    onEntered: root.score++
    TrailEmitter {
        id: fireballFlame
        group: "flame"

        emitRatePerParticle: 48
        lifeSpan: 200
        emitWidth: 8
        emitHeight: 8

        size: 24
        sizeVariation: 8
        endSize: 4
    }

    TrailEmitter {
        id: fireballSmoke
        group: "smoke"

“lit”组还附加了“TrailEmitters”以增强火焰和烟雾效果,但不会过渡到任何其他组。另有两个“GroupGoal ”对象,允许“unlit”组中的粒子过渡到“lighting”组(随后再过渡到“lit”组)。

GroupGoal {
    groups: ["unlit"]
    goalState: "lit"
    jump: true
    system: particles
    x: -15
    y: -55
    height: 75
    width: 30
    shape: MaskShape {source: "images/matchmask.png"}
}

第一个只是一个与引导火焰图像位置绑定的区域。当未点亮的光球穿过火焰时,由于引导火焰温度极高,它们会直接进入“点亮”组。

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

第二个区域与最近一次指针交互的位置绑定,因此触摸或点击未点燃的球体(由于它们持续移动,这很难做到)会使它们移动到“照明”组。

“移动”展示了通过中途改变轨迹所能实现的一些简单效果。红色粒子有一个影响其位置的“影响器”,会将它们向前跳跃 120px。

Affector {
    groups: ["A"]
    x: 120
    width: 80
    height: 80
    once: true
    position: PointDirection { x: 120; }
}

绿色粒子则有一个影响其速度的“影响器”,但会产生一定的角度变化。通过在其现有的向前速度上添加一些随机方向的速度分量,它们开始呈锥形向外散射。

Affector {
    groups: ["B"]
    x: 120
    y: 240
    width: 80
    height: 80
    once: true
    velocity: AngleDirection { angleVariation:360; magnitude: 72 }
}

蓝色粒子有一个影响其加速度的效应器,由于其设置为相对于 false,因此会重置加速度而非累加。一旦蓝色粒子到达该效应器,其水平速度将停止增加,而垂直速度则会减小。

Affector {
    groups: ["C"]
    x: 120
    y: 400
    width: 80
    height: 120
    once: true
    relative: false
    acceleration: PointDirection { y: -80; }
}

SpriteGoal 该影响器与粒子的精灵引擎交互,前提是这些粒子是由ImageParticle 作为精灵绘制的。

SpriteGoal {
    groups: ["meteor"]
    system: sys
    goalState: "explode"
    jump: true
    anchors.fill: rocketShip
    width: 60
    height: 60
}

SpriteGoal 会追踪屏幕上火箭飞船的图像,当它与由ImageParticle 作为精灵绘制的粒子交互时,会指示它们立即进入“爆炸”状态——在此场景中,即小行星碎裂成无数碎片的动画。

“湍流”效果包含带有烟雾的火焰,这两组粒子均受“湍流”影响器作用,从而产生微弱的风效。

Turbulence {
    id: turb
    enabled: true
    height: (parent.height / 2) - 4
    width: parent.width
    x: parent. width / 4
    anchors.fill: parent
    NumberAnimation on strength{from: 16; to: 64; easing.type: Easing.InOutBounce; duration: 1800; loops: -1}
}

若要让风向发生变化,请在 noiseSource 参数中替换为黑白噪点图像(当前使用的是默认噪点源)。

“Wander”使用 Wander 影响器,为下落中的雪花添加一些水平漂移效果。

Wander {
    id: wanderer
    system: particles
    anchors.fill: parent
    xVariance: 360/(wanderer.affectedParameter+1);
    pace: 100*(wanderer.affectedParameter+1);
}

将“Wander”应用于轨迹的不同属性会产生不同的运动效果,因此该示例便于您进行尝试并观察其中的差异。

示例项目 @ code.qt.io

© 2026 The Qt Company Ltd. Documentation contributions included herein are the copyrights of their respective owners. The documentation provided herein is licensed under the terms of the GNU Free Documentation License version 1.3 as published by the Free Software Foundation. Qt and respective logos are trademarks of The Qt Company Ltd. in Finland and/or other countries worldwide. All other trademarks are property of their respective owners.