I'm tying to create an app with SceneKit to solve a Rubik's Cube. I've made my own dae
file for the cube. Upon touches began I have the object that's been hit
func tapGesture(sender: UITapGestureRecognizer){
// check what nodes are tapped
var p = sender.locationInView(sceneView)
var hitResults = sceneView.hitTest(p, options: nil)
if hitResults.count > 0
{
var hitnode = (hitResults.first)!.node
print("\nName of node hit is \(hitnode.name)")
//var indexvalue = hitResults.first?.faceIndex
//print(indexvalue)
}
}
How can I find exactly which face of the cube is being hit?
faceIndex
looks promising, but will not actually get something you're likely to consider useful. The "faces" counted by that property are the tessellation of the mesh, so a cube won't be a collection of six quads, it'll be twelve triangles. (Or more: in some cases, even a flat-sided cube will be tessellated with more than one quad / two triangles per side. If you're using SCNBox
you control these with widthSegmentCount
etc.)
Instead — especially if your cube is an SCNBox
— the easiest solution might be to leverage this interesting behavior of that class:
You can assign up to six
SCNMaterial
instances to a box—one for each side—with itsmaterials
property. TheSCNBox
class automatically createsSCNGeometryElement
objects as needed to handle the number of materials.
So, if you assign six materials, you'll get one for each side:
let front = SCNMaterial()
let right = SCNMaterial()
let back = SCNMaterial()
let left = SCNMaterial()
let top = SCNMaterial()
let bottom = SCNMaterial()
cube.materials = [ front, right, back, left, top, bottom ]
And in so doing, your SCNBox
will have six geometry elements — one for each material, which corresponds to one for each side.
Now, you can use hit testing to find out which geometry element was clicked:
if let result = hitResults.first {
let node = result.node
// Find the material for the clicked element
// (Indices match between the geometryElements and materials arrays)
let material = node.geometry!.materials[result.geometryIndex]
// Do something with that material, for example:
let highlight = CABasicAnimation(keyPath: "diffuse.contents")
highlight.toValue = NSColor.redColor()
highlight.duration = 1.0
highlight.autoreverses = true
highlight.removedOnCompletion = true
material.addAnimation(highlight, forKey: nil)
}
Or if you're not highlighting and want to use the face index for logic, here's the beginning of something you could use for that:
enum CubeFace: Int {
case Front, Right, Back, Left, Top, Bottom
}
// when processing hit test result:
print("hit face: \(CubeFace(rawValue: result.geometryIndex))")