识别立方体的面接触开始于 Swift - SceneKit
Identify face of a cube hit on touches began in Swift - SceneKit
我打算用 SceneKit 创建一个应用来解魔方。我为立方体制作了自己的 dae
文件。触摸开始后,我得到了被击中的对象
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)
}
}
我怎样才能准确找到立方体的哪个面被击中?
faceIndex
看起来很有前途,但实际上不会得到您可能认为有用的东西。 属性 算出的 "faces" 是网格的细分,所以一个立方体不会是六个四边形的集合,而是十二个三角形。 (或者更多:在某些情况下,即使是一个平面立方体,每边也会镶嵌有多个四边形/两个三角形。如果您使用 SCNBox
,您可以使用 widthSegmentCount
等来控制这些。)
相反——特别是如果你的立方体是 SCNBox
——最简单的解决方案可能是利用 class:
的这种有趣行为
You can assign up to six SCNMaterial
instances to a box—one for each side—with its materials
property. The SCNBox
class automatically creates SCNGeometryElement
objects as needed to handle the number of materials.
因此,如果您分配六个 material,则每一侧都会得到一个:
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 ]
这样一来,您的 SCNBox
将有六个几何元素 - 每个 material 一个,对应每一边一个。
现在,您可以使用命中测试找出单击了哪个几何元素:
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)
}
或者,如果您没有突出显示并希望将面部索引用于逻辑,那么您可以使用以下内容的开头:
enum CubeFace: Int {
case Front, Right, Back, Left, Top, Bottom
}
// when processing hit test result:
print("hit face: \(CubeFace(rawValue: result.geometryIndex))")
我们使用此函数根据 SCNHitResult
的 localNormal 值确定面部命中。
如果轴的大小为 1,此函数会得出已击中面的结论。
它假定只有一个轴的大小等于 1。否则代码将中断。它还假定 SCNBox 几何体。
在测试中,这似乎有效(对于 SCNBox 几何图形)。唯一的问题是 localNormal
值并不总是 return 干净的 0 值。有时它 return 的值类似于 -5.96046448e-08,因此我们使用 round
函数来确保安全,以防同样适用于接近 1 但不完全是 1 的值。
一般来说,我们是 SceneKit 和 3D 的新手,因此代码可能存在缺陷。如果您发现问题或潜在的优化,请发表评论。
private func getHitFaceFromNormal(normal: SCNVector3) {
if round(normal.x) == -1 {
// Left face hit
} else if round(normal.x) == 1 {
// Right face hit
} else if round(normal.y) == -1 {
// Bottom face hit
} else if round(normal.y) == 1 {
// Top face hit
} else if round(normal.z) == -1 {
// Back face hit
} else if round(normal.z) == 1 {
// Front face hit
} else {
// Error, no face detected
}
}
我打算用 SceneKit 创建一个应用来解魔方。我为立方体制作了自己的 dae
文件。触摸开始后,我得到了被击中的对象
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)
}
}
我怎样才能准确找到立方体的哪个面被击中?
faceIndex
看起来很有前途,但实际上不会得到您可能认为有用的东西。 属性 算出的 "faces" 是网格的细分,所以一个立方体不会是六个四边形的集合,而是十二个三角形。 (或者更多:在某些情况下,即使是一个平面立方体,每边也会镶嵌有多个四边形/两个三角形。如果您使用 SCNBox
,您可以使用 widthSegmentCount
等来控制这些。)
相反——特别是如果你的立方体是 SCNBox
——最简单的解决方案可能是利用 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.
因此,如果您分配六个 material,则每一侧都会得到一个:
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 ]
这样一来,您的 SCNBox
将有六个几何元素 - 每个 material 一个,对应每一边一个。
现在,您可以使用命中测试找出单击了哪个几何元素:
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)
}
或者,如果您没有突出显示并希望将面部索引用于逻辑,那么您可以使用以下内容的开头:
enum CubeFace: Int {
case Front, Right, Back, Left, Top, Bottom
}
// when processing hit test result:
print("hit face: \(CubeFace(rawValue: result.geometryIndex))")
我们使用此函数根据 SCNHitResult
的 localNormal 值确定面部命中。
如果轴的大小为 1,此函数会得出已击中面的结论。
它假定只有一个轴的大小等于 1。否则代码将中断。它还假定 SCNBox 几何体。
在测试中,这似乎有效(对于 SCNBox 几何图形)。唯一的问题是 localNormal
值并不总是 return 干净的 0 值。有时它 return 的值类似于 -5.96046448e-08,因此我们使用 round
函数来确保安全,以防同样适用于接近 1 但不完全是 1 的值。
一般来说,我们是 SceneKit 和 3D 的新手,因此代码可能存在缺陷。如果您发现问题或潜在的优化,请发表评论。
private func getHitFaceFromNormal(normal: SCNVector3) {
if round(normal.x) == -1 {
// Left face hit
} else if round(normal.x) == 1 {
// Right face hit
} else if round(normal.y) == -1 {
// Bottom face hit
} else if round(normal.y) == 1 {
// Top face hit
} else if round(normal.z) == -1 {
// Back face hit
} else if round(normal.z) == 1 {
// Front face hit
} else {
// Error, no face detected
}
}