我正在构建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
答案 0 :(得分:2)
从2D触摸位置到3D空间的棘手部分显然是z坐标。不是尝试将触摸位置转换为假想的3D空间,而是使用hittest将2D触摸映射到该3D空间中的2D平面。特别是当仅在两个方向上需要移动时,例如像棋盘上的棋子一样,这种方法非常有效。无论平面的方向和相机设置如何(只要相机没有明显地从侧面看平面),这将触摸位置直接映射到触摸手指下方的3D位置,并按照一致。
我用Xcode修改了游戏模板。 https://github.com/Xartec/PrecisePan/
主要部分是:
平移手势代码:
// retrieve the SCNView
let scnView = self.view as! SCNView
// check what nodes are tapped
let p = gestureRecognize.location(in: scnView)
let hitResults = scnView.hitTest(p, options: [SCNHitTestOption.searchMode: 1, SCNHitTestOption.ignoreHiddenNodes: false])
if hitResults.count > 0 {
// check if the XZPlane is in the hitresults
for result in hitResults {
if result.node.name == "XZPlane" {
//NSLog("Local Coordinates on XZPlane %f, %f, %f", result.localCoordinates.x, result.localCoordinates.y, result.localCoordinates.z)
//NSLog("World Coordinates on XZPlane %f, %f, %f", result.worldCoordinates.x, result.worldCoordinates.y, result.worldCoordinates.z)
ship.position = result.worldCoordinates
ship.position.y += 1.5
return;
}
}
}
在viewDidload:
中添加XZ平面节点let XZPlaneGeo = SCNPlane(width: 100, height: 100)
let XZPlaneNode = SCNNode(geometry: XZPlaneGeo)
XZPlaneNode.geometry?.firstMaterial?.diffuse.contents = UIImage(named: "grid")
XZPlaneNode.name = "XZPlane"
XZPlaneNode.rotation = SCNVector4(-1, 0, 0, Float.pi / 2)
//XZPlaneNode.isHidden = true
scene.rootNode.addChildNode(XZPlaneNode)
取消注释isHidden行以隐藏辅助平面,它仍然可以工作。飞机显然需要足够大以填满屏幕或至少允许用户平移的部分。
通过设置全局变量来保存平移的startWorldPosition(处于状态.began)并将其与状态.change中的命中worldPosition进行比较,您可以确定世界空间中的增量/平移并相应地转换其他对象。 / p>