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는 자바스크립트를 통해 입자의 속성을 직접 조작합니다. 한 Affector는 나뭇잎이 떨어지면서 앞뒤로 흔들리도록 만들어, 단순히 빙글빙글 도는 것보다 더 실제 나뭇잎처럼 보이게 합니다:

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)'은 사용자 정의 어펙터를 사용한 낙엽 효과와 유사하지만, 사용자 정의 어펙터 대신 낙하하는 전 구간에 걸쳐 일정한 마찰력을 적용한다는 점이 다릅니다.

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

중력은 내부에 있는 입자에 일정한 가속도를 적용하기 위한 편의용 어펙터입니다.

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

불이 붙지 않은 공들은 매초 100분의 1의 확률로 스스로 불이 붙지만, 그룹 전체에 ' GroupGoal '가 설정되어 있습니다. 이 어펙터는 불이 붙지 않은 그룹의 모든 입자가 불이 붙은 그룹의 입자와 충돌할 때 영향을 미치며, 해당 입자들이 불이 붙은 그룹으로 이동하도록 합니다.

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

'lighting' 그룹은 중간 단계 그룹으로, 빛의 강도가 서서히 쌓여 전환이 덜 갑작스럽게 느껴지도록 합니다. 따라서 100ms가 지나면 자동으로 '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가 적용되어 있지만, 다른 그룹으로 전환되지는 않습니다. 또한, unlit 그룹의 입자가 lighting 그룹(그리고 그 후 lit 그룹)으로 전환될 수 있도록 하는 GroupGoal 오브젝트가 두 개 더 있습니다.

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
}

두 번째 영역은 마지막 포인터 상호작용 위치에 바인딩되어 있어, 조명이 켜지지 않은 구체를 터치하거나 클릭하면(끊임없이 움직이기 때문에 어렵지만) 해당 구체가 조명 그룹으로 이동하게 됩니다.

'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.