【发布时间】:2018-07-06 08:37:28
【问题描述】:
我正在构建一个UIPanGestureRecognizer,这样我就可以在 3D 空间中移动节点。
目前,我有一些工作,但只有当相机完全垂直于平面时,我的UIPanGestureRecognizer 看起来像这样:
@objc func handlePan(_ sender:UIPanGestureRecognizer) {
let projectedOrigin = self.sceneView!.projectPoint(SCNVector3Zero)
let viewCenter = CGPoint(
x: self.view!.bounds.midX,
y: self.view!.bounds.midY
)
let touchlocation = sender.translation(in: self.view!)
let moveLoc = CGPoint(
x: CGFloat(touchlocation.x + viewCenter.x),
y: CGFloat(touchlocation.y + viewCenter.y)
)
let touchVector = SCNVector3(x: Float(moveLoc.x), y: Float(moveLoc.y), z: Float(projectedOrigin.z))
let worldPoint = self.sceneView!.unprojectPoint(touchVector)
let loc = SCNVector3( x: worldPoint.x, y: 0, z: worldPoint.z )
worldHandle?.position = loc
}
当相机旋转时会出现问题,并且坐标会受到透视变化的影响。这是你可以看到的触摸位置漂移:
我曾经担任此职位的相关 SO 帖子: How to use iOS (Swift) SceneKit SCNSceneRenderer unprojectPoint properly
它引用了这些很棒的幻灯片:http://www.terathon.com/gdc07_lengyel.pdf
【问题讨论】:
-
如果网格是一个平面(否则你可以添加一个平面并使其不可见)你可以简单地在平面节点上做一个hittest来获得平面上的坐标。适用于轻击手势,但也非常准确地适用于平移手势(例如,平移开始时的 hittest 和每次手势状态更改为活动时再次 hittest)。这将排除相机因素以及平面的方向。您知道瓦片的数量及其宽度/高度,因此您只需将坐标除以瓦片的宽度即可得到列,与行类似。
-
这可行,但是平面上 SCNVector3 的坐标会受到透视失真的影响,并且在错误的位置检测到命中,但是当平面垂直时可以完美运行。
-
不,无论相机设置如何,飞机上(命中结果的)localCoordinates 将始终保持一致。然后,您可以将结果位置转换为世界空间,以获取要放置在该位置上的节点的位置。
-
也许我的问题在于翻译位置,我已经用触摸位置漂移的屏幕记录编辑了我的问题。
-
谢谢,这让事情变得更清楚了 :) 正如 Rickster 在帖子中提到的那样,您提到的这种方法只有在平面垂直于相机时才有效。这就是为什么我建议使用 hittest,因为它使相机和平面方向脱离了等式。我将很快发布带有示例代码的答案。