Particle Groups

At the beginning of this chapter, we stated particles are in groups, which is by default the empty group (‘’). Using the GroupGoal affector is it possible to let the particle change groups. To visualize this we would like to create a small firework, where rockets start into space and explode in the air into a spectacular firework.

image

The example is divided into 2 parts. The 1st part called “Launch Time” is concerned to set up the scene and introduce particle groups and the 2nd part called “Let there be fireworks” focuses on the group changes.

Let’s get started!

Launch Time

To get it going we create a typical dark scene:

  1. import QtQuick 2.5
  2. import QtQuick.Particles 2.0
  3. Rectangle {
  4. id: root
  5. width: 480; height: 240
  6. color: "#1F1F1F"
  7. property bool tracer: false
  8. }

The tracer property will be used to switch the tracer scene wide on and off. The next thing is to declare our particle system:

  1. ParticleSystem {
  2. id: particleSystem
  3. }

And our two image particles (one for the rocket and one for the exhaust smoke):

  1. ImageParticle {
  2. id: smokePainter
  3. system: particleSystem
  4. groups: ['smoke']
  5. source: "assets/particle.png"
  6. alpha: 0.3
  7. entryEffect: ImageParticle.None
  8. }
  9. ImageParticle {
  10. id: rocketPainter
  11. system: particleSystem
  12. groups: ['rocket']
  13. source: "assets/rocket.png"
  14. entryEffect: ImageParticle.None
  15. }

You can see in on the images, they use the groups property to declare to which group the particle belongs. It is enough to just declare a name and an implicit group will be created by Qt Quick.

Now it’s time to emit some rockets into the air. For this, we create an emitter on the bottom of our scene and set the velocity in an upward direction. To simulate some gravity we set an acceleration downwards:

  1. Emitter {
  2. id: rocketEmitter
  3. anchors.bottom: parent.bottom
  4. width: parent.width; height: 40
  5. system: particleSystem
  6. group: 'rocket'
  7. emitRate: 2
  8. maximumEmitted: 4
  9. lifeSpan: 4800
  10. lifeSpanVariation: 400
  11. size: 32
  12. velocity: AngleDirection { angle: 270; magnitude: 150; magnitudeVariation: 10 }
  13. acceleration: AngleDirection { angle: 90; magnitude: 50 }
  14. Tracer { color: 'red'; visible: root.tracer }
  15. }

The emitter is in the group ‘rocket’, the same as our rocket particle painter. Through the group name, they are bound together. The emitter emits particles into the group ‘rocket’ and the rocket particle painter will pain them.

For the exhaust, we use a trail emitter, which follows our rocket. It declares an own group called ‘smoke’ and follows the particles from the ‘rocket’ group:

  1. TrailEmitter {
  2. id: smokeEmitter
  3. system: particleSystem
  4. emitHeight: 1
  5. emitWidth: 4
  6. group: 'smoke'
  7. follow: 'rocket'
  8. emitRatePerParticle: 96
  9. velocity: AngleDirection { angle: 90; magnitude: 100; angleVariation: 5 }
  10. lifeSpan: 200
  11. size: 16
  12. sizeVariation: 4
  13. endSize: 0
  14. }

The smoke is directed downwards to simulate the force the smoke comes out of the rocket. The emitHeight and emitWidth specify the are around the particle followed from where the smoke particles shall be emitted. If this is not specified then they are of the particle followed is taken but for this example, we want to increase the effect that the particles stem from a central point near the end of the rocket.

If you start the example now you will see the rockets fly up and some are even flying out of the scene. As this is not really wanted we need to slow them down before they leave the screen. A friction affector can be used here to slow the particles down to a minimum threshold:

  1. Friction {
  2. groups: ['rocket']
  3. anchors.top: parent.top
  4. width: parent.width; height: 80
  5. system: particleSystem
  6. threshold: 5
  7. factor: 0.9
  8. }

In the friction affector, you also need to declare which groups of particles it shall affect. The friction will slow all rockets, which are 80 pixels downwards from the top of the screen down by a factor of 0.9 (try 100 and you will see they almost stop immediately) until they reach a velocity of 5 pixels per second. As the particles have still an acceleration downwards applied the rockets will start sinking toward the ground after they reach the end of their life-span.

As climbing up in the air is hard work and a very unstable situation we want to simulate some turbulences while the ship is climbing:

  1. Turbulence {
  2. groups: ['rocket']
  3. anchors.bottom: parent.bottom
  4. width: parent.width; height: 160
  5. system: particleSystem
  6. strength: 25
  7. Tracer { color: 'green'; visible: root.tracer }
  8. }

Also, the turbulence needs to declare which groups it shall affect. The turbulence itself reaches from the bottom 160 pixels upwards (until it reaches the border of the friction). They also could overlap.

When you start the example now you will see the rockets are climbing up and then will be slowed down by the friction and fall back to the ground by the still applied downwards acceleration. The next thing would be to start the firework.

image

TIP

The image shows the scene with the tracers enabled to show the different areas. Rocket particles are emitted in the red area and then affected by the turbulence in the blue area. Finally, they are slowed down by the friction affector in the green area and start falling again, because of the steady applied downwards acceleration.

Let there be fireworks

To be able to change the rocket into a beautiful firework we need add a ParticleGroup to encapsulate the changes:

  1. ParticleGroup {
  2. name: 'explosion'
  3. system: particleSystem
  4. }

We change to the particle group using a GroupGoal affector. The group goal affector is placed near the vertical center of the screen and it will affect the group ‘rocket’. With the groupGoal property we set the target group for the change to ‘explosion’, our earlier defined particle group:

  1. GroupGoal {
  2. id: rocketChanger
  3. anchors.top: parent.top
  4. width: parent.width; height: 80
  5. system: particleSystem
  6. groups: ['rocket']
  7. goalState: 'explosion'
  8. jump: true
  9. Tracer { color: 'blue'; visible: root.tracer }
  10. }

The jump property states the change in groups shall be immediately and not after a certain duration.

TIP

In the Qt 5 alpha release we could the duration for the group change not get working. Any ideas?

As the group of the rocket now changes to our ‘explosion’ particle group when the rocket particle enters the group goal area we need to add the firework inside the particle group:

  1. // inside particle group
  2. TrailEmitter {
  3. id: explosionEmitter
  4. anchors.fill: parent
  5. group: 'sparkle'
  6. follow: 'rocket'
  7. lifeSpan: 750
  8. emitRatePerParticle: 200
  9. size: 32
  10. velocity: AngleDirection { angle: -90; angleVariation: 180; magnitude: 50 }
  11. }

The explosion emits particles into the ‘sparkle’ group. We will define soon a particle painter for this group. The trail emitter used follows the rocket particle and emits per rocket 200 particles. The particles are directed upwards and vary by 180 degrees.

As the particles are emitted into the ‘sparkle’ group, we also need to define a particle painter for the particles:

  1. ImageParticle {
  2. id: sparklePainter
  3. system: particleSystem
  4. groups: ['sparkle']
  5. color: 'red'
  6. colorVariation: 0.6
  7. source: "assets/star.png"
  8. alpha: 0.3
  9. }

The sparkles of our firework shall be little red stars with an almost transparent color to allow some shine effects.

To make the firework more spectacular we also add a second trail emitter to our particle group, which will emit particles in a narrow cone downwards:

  1. // inside particle group
  2. TrailEmitter {
  3. id: explosion2Emitter
  4. anchors.fill: parent
  5. group: 'sparkle'
  6. follow: 'rocket'
  7. lifeSpan: 250
  8. emitRatePerParticle: 100
  9. size: 32
  10. velocity: AngleDirection { angle: 90; angleVariation: 15; magnitude: 400 }
  11. }

Otherwise, the setup is similar to the other explosion trail emitter. That’s it.

Here is the final result.

image

Here is the full source code of the rocket firework.

  1. import QtQuick
  2. import QtQuick.Particles
  3. Rectangle {
  4. id: root
  5. width: 480; height: 240
  6. color: "#1F1F1F"
  7. property bool tracer: false
  8. ParticleSystem {
  9. id: particleSystem
  10. }
  11. ImageParticle {
  12. id: smokePainter
  13. system: particleSystem
  14. groups: ['smoke']
  15. source: "assets/particle.png"
  16. alpha: 0.3
  17. }
  18. ImageParticle {
  19. id: rocketPainter
  20. system: particleSystem
  21. groups: ['rocket']
  22. source: "assets/rocket.png"
  23. entryEffect: ImageParticle.Fade
  24. }
  25. Emitter {
  26. id: rocketEmitter
  27. anchors.bottom: parent.bottom
  28. width: parent.width; height: 40
  29. system: particleSystem
  30. group: 'rocket'
  31. emitRate: 2
  32. maximumEmitted: 8
  33. lifeSpan: 4800
  34. lifeSpanVariation: 400
  35. size: 128
  36. velocity: AngleDirection { angle: 270; magnitude: 150; magnitudeVariation: 10 }
  37. acceleration: AngleDirection { angle: 90; magnitude: 50 }
  38. Tracer { color: 'red'; visible: root.tracer }
  39. }
  40. TrailEmitter {
  41. id: smokeEmitter
  42. system: particleSystem
  43. group: 'smoke'
  44. follow: 'rocket'
  45. size: 16
  46. sizeVariation: 8
  47. emitRatePerParticle: 16
  48. velocity: AngleDirection { angle: 90; magnitude: 100; angleVariation: 15 }
  49. lifeSpan: 200
  50. Tracer { color: 'blue'; visible: root.tracer }
  51. }
  52. Friction {
  53. groups: ['rocket']
  54. anchors.top: parent.top
  55. width: parent.width; height: 80
  56. system: particleSystem
  57. threshold: 5
  58. factor: 0.9
  59. }
  60. Turbulence {
  61. groups: ['rocket']
  62. anchors.bottom: parent.bottom
  63. width: parent.width; height: 160
  64. system: particleSystem
  65. strength:25
  66. Tracer { color: 'green'; visible: root.tracer }
  67. }
  68. ImageParticle {
  69. id: sparklePainter
  70. system: particleSystem
  71. groups: ['sparkle']
  72. color: 'red'
  73. colorVariation: 0.6
  74. source: "assets/star.png"
  75. alpha: 0.3
  76. }
  77. GroupGoal {
  78. id: rocketChanger
  79. anchors.top: parent.top
  80. width: parent.width; height: 80
  81. system: particleSystem
  82. groups: ['rocket']
  83. goalState: 'explosion'
  84. jump: true
  85. Tracer { color: 'blue'; visible: root.tracer }
  86. }
  87. ParticleGroup {
  88. name: 'explosion'
  89. system: particleSystem
  90. TrailEmitter {
  91. id: explosionEmitter
  92. anchors.fill: parent
  93. group: 'sparkle'
  94. follow: 'rocket'
  95. lifeSpan: 750
  96. emitRatePerParticle: 200
  97. size: 32
  98. velocity: AngleDirection { angle: -90; angleVariation: 180; magnitude: 50 }
  99. }
  100. TrailEmitter {
  101. id: explosion2Emitter
  102. anchors.fill: parent
  103. group: 'sparkle'
  104. follow: 'rocket'
  105. lifeSpan: 250
  106. emitRatePerParticle: 100
  107. size: 32
  108. velocity: AngleDirection { angle: 90; angleVariation: 15; magnitude: 400 }
  109. }
  110. }
  111. }