Search code examples
swiftaugmented-realityarkitrealitykitreality-composer

Reality Composer - Custom Collision Between Entities of Different Scenes


I'm pretty new to RealityKit and ARKit. I have two scenes in Reality Composer, one with a book image anchor and one with a horizontal plane anchor. The first scene with an image anchor has a cube attached to the top of it and the second scene built on a horizontal plane has two rings. All objects have a fixed collision. I'd like to run an animation when the rings and the cube touch. I couldn't find a way to do this in Reality Composer, so I made two attempts within the code to no avail. (I'm printing "collision started" just to test the collision code without the animation) Unfortunately, it didn't work. Would appreciate help on this.

Attempt #1:

func makeUIView(context: Context) -> ARView {

    let arView = ARView(frame: .zero)

    let componentBreakdownAnchor = try! CC.loadComponentBreakdown()

    arView.scene.anchors.append(componentBreakdownAnchor)

    let bookAnchor = try! CC.loadBook()
    arView.scene.anchors.append(bookAnchor)   

    let ringsAnchor = try! CC.loadRings()
    arView.scene.anchors.append(ringsAnchor)

    // Add the componentBreakdown anchor to the scene
    arView.scene.anchors.append(componentBreakdownAnchor)

    let bookAnchor = try! CC.loadBook()
    arView.scene.anchors.append(bookAnchor)    

    let ringsAnchor = try! CC.loadRings()
    arView.scene.anchors.append(ringsAnchor)

    let _ = ringsAnchor.scene?.subscribe(
    to: CollisionEvents.Began.self,
    on: bookAnchor
    ) { event in
      print("collision started")
    }

    return arView
}

Attempt #2

func makeUIView(context: Context) -> ARView {

    let arView = ARView(frame: .zero)

    let componentBreakdownAnchor = try! CC.loadComponentBreakdown()

    arView.scene.anchors.append(componentBreakdownAnchor)

    let bookAnchor = try! CC.loadBook()
    arView.scene.anchors.append(bookAnchor)  

    let ringsAnchor = try! CC.loadRings()
    arView.scene.anchors.append(ringsAnchor)

    // Add the componentBreakdown anchor to the scene
    arView.scene.anchors.append(componentBreakdownAnchor)

    let bookAnchor = try! CC.loadBook()
    arView.scene.anchors.append(bookAnchor)   

    let ringsAnchor = try! CC.loadRings()
    arView.scene.anchors.append(ringsAnchor)

    arView.scene.subscribe(
      to: CollisionEvents.Began.self,
      on: bookAnchor

    ) { event in
      print("collision started")
    }

    return arView
}

Solution

  • RealityKit scene

    If you want to use models' collisions made in RealityKit's scene from scratch, at first you need to implement a HasCollision protocol.

    Let's see what a developer documentation says about it:

    HasCollision protocol is an interface used for ray casting and collision detection.

    Here's how your implementation should look like if you generate models in RealityKit:

    import Cocoa
    import RealityKit
    
    class CustomCollision: Entity, HasModel, HasCollision {
        
        let color: NSColor = .gray
        let collider: ShapeResource = .generateSphere(radius: 0.5)
        let sphere: MeshResource = .generateSphere(radius: 0.5)
    
        required init() {
            super.init()
            
            let material = SimpleMaterial(color: color,
                                     isMetallic: true)
            
            self.components[ModelComponent] = ModelComponent(mesh: sphere,
                                                        materials: [material])
    
            self.components[CollisionComponent] = CollisionComponent(shapes: [collider],
                                                                       mode: .trigger,
                                                                     filter: .default)
        }
    }
    

    Reality Composer scene

    And here's how your code should look like if you use models from Reality Composer:

    import UIKit
    import RealityKit
    import Combine
    
    class ViewController: UIViewController {
        
        @IBOutlet var arView: ARView!
        var subscriptions: [Cancellable] = []
        
        override func viewDidLoad() {
            super.viewDidLoad()
            
            let groundSphere = try! Experience.loadStaticSphere()
            let upperSphere = try! Experience.loadDynamicSphere()
            
            let gsEntity = groundSphere.children[0].children[0].children[0]
            let usEntity = upperSphere.children[0].children[0].children[0]
            
            // CollisionComponent exists in case you turn on 
            // "Participates" property in Reality Composer app
            print(gsEntity)   
            
            let gsComp: CollisionComponent = gsEntity.components[CollisionComponent]!.self
            let usComp: CollisionComponent = usEntity.components[CollisionComponent]!.self
    
            gsComp.shapes = [.generateBox(size: [0.05, 0.07, 0.05])]
            usComp.shapes = [.generateBox(size: [0.05, 0.05, 0.05])]
            
            gsEntity.components.set(gsComp)
            usEntity.components.set(usComp)
    
            let subscription = self.arView.scene.subscribe(to: CollisionEvents.Began.self,
                                                           on: gsEntity) { event in
                print("Balls' collision occured!")
            }
            self.subscriptions.append(subscription)
            
            arView.scene.anchors.append(upperSphere)
            arView.scene.anchors.append(groundSphere)
        }
    }
    

    enter image description here enter image description here