Search code examples
swiftuirealitykitvisionos

visionOS | How can I detect when two entities collide in RealityKit?


So I have the default scene for an ImmersiveView that has two plain spheres. I am moving one of them with my game controller.

import SwiftUI
import RealityKit
import RealityKitContent
import GameController

struct ImmersiveView: View {
    
    @State var sphereX: Float = 0.5
    @State var sphereY: Float = 1.5
    @State var sphereZ: Float = -1.5
    
    var body: some View {
        RealityView { content in
            if let scene = try? await Entity(named: "Immersive", in: realityKitContentBundle) {
                content.add(scene)
            }
            
            Task.detached {
                for await _ in NotificationCenter.default.notifications(named: .GCControllerDidConnect) {
                    Task { @MainActor in
                        for controller in GCController.controllers() {
                            controller.extendedGamepad?.valueChangedHandler = { pad, _ in
                                Task { @MainActor in
                                    let speed: Float = 0.1
                                    sphereX += pad.leftThumbstick.xAxis.value * speed
                                    sphereY += pad.leftThumbstick.yAxis.value * speed
                                    sphereZ -= pad.rightThumbstick.yAxis.value * speed
                                }
                            }
                        }
                    }
                }
            }
        } update: { content in
            guard let scene = content.entities.first,
                  let movableSphere = scene.findEntity(named: "Sphere_Right") else {
                return
            }
            movableSphere.position.x = sphereX
            movableSphere.position.y = sphereY
            movableSphere.position.z = sphereZ
        }
    }
}

How can I detect that the movable sphere collides with the other sphere?


Solution

  • Detecting collisions in RealityView for visionOS

    Let's assume that Reality Composer Pro scene contains a cube that sits above a sphere primitive. Both models must have the Physics Body component (first is dynamic and second is static) and Collision component. Try the following code if you're planning to detect a collision between 3D objects.

    import SwiftUI
    import RealityKit
    import RealityKitContent
    
    struct ContentView : View {
        
        @State private var subs: [EventSubscription] = []
    
        var body: some View {
            VStack {
                RealityView { content in
                    if let scene = try? await Entity(named: "Scene", in: realityKitContentBundle) {
                        content.add(scene)
                    }
                } update: { content in
                    if let cube = content.entities.first?.findEntity(named: "Cube") as? ModelEntity {
                        let event = content.subscribe(to: CollisionEvents.Began.self, on: cube) { ce in
    
                            print("Collision between \(ce.entityA.name) and \(ce.entityB.name) is occured")
                        }
                        DispatchQueue.main.async {
                            subs.append(event)
                        }
                    }
                }
            }
        }
    }
    #Preview {
        ContentView()
    }
    

    enter image description here


    Or create a scene programmatically from scratch:

    struct ContentView : View {
        
        @State private var subs: [EventSubscription] = []
    
        var body: some View {
            VStack {
                RealityView { content in                    
                    let ball = ModelEntity(mesh: .generateSphere(radius: 0.15))
                    ball.generateCollisionShapes(recursive: false)
                    ball.name = "Sphere"
                    content.add(ball)
                    
                    let cube = ModelEntity(mesh: .generateBox(size: 0.25))
                    cube.generateCollisionShapes(recursive: false)
                    cube.components[PhysicsBodyComponent.self] = .init()
                    cube.position = [0.259, 2.0, 0.0]
                    cube.name = "Cube"
                    content.add(cube)
    
                    let event = content.subscribe(to: CollisionEvents.Began.self, on: cube) { ce in
                        print("Collision between \(ce.entityA.name) and \(ce.entityB.name) is occured")
                    }
                    Task {
                        subs.append(event)
                    }
                } 
            }
        }
    }