Reality Composer - Custom Collision Between Entities of Different Scenes
Asked Answered
W

1

2

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
}
Whirlwind answered 12/2, 2020 at 14:50 Comment(0)
B
1

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

Breslau answered 12/2, 2020 at 15:52 Comment(5)
This is helpful. For the example with the models from Reality Composer, how do I perform an action like print to the console when those two shapes collide? I've tried subscribing to the collision event on groundSphere and gsEntity ` arView.scene.subscribe( to: CollisionEvents.Began.self, on: gsEntity ) { event in print("collision started") }`Whirlwind
@aspdev91, I've added lines with subscribe() method. Don't forget import Combine framework.Breslau
It works! Thank you so much for the thorough clarifications and updates...Whirlwind
Thanks for this! As a next step, how would one go about to prevent the entities to be able to overlap each other? It seems like a pretty basic use case for AR, where things should behave "as in reality", and somehow it seems extremely hard to get it to work...Centrum
@Silvain, sorry for such a delay! Please publish it as a new question!Breslau

© 2022 - 2024 — McMap. All rights reserved.