Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How to identify if user clicks on object that has been shown on screen using ARKit - swift

I am a new iOS programming. I want to build an application which allow user to click on specific object that has been shown on the screen. I am using addGestureRecognizer to the object that has been shown in order to identify if user has been clicked then i just want to add another object to the screen.

Here is what i done so far

objpizza = make2dNode(image:#imageLiteral(resourceName: "pizza"),width: 0.07,height: 0.07)
    objpizza.position = SCNVector3(0,0,-0.2)
    objpizza.name = "none"

    self.arView.addGestureRecognizer(UIGestureRecognizer(target: self, action: #selector(selectObject)))
    arView.scene.rootNode.addChildNode(objpizza)

Here is make2dNode function just adjust the object

func make2dNode(image: UIImage, width: CGFloat = 0.1, height: CGFloat = 0.1) -> SCNNode {
    let plane = SCNPlane(width: width, height: height)
    plane.firstMaterial!.diffuse.contents = image
    let node = SCNNode(geometry: plane)
    node.constraints = [SCNBillboardConstraint()]
    return node
}

Here is function that never called when i implemented in self.arView.addGestureRecognizer(UIGestureRecognizer(target: self, action: #selector(selectObject)))

@objc func selectObject() {

    print("Image has been selected")
}
like image 806
Visalkoko Avatar asked Jul 01 '18 09:07

Visalkoko


2 Answers

Detecting a touch on an SCNNode requires more than simply adding a UITapGestureRecogniser to your view.

In order to detect which SCNNode you have touched you need to make use of an SCNHitTest (in combination with your gestureRecognizer) which is:

The process of finding elements of a scene located at a specified point, or along a specified line segment (or ray).

An SCNHitTest looks for:

SCNGeometry objects along the ray you specify. For each intersection between the ray and and a geometry, SceneKit creates a hit-test result to provide information about both the SCNNode object containing the geometry and the location of the intersection on the geometry’s surface.

Ok great you might be thinking, but how does this actually work in my case?

Well, lets begin by creating an SCNNode with an SCNSphere Geometry and adding it to our scene.

//1. Create An SCNNode With An SCNSphere Geometry
let nodeOneGeometry = SCNSphere(radius: 0.2)

//2. Set It's Colour To Cyan
nodeOneGeometry.firstMaterial?.diffuse.contents = UIColor.cyan

//3. Assign The Geometry To The Node
nodeOne = SCNNode(geometry: nodeOneGeometry)

//4. Assign A Name For Our Node
nodeOne.name = "Node One"

//5. Position It & Add It To Our ARSCNView
nodeOne.position = SCNVector3(0, 0, -1.5)
augmentedRealityView.scene.rootNode.addChildNode(nodeOne)

You will note here, that I have assigned a name to our SCNNode, this makes keeping track of it (e.g. identifying it through a hitTest) much easier.

Now we have added our SCNNode to the hierachy, lets create a UITapGestureRecognizer like so:

//1. Create A UITapGestureRecognizer & Add It To Our MainView
let tapGesture = UITapGestureRecognizer(target: self, action: #selector(checkNodeHit(_:)))
tapGesture.numberOfTapsRequired = 1
self.view.addGestureRecognizer(tapGesture)

Now we have this all setup we need to create our checkNodeHit function will detect which node the user has tapped on:

/// Runs An SCNHitTest To Check If An SCNNode Has Been Hit
///
/// - Parameter gesture: UITapGestureRecognizer
@objc func checkNodeHit(_ gesture: UITapGestureRecognizer){

    //1. Get The Current Touch Location In The View
    let currentTouchLocation = gesture.location(in: self.augmentedRealityView)

    //2. Perform An SCNHitTest To Determine If We Have Hit An SCNNode
    guard let hitTestNode = self.augmentedRealityView.hitTest(currentTouchLocation, options: nil).first?.node else { return }

    if hitTestNode.name == "Node One"{

        print("The User Has Successfuly Tapped On \(hitTestNode.name!)")

    }
}

Now if you wanted to place an SCNNode where the user tapped you would have to use an ARSCNHitTest instead to do so which provides:

Information about a real-world surface found by examining a point in the device camera view of an AR session.

By performing this we are then able to use the worldTransform property of our result to place virtual content at that location.

For your information the worldTransform is:

The position and orientation of the hit test result relative to the world coordinate system.

Positioning in ARKit can easily be visualised like so:

enter image description here

Again lets look at how we could use this to place a virtual object:

override func touchesBegan(_ touches: Set<UITouch>, with event: UIEvent?) {

    //1. Get The Current Touch Location In Our ARSCNView & Perform An ARSCNHitTest For Any Viable Feature Points
    guard let currentTouchLocation = touches.first?.location(in: self.augmentedRealityView),
          let hitTest = self.augmentedRealityView.hitTest(currentTouchLocation, types: .featurePoint).first else { return }

    //2. Get The World Transform From The HitTest & Get The Positional Data From The Matrix (3rd Column)
    let worldPositionFromTouch = hitTest.worldTransform.columns.3

    //3. Create An SCNNode At The Touch Location
    let boxNode = SCNNode()
    let boxGeometry = SCNBox(width: 0.1, height: 0.1, length: 0.1, chamferRadius: 0)
    boxGeometry.firstMaterial?.diffuse.contents = UIColor.cyan
    boxNode.geometry = boxGeometry
    boxNode.position = SCNVector3(worldPositionFromTouch.x, worldPositionFromTouch.y, worldPositionFromTouch.z)

    //4. Add It To The Scene Hierachy
    self.augmentedRealityView.scene.rootNode.addChildNode(boxNode)

}

Hope it helps...

like image 112
BlackMirrorz Avatar answered Sep 22 '22 03:09

BlackMirrorz


You need to implement

let tapGesture = UITapGestureRecognizer(target: self, action: #selector(didTap(_:)))
arView.addGestureRecognizer(tapGesture)

For function identifies if user has been clicked

@obj func didTap(_ gesture: UITabGestureRecognizer){ 
  object2 = make2Node(image: nameOfimage, width: 0.07, height: 0.07)
  object2.position = SCNVector(0,0,0.2)
  arView.scene.rootNode.addChildNode(object2)

Enjoy coding bro.

like image 43
Visal Sambo Avatar answered Sep 24 '22 03:09

Visal Sambo