Qt Quick パーティクルの例 - アフェクター

これは、QML パーティクルシステムにおけるアフェクターの使用例を集めたものです。

青い炎の前を赤いボールが浮かんで通り過ぎ、そのうちの1つのボールが燃えている

これは、パーティクルシステムにおけるアフェクターの使用に関連する、簡単なQMLのサンプル集です。各サンプルは、特定のタイプや機能を強調した小さなQMLファイルです。

「Age」は、Ageアフェクターを使用してパーティクルの寿命を早期に終了させる方法を示しています。

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

アフェクターを画面上で移動させると、その範囲内にある(まだ影響を受けていない)パーティクルは、寿命の終了間近の期間にジャンプします。これにより、フェードアウトを完了するための短い時間が与えられますが、lifeLeftを0(デフォルト)に変更すると、パーティクルは即座に寿命の終わりに達してしまいます。

「Attractor」では、アトラクター・アフェクターを使用してブラックホールをシミュレートする方法を示しています。

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

ロケットの排気やペレットを含め、シーン内のすべてのパーティクルがブラックホールに向かって引き寄せられます。この引力はブラックホールに近づくほど強くなるため、画面上部の小惑星にはほとんど影響が及ばない一方で、中央付近の小惑星は時に劇的に軌道が曲がることもあります。 この効果を完成させるため、ブラックホールの周囲に「Age」アフェクターを配置し、ブラックホールに接触した粒子を破壊するようにしています。

「Custom Affector」は、JavaScriptでパーティクルのプロパティを直接操作します。1つのアフェクターは、葉が落下する際に前後に揺れるようにし、単に円を描くよりもより本物の葉のように見えるようにするために使用されています:

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

もう1つのアフェクターは、葉が「着地」する際にわずかに変化する摩擦を加えることで、より自然な動きを実現するために使用されています:

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」は、カスタムアフェクターによる落ち葉のエフェクトと似ていますが、カスタムアフェクターの代わりに、落下経路全体にわたって一定の摩擦を適用するという点が異なります。

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

「重力」は、その範囲内のパーティクルに一定の加速度を適用するための便利なアフェクターです。

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

GroupGoal 炎を帯びたボールと炎を帯びていないボールの2つのパーティクルグループを設定し、それらを切り替えるための様々な方法を提供します。

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

炎のないボールは、毎秒100分の1の確率で自発的に点灯しますが、グループ全体に「GroupGoal 」が設定されています。このエフェクターは、点灯していないグループのすべてのパーティクルが、点灯しているグループのパーティクルと衝突した際に作用し、それらを点灯グループへ移動させます。

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

「lighting」グループは中間グループとして機能し、光の輝きが徐々に蓄積され、遷移が滑らかになるよう設計されています。そのため、100ミリ秒経過すると自動的に「lit」グループへ移動します。

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も設定されていますが、どこへも遷移しません。さらに2つの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"}
}

1つ目は、パイロットフレームの画像の位置にバインドされたエリアです。未点灯のボールがこの炎を通過すると、パイロットフレームの温度が非常に高いため、ボールは直接「点灯」グループへと移動します。

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

2つ目は、最後のポインタ操作の位置にバインドされており、これにより、点灯していないボール(絶えず動いているため操作が難しい)に触れたりクリックしたりすると、それらが点灯グループに移動するようになっています。

「Move」では、軌道を途中で変更することで得られる簡単なエフェクトをいくつか紹介しています。赤いパーティクルには位置に影響を与えるアフェクタが設定されており、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には煙を伴う炎があり、両方のパーティクルセットはTurbulenceエフェクターの影響を受けています。これにより、かすかな風の効果が生まれます。

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.