Search code examples
swiftaugmented-realityarkitrealitykitreality-composer

Implement a crosshair kind behaviour in RealityKit


What I want to achieve: Attach a sphere to the camera position (so that it always stay at the center of the screen as the device move) and detect when it is on top of other AR objects - to trigger other actions/behaviour on the AR objects.

Approach: I have created the sphere and attached to the center of the screen as shown below

@IBOutlet var arView: ARView!

override func viewDidLoad() {
    super.viewDidLoad()

    let mesh = MeshResource.generateSphere(radius: 0.1)
    let sphere = ModelEntity(mesh: mesh)

    let anchor = AnchorEntity(.camera)

    sphere.setParent(anchor)
    arView.scene.addAnchor(anchor)

    sphere.transform.translation.z = -0.75
}

Next step, perform a hittest or a raycast in session(_:didUpdate:)

 let results = arView.hitTest(CGPoint(x: 0.5, y: 0.5), query: .all, mask: .default)
//normalised center ; 2D position of the camera (our sphere) in the view’s coordinate system

But I am constantly getting ground plane as my result with this approach. Is there something I am missing or there is a different approach to achieving this

Note: Just in case there is something wrong I have created my basic scene as I want to track an image and add content on top of the image marker in Reality Composer and using the .rcproject in Xcode also have enabled collision property for all the overlaid items.


Solution

  • Try the following solution:

    import ARKit
    import RealityKit
    
    class ViewController: UIViewController {
        
        @IBOutlet var arView: ARView!
        var sphere: ModelEntity?
        
        override func touchesBegan(_ touches: Set<UITouch>, with event: UIEvent?) {
            let touch = arView.center
            let results: [CollisionCastHit] = arView.hitTest(touch)
            
            if let result: CollisionCastHit = results.first {
                if result.entity.name == "Cube" && sphere?.isAnchored == true {
                    print("BOOM!")
                }
            }
        }
        
        override func viewDidLoad() {
            super.viewDidLoad()
            // Crosshair
            let mesh01 = MeshResource.generateSphere(radius: 0.01)
            sphere = ModelEntity(mesh: mesh01)
            sphere?.transform.translation.z = -0.15
            let cameraAnchor = AnchorEntity(.camera)
            sphere?.setParent(cameraAnchor)
            arView.scene.addAnchor(cameraAnchor)
            
            // Model for collision
            let mesh02 = MeshResource.generateBox(size: 0.3)
            let box = ModelEntity(mesh: mesh02, materials: [SimpleMaterial()])
            box.generateCollisionShapes(recursive: true)
            box.name = "Cube"
            let planeAnchor = AnchorEntity(.plane(.any, 
                                  classification: .any, 
                                   minimumBounds: [0.2, 0.2]))
            box.setParent(planeAnchor)
            arView.scene.addAnchor(planeAnchor)
        }
    }