RealityKit Basics: Working with Events

Our apps can respond to a number of scene and component events.

Setting up an Event Subscription

In the article covering RealityViewContent we saw that an important feature it provides is access to Events. We can subscribe to a number of events that occur throughout RealityKit. We call content.subscribe with the event we want. Then we provide a closure with the code to execute when the event fires.

_ = content.subscribe(to: ManipulationEvents.WillBegin.self) { event in
  print("picked up \(event.entity.name)")
}

We can improve this by keeping track of the EventSubscription in our view.

@State private var willBegin: EventSubscription?

Then we can assign the subscription to this variable.

willBegin = content.subscribe(to: ManipulationEvents.WillBegin.self) { event in
  print("picked up \(event.entity.name)")
}

The main reason we do this is to provide ourselves with access to the subscription. We can cancel and reset it if needed. It also helps to cancel and clean up these events with the host view will disappear.

.onDisappear() {
    willBegin?.cancel() // cancel the subscription
    willBegin = nil // nil out the var so we no longer have a reference to the subscription
    willRelease?.cancel()
    willRelease = nil
}

Notable RealityKit Events

Some common event sets include

  • SceneEvents: Events for when entities are added or removed, activated or deactivated. We can also watch hierarchy changes. There is even an update event from which we can read delta time†
  • ComponentEvents: Events raise for adding, removing, activating or deactivating components.
  • CollisionEvents: These events are specific to the Collision features in RealityKit.
  • ManipulationEvents: Convenient events for working with Manipulation Component.

†if you find yourself using delta time in RealityKit, then should probably be working in a custom system…

This Scene Event will print when we add an entity to the scene.

sceneEventExample = content.subscribe(to: SceneEvents.DidAddEntity.self) { event in
  print("entity added to the scene! \(event.entity.name)")
}

While this Component Event will print for each component we add to the entity.

componentEventExample = content.subscribe(to: ComponentEvents.DidAdd.self) { event in
  print("component \(event.componentType) added to \(event.entity.name)")
}

Our entity gets created with three default components. The others are added when we call configureEntity

entity added to the scene! subjectA
component Transform added to subjectA
component SynchronizationComponent added to subjectA
component ModelComponent added to subjectA
component InputTargetComponent added to subjectA
component CollisionComponent added to subjectA
component HoverEffectComponent added to subjectA
component ManipulationComponent added to subjectA

See also:

Full Example Code

struct Example107: View {

    @State private var sceneEventExample: EventSubscription?
    @State private var componentEventExample: EventSubscription?
    @State private var willBegin: EventSubscription?
    @State private var willRelease: EventSubscription?
    @State private var collisionBeganUnfiltered: EventSubscription?

    var body: some View {
        RealityView { content in

            // Scene Event Example
            sceneEventExample = content.subscribe(to: SceneEvents.DidAddEntity.self) { event in
                print("entity added to the scene! \(event.entity.name)")
            }

            // Component Event Example
            componentEventExample = content.subscribe(to: ComponentEvents.DidAdd.self) { event in
                print("component \(event.componentType) added to \(event.entity.name)")
            }

            let subjectA = createStepDemoBox()
            subjectA.name = "subjectA"
            subjectA.position.x = 0.15
            ManipulationComponent.configureEntity(subjectA, collisionShapes: [.generateBox(size: .init(repeating: 0.25))])
            content.add(subjectA)

            let subjectB = subjectA.clone(recursive: true)
            subjectB.name = "subjectB"
            subjectB.position.x = -0.15
            content.add(subjectB)

            // Manipulation Event Examples
            willBegin = content.subscribe(to: ManipulationEvents.WillBegin.self) { event in
                print("picked up \(event.entity.name)")
            }

            willRelease = content.subscribe(to: ManipulationEvents.WillRelease.self) { event in
                print("released up \(event.entity.name)")
            }

            // Collision Event Example
            collisionBeganUnfiltered = content.subscribe(to: CollisionEvents.Began.self)  { collisionEvent in
                print("Collision between \(collisionEvent.entityA.name) and \(collisionEvent.entityB.name)")
            }
        }
        .onDisappear() {
            sceneEventExample?.cancel()
            sceneEventExample = nil
            componentEventExample?.cancel()
            componentEventExample = nil
            willBegin?.cancel()
            willBegin = nil
            willRelease?.cancel()
            willRelease = nil
            collisionBeganUnfiltered?.cancel()
            collisionBeganUnfiltered = nil
        }
    }
}

Download the Xcode project with this and many more examples from Step Into Vision.

Some examples are provided as standalone Xcode projects. You can find those here.

Questions or feedback?