Collisions & Physics: Collision Use Cases

The Collision Component is used input, collision detection, and physics.

Overview

In the last example we touched on a handful of use cases for Collision Components in RealityKit.

We’ll explore three of those today.

  • Using Input and Collision Components to interact with entities using system gestures.
  • Executing code when entities collide.
  • Using Collision Components to define the shapes used in physics.

This example has three spheres.

We’ll use the the red sphere for a tap gesture. To use an entity with system gestures, we need two things. An Input Component and a Collision Component. When we tap this sphere, we’ll fire a notification that will trigger an animation timeline in the scene. This will cause the green sphere to move up to a new position.

var inputTapExample: some Gesture {
    TapGesture().targetedToEntity(exampleInput)
        .onEnded({ value in

            // Fire a behavior from the scene in Reality Composer Pro
            _ = value.entity.applyTapForBehaviors()

            // Then fire a notification to move the trigger sphere to the switch
            NotificationCenter.default.post(
                name: NSNotification.Name("RealityKit.NotificationTrigger"),
                object: nil,
                userInfo: [
                    "RealityKit.NotificationTrigger.Scene": scene as Any,
                    "RealityKit.NotificationTrigger.Identifier": "MoveTriggerToSwitch"
                ]
            )
        })
}

We’ll use the green sphere to explore the second use case: collision detection. Both the green sphere and the ExampleTriggerSwitch have basic collision shapes, with their mode values set to Trigger. We can watch for collisions using a Swift EventSubscription.

collisionExampleEvent = content.subscribe(to: CollisionEvents.Began.self, on: triggerSwitch)  { _ in
  examplePhysics.setPosition([0.25, 1, 0], relativeTo: examplePhysics.parent)
}

When the green sphere reaches the ExampleTriggerSwitch, this event will fire and will reset the position of the blue sphere.

Finally, we’ll use the blue sphere to take a look at physics. We’ll define a sphere collision, then give it a Physics Body component. Gravity is enabled, so this will fall as soon as the scene starts. We’ll also add a collision and physics body to the table. We’ll mark that it as static and unaffected by gravity. This will allow the blue sphere to fall onto the table

We can also use triggers directly in Reality Composer Pro to activate behaviors that run timelines. We’ll explore that more in a series on Timelines & Behaviors.

Full Example Code

This example makes heavy use of Reality Composer Pro to set up collisions and run timelines. See the scene called CollisionUsecases if you want to explore it yourself.

struct Example056: View {
    // We need to get the realityKitScene to be able to send notifications that trigger behaviors
    @Environment(\.realityKitScene) var scene

    @State var exampleInput = Entity()
    @State var collisionExampleEvent: EventSubscription?

    var body: some View {
        RealityView { content, attachments in

            // Load the scene and position it in the volume
            guard let scene = try? await Entity(named: "CollisionUsecases", in: realityKitContentBundle) else { return }
            content.add(scene)
            scene.position.y = -0.4

            // Example 01: The red sphere has input and collision components, which allows it to receive targeted gestures like tap drag, etc. We'll send a notification to call a timeline to start moving the green sphere.
            // Example 02: The green sphere has a collision the mode set to trigger. When it collides with the triggerSwitch, we will reset the position of the blue sphere.
            // Example 03: The blue sphere collision and physics body components. Gravity is enabled, so it will fall to the table.
            if let input = scene.findEntity(named: "ExampleInput"),
                let triggerSwitch = scene.findEntity(named: "ExampleTriggerSwitch"),
                let examplePhysics = scene.findEntity(named: "ExamplePhysics")  {

                input.components.set(HoverEffectComponent())
                exampleInput = input

                collisionExampleEvent = content
                    .subscribe(to: CollisionEvents.Began.self, on: triggerSwitch)  { _ in
                        examplePhysics.setPosition([0.25, 1, 0], relativeTo: examplePhysics.parent)
                    }

                // Position our attachments
                if let label01 = attachments.entity(for: "Example01") {
                    label01.setPosition([0,0.2,0], relativeTo: input)
                    label01.components.set(BillboardComponent())
                    content.add(label01)
                }
                if let label02 = attachments.entity(for: "Example02") {
                    label02.setPosition([0,0.12,0], relativeTo: triggerSwitch)
                    label02.components.set(BillboardComponent())
                    content.add(label02)
                }

                if let label03 = attachments.entity(for: "Example03") {
                    label03.setPosition([0.25,0.3,0], relativeTo: nil)
                    label03.components.set(BillboardComponent())
                    content.add(label03)
                }

            }

        } update: { content, attachments in
        } attachments: {
            Attachment(id: "Example01") {
                Text("Collision + Input")
                    .font(.title)
                    .padding()
                    .glassBackgroundEffect()
            }

            Attachment(id: "Example02") {
                Text("Collision triggers an action")
                    .font(.title)
                    .padding()
                    .glassBackgroundEffect()
            }

            Attachment(id: "Example03") {
                Text("Collision shapes are used for physics")
                    .font(.title)
                    .padding()
                    .glassBackgroundEffect()
            }
        }
        .gesture(inputTapExample)
    }

    var inputTapExample: some Gesture {
        TapGesture().targetedToEntity(exampleInput)
            .onEnded({ value in

                // Fire a behavior from the scene in Reality Composer Pro
                _ = value.entity.applyTapForBehaviors()

                // Then fire a notification to move the trigger sphere to the switch
                NotificationCenter.default.post(
                    name: NSNotification.Name("RealityKit.NotificationTrigger"),
                    object: nil,
                    userInfo: [
                        "RealityKit.NotificationTrigger.Scene": scene as Any,
                        "RealityKit.NotificationTrigger.Identifier": "MoveTriggerToSwitch"
                    ]
                )
            })
    }
}

#Preview {
    Example056()
}

Support our work so we can continue to bring you new examples and articles.

Questions or feedback?