Creating custom particle emitters with RealityKit

Creating custom particle emitters with RealityKit

Learn how to create a custom particle emitter with RealityKit in a SwiftUI app for visionOS.

The article The Power of Particle Emitters in Digital Creation explained how particles can improve the user experience of applications in the era of spatial computing. Using RealityKit we can already include some built-in emitters provided by the framework in our applications. Additionally, we can create our custom particle emitter that fits our specific needs.

Let’s explore using default particle effects and create a customized one in a visionOS project. By the end of the tutorial, you will learn how to use default particle emitters and create a custom one.

Step 1 - Creating a new project

The first step involves creating a new visionOS project:

In the setup process specify the following configuration

  • Initial Scene: Window
  • Immersive Space Render: None
  • Immersive Space: None

Step 2 - Implement built-in particle emitter

RealityKit provides a collection of particle emitters we can easily include in our project. Start by defining a new function inside the ContentView that returns an Entity with a ParticleEmitterComponent object applied as a component.

In the ContentView.swift file:

import SwiftUI
import RealityKit

struct ContentView: View {
    var body: some View {
        RealityView { content in
        
        }
    }
    
    // 1.
    func emitter() -> Entity {
        // 2.
        let particles = ParticleEmitterComponent.Presets.magic
        // 3.
        let model = Entity()
        // 4.
        model.components.set(particles)
        
        return model
    }
}
  1. Define a new method named emitter() that returns an Entity object
  2. Define a property named particles where we assign one of the built-in emitters included in the ParticleEmitterComponent object. In this case, we chose the Presets.magic emitter
  3. Create an instance of the Entity object named model
  4. Attach to the model property the particle emitter as a component of the entity

Step 3 - Display the particle emitter

After defining the method that returns the model with the ParticleEmitterComponent applied we can include the entity inside a RealityView component.

import SwiftUI
import RealityKit

struct ContentView: View {
    var body: some View {
        // 1.
        RealityView { content in
            content.add(emitter())
        }
    }
    
    func emitter() -> Entity { ... }
}
  1. Add to the content of the RealityView component the Entity that the emitter() function returns

Step 4 - Create a custom emitter component

Until now we used the Presets that RealityKit provides by default. In this step, we are going to add a custom particle emitter where we can define some important properties.

import SwiftUI
import RealityKit

struct ContentView: View {
    var body: some View {
        RealityView { content in
            ...
        }
    }
    
    func emitter() -> Entity {
        ...
    }
    
   // 1.
    func customParticleEmitter(color: Color) -> ModelEntity {
        // 2.
        let model = ModelEntity()
        // 3.
        var particles = ParticleEmitterComponent()
        // 4.
        particles.emitterShape = .sphere
        // 5.
        particles.emitterShapeSize = [0.05, 0.05, 0.05]
        // 6.
        particles.mainEmitter.birthRate = 2000
        // 7.
        particles.mainEmitter.size = 0.05
        // 8.
        particles.mainEmitter.lifeSpan = 0.5
        
        // 9.
        particles.mainEmitter.color = .evolving(
            start: .single(UIColor(color)),
            end: .single(.black)
        )
        // 10.
        model.components.set(particles)
        // 11.
        return model
    }
}
  1. Define a new method named customParticleSystem(color:) that returns a ModelEntity object
  2. Create a new instance of the ModelEntity object and name it model
  3. Initialize a new instance of ParticleEmitterComponent
  4. Set the emitterShape property of the particles object as .sphere. In this way, the particles will be emitted in a spherical shape
  5. Set the emitterShapeSize property of the particles object as [0.05, 0.05, 0.05] to determine the size of the emitter in meters
  6. Set the birthRate property of the mainEmitter to 2000, controlling the number of particles emitted per second
  7. Set the size property of the mainEmitter to 0.05, determining the size of the particles
  8. Set the lifeSpan property of the mainEmitter to 0.5, determining how long each particle will live
  9. Set the color property of the mainEmitter to an evolving color, meaning the color of the particles will change over time from red to blue
  10. Attach the previously defined particles object as a component of the model property
  11. Return the model object

Step 5 - Displaying the custom particle emitter

As we did in the second step, let’s add the custom emitter to the RealityView's content inside the ContentView view.

import SwiftUI
import RealityKit

struct ContentView: View {
    var body: some View {
        RealityView { content in
            content.add(emitter())
            
            // 1.
            content.add(customParticleEmitter(color: .blue))
        }
    }
    
    func emitter() -> Entity {
        ...
    }
    
    func customParticleSystem() -> ModelEntity {
		...
    }
}
  1. Add to the content of the RealityView the ModelEntity that the customParticleSystem() method returns

Step 6 - Make the custom particle emitter dynamic

Now that we have defined our custom particle emitter, we can experiment with changing some parameters, such as the color of the particles, by including a simple ColorPicker in our UI.

import SwiftUI
import RealityKit

struct ContentView: View {
	// 1.
    @State var selectedColor: Color = .cyan
    
    var body: some View {
        // 2.
        HStack {
            ColorPicker("Select first color", selection: $selectedColor)
                .pickerStyle(InlinePickerStyle())
                .frame(width: 250)
                .padding()
                
            Spacer()
            
            RealityView { content in
                
                content.add(emitter())
                content.add(customParticleEmitter(color: selectedColor))
                
              // 3.
            } update: { content in
                // 4.
                content.entities.removeAll()
                // 5.
                content.add(customParticleEmitter(color: selectedColor))
                content.add(emitter())
            }
        }
    }
    
    func customParticleEmitter(color: Color) -> ModelEntity {
        ...
    }
    
    
    func emitter() -> Entity {
        ...
    }
}
  1. Declare a new property named selectedColor to store color information.
  2. Wrap the RealityView component inside an HStack container and add a ColorPicker control to allow the user to select the color of the particler of the emitter
  3. Use the update closure to update the RealityView content in response to changes in the selectedColor parameter.
  4. When the update closure is called all the entities are removed from the RealityView content using the removeAll() method
  5. Add to the RealityView content the two new particle emitters after the update runs

Conclusion

0:00
/0:23

The final result will be a combination of our custom emitter with the magic particle emitter provided by RealityKit. Using the capabilities of the RealityView we also managed to update the emitter when the user selected a new color.

Another option that you need to consider is using software such as Reality Composer Pro to define custom particle emitter components for your app.