Qt Quick Particles Exemples - Affecteurs

Voici une série d'exemples d'utilisation des Affecteurs dans le système de particules QML.

Des boules rouges flottant devant une flamme bleue, dont l'une est en feu

Il s'agit d'une série de petits exemples QML relatifs à l'utilisation des affectateurs dans le système de particules. Chaque exemple est un petit fichier QML mettant en avant un type ou une fonctionnalité particulière.

L'exemple « Age » montre comment utiliser un affecteur « Age » pour mettre fin prématurément à la durée de vie des particules.

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

Lorsque vous déplacez l’affecteur sur l’écran, les particules qu’il contient (et qui n’ont pas encore été affectées) passent à une durée proche de la fin de leur vie. Cela leur laisse un court laps de temps pour s’éteindre progressivement, mais en définissant `lifeLeft` sur 0 (valeur par défaut), elles atteindraient instantanément la fin de leur vie.

« Attractor » montre comment utiliser un affecteur « Attractor » pour simuler un trou noir

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

Toutes les particules de la scène, y compris les gaz d’échappement et les projectiles de la fusée, sont attirées vers le trou noir. Cet effet est d’autant plus fort qu’on se rapproche du trou noir : ainsi, les astéroïdes situés près du haut de l’écran ne sont pratiquement pas affectés, tandis que ceux situés vers le milieu décrivent parfois des trajectoires très courbes. Pour parachever l’effet, un affecteur « Age » recouvre le trou noir afin de détruire les particules qui entrent en contact avec lui.

L’affecteur « Custom » manipule directement les propriétés des particules en JavaScript. Un affecteur est utilisé pour faire osciller les feuilles d’avant en arrière pendant leur chute, ce qui leur donne un aspect plus réaliste que si elles tournaient simplement en rond :

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

Un autre est utilisé pour appliquer une friction légèrement variable aux feuilles lorsqu’elles « atterrissent », afin de leur donner un aspect plus naturel :

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

L’affecteur « Friction » fonctionne de manière similaire à celui des feuilles qui tombent dans l’affecteur personnalisé, à la différence qu’il applique un frottement constant tout au long de la chute, contrairement aux affecteurs personnalisés.

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

L’affecteur « Gravité » est un affecteur pratique permettant d’appliquer une accélération constante aux particules qu’il contient

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

GroupGoal crée deux groupes de particules pour les boules enflammées et non enflammées, et vous offre différentes façons d’effectuer la transition entre les deux.

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

Les boules non enflammées ont une chance sur cent de s’enflammer d’elles-mêmes chaque seconde, mais elles sont également soumises à un « GroupGoal » appliqué à l’ensemble du groupe. Cet affecteur agit sur toutes les particules du groupe non enflammé lorsqu’elles entrent en collision avec des particules du groupe enflammé, et les fait passer dans le groupe enflammé.

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

Le groupe « lighting » sert de groupe intermédiaire afin que la lueur s’intensifie progressivement et que la transition soit moins brutale. Les particules rejoignent donc automatiquement le groupe « lit » au bout de 100 ms.

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"

Le groupe « lit » comporte également des TrailEmitters pour ajouter des effets de feu et de fumée, mais ne subit aucune transition. Il existe deux autres objets « GroupGoal » qui permettent aux particules du groupe « unlit » de passer dans le groupe « lighting » (puis dans le groupe « lit »).

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

Le premier est simplement une zone liée à l’emplacement d’une image représentant une flamme pilote. Lorsque des boules non éclairées traversent la flamme, elles passent directement dans le groupe « lit » car la flamme pilote est extrêmement chaude.

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

La seconde est liée à l’emplacement de la dernière interaction du pointeur, de sorte que le fait de toucher ou de cliquer sur des boules non éclairées (ce qui est difficile en raison de leur mouvement constant) les fait passer dans le groupe « éclairage ».

La section « Déplacement » présente quelques effets simples que vous pouvez obtenir en modifiant la trajectoire en cours de route. Les particules rouges sont soumises à un affecteur qui influence leur position, les faisant bondir de 120 px vers l’avant.

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

Les particules vertes sont soumises à un affecteur qui agit sur leur vitesse, mais avec une variation d’angle. En ajoutant une composante de vitesse aléatoire à leur vitesse vers l’avant existante, elles commencent à se disperser en formant un cône.

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

Les particules bleues sont soumises à un affecteur qui agit sur leur accélération ; comme celui-ci est défini sur « false », cela réinitialise l’accélération au lieu de l’augmenter. Dès que les particules bleues atteignent l’affecteur, leur vitesse horizontale cesse d’augmenter tandis que leur vitesse verticale diminue.

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

SpriteGoal dispose d’un affecteur qui interagit avec le moteur de sprites des particules, si celles-ci sont dessinées en tant que sprites par ImageParticle.

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

SpriteGoal suit l’image de la fusée à l’écran, et lorsqu’il interagit avec les particules dessinées sous forme de sprites par ImageParticle, il leur ordonne de passer immédiatement à l’état « exploser », qui correspond dans ce cas à l’animation de l’astéroïde se brisant en mille morceaux.

Turbulence génère une flamme accompagnée de fumée, et les deux ensembles de particules sont affectés par un affecteur Turbulence. Cela produit un léger effet de vent.

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

Pour faire changer la direction du vent, remplacez l’image par une image de bruit en noir et blanc dans le paramètre `noiseSource` (il utilise actuellement une source de bruit par défaut).

Wander utilise un affecteur Wander pour ajouter une dérive horizontale aux flocons de neige pendant leur chute.

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

L’application de Wander à différents attributs de la trajectoire génère des mouvements variés ; l’exemple permet donc d’expérimenter facilement et de constater les différences.

Exemple de projet sur 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.