hitTest vs rayCast

Raycasting if for real world objects (eg, walls, the ground IRL), and HitTesting is for virtual content eg, your 3d models.

//  ViewController.swift
//  RayVsHit
//
//  Created by Dan Monaghan on 8/08/22.
//

import UIKit
import SceneKit
import ARKit

class ViewController: UIViewController, ARSCNViewDelegate {

    enum tapType : Int {
        case hitTest
        case rayCast
    }

    @IBOutlet var sceneView: ARSCNView!

    var tapGesture = UITapGestureRecognizer()
    var tapCounter = 0
    var gestureSwitcher = UISwitch()
    var currentTapType = tapType.hitTest


    override func viewDidLoad() {
        super.viewDidLoad()
        sceneView.delegate = self
        let scene = SCNScene(named: "art.scnassets/ship.scn")
        sceneView.scene = scene

        tapGesture = UITapGestureRecognizer(target: self, action: #selector(handleTap(_:)))
        sceneView.gestureRecognizers?.append(tapGesture)
        gestureSwitcher.frame = CGRect(x: 50, y: 50, width: 0, height: 0)
        gestureSwitcher.addTarget(self, action: #selector(handleSwitch(_:)), for: .touchUpInside)
        sceneView.addSubview(gestureSwitcher)
    }
    @objc func handleSwitch(_ sender: UISwitch){
        if sender.isOn {
            currentTapType = .hitTest
        } else if !sender.isOn{
            currentTapType = .rayCast
        }


    }

    @objc func handleTap(_ sender: UITapGestureRecognizer){
        let nodeToAdd = SCNNode()
        nodeToAdd.name = "testingNode"
        if currentTapType == .rayCast {
            print("ray casting")
            guard let query = sceneView.raycastQuery(from: sender.location(in: sceneView), allowing: .estimatedPlane , alignment: .any) else { return }
            if let result = sceneView.session.raycast(query).first {
                print("result = \(String(describing: result))")
                nodeToAdd.transform = SCNMatrix4(result.worldTransform)
                let boxGeo = SCNBox(width: 0.04, height: 0.04, length: 0.04, chamferRadius: 0)
                boxGeo.firstMaterial?.diffuse.contents = UIColor.systemBlue
                nodeToAdd.geometry = boxGeo
                sceneView.scene.rootNode.addChildNode(nodeToAdd)
            }

        }

        if currentTapType == .hitTest{
            if let hitResults = sceneView.hitTest(sender.location(in: sceneView), options: [:]) as [SCNHitTestResult]? {

                if !hitResults.isEmpty {
                    if let hitRes : SCNHitTestResult = hitResults.first {
                        if hitRes.node.name == "testingNode" {
                            return
                        }
                        print("results = \(hitRes)")
                        let point = hitRes.worldCoordinates
                        nodeToAdd.position = point
                        let sphereGeo = SCNSphere(radius: 0.04)
                        sphereGeo.firstMaterial?.diffuse.contents = UIColor.systemRed
                        nodeToAdd.geometry = sphereGeo
                        sceneView.scene.rootNode.addChildNode(nodeToAdd)
                    }
                }
            }
        }
        tapCounter += 1
    }

    override func viewWillAppear(_ animated: Bool) {
        super.viewWillAppear(animated)
        let configuration = ARWorldTrackingConfiguration()
        sceneView.session.run(configuration)
    }

    override func viewWillDisappear(_ animated: Bool) {
        super.viewWillDisappear(animated)
        sceneView.session.pause()
    }


}
/r/ARKitCreators Thread Parent