【发布时间】:2018-06-20 23:23:55
【问题描述】:
我正在开发一个增强现实应用程序,我希望能够在空间中拖动一个对象。我在 SO 中找到的解决方案的问题是,建议使用 projectPoint/unprojectPoint 的解决方案是它们沿 XY 平面产生运动。
我试图使用屏幕上的手指移动作为节点的 x 和 z 坐标的偏移量。问题是要考虑很多东西(相机的位置,节点的位置,节点的旋转等)。
有没有更简单的方法?
【问题讨论】:
我正在开发一个增强现实应用程序,我希望能够在空间中拖动一个对象。我在 SO 中找到的解决方案的问题是,建议使用 projectPoint/unprojectPoint 的解决方案是它们沿 XY 平面产生运动。
我试图使用屏幕上的手指移动作为节点的 x 和 z 坐标的偏移量。问题是要考虑很多东西(相机的位置,节点的位置,节点的旋转等)。
有没有更简单的方法?
【问题讨论】:
我已经更新了 @Alok 答案,因为在我的情况下,它仅从上述解决方案在 x 平面中拖动。所以我添加了 y 坐标,为我工作。
var PCoordx: Float = 0.0
var PCoordy: Float = 0.0
var PCoordz: Float = 0.0
@objc func handleDragGesture(_ sender: UIPanGestureRecognizer) {
switch sender.state {
case .began:
let hitNode = self.sceneView.hitTest(sender.location(in: self.sceneView),
options: nil)
self.PCoordx = (hitNode.first?.worldCoordinates.x)!
self.PCoordy = (hitNode.first?.worldCoordinates.y)!
self.PCoordz = (hitNode.first?.worldCoordinates.z)!
case .changed:
// when you start to pan in screen with your finger
// hittest gives new coordinates of touched location in sceneView
// coord-pcoord gives distance to move or distance paned in sceneview
let hitNode = sceneView.hitTest(sender.location(in: sceneView), options: nil)
if let coordx = hitNode.first?.worldCoordinates.x,
let coordy = hitNode.first?.worldCoordinates.y,
let coordz = hitNode.first?.worldCoordinates.z {
let action = SCNAction.moveBy(x: CGFloat(coordx - PCoordx),
y: CGFloat(coordy - PCoordy),
z: CGFloat(coordz - PCoordz),
duration: 0.0)
self.photoNode.runAction(action)
self.PCoordx = coordx
self.PCoordy = coordy
self.PCoordz = coordz
}
sender.setTranslation(CGPoint.zero, in: self.sceneView)
case .ended:
self.PCoordx = 0.0
self.PCoordy = 0.0
self.PCoordz = 0.0
default:
break
}
}
【讨论】:
首先,您需要在原点以下几米(我有 10 米)创建地板或非常大的平面。这可以确保你的 hittest 总是返回值。然后使用平移手势:
//store previous coordinates from hittest to compare with current ones
var PCoordx: Float = 0.0
var PCoordz: Float = 0.0
@objc func move(_ gestureRecognizer: UIPanGestureRecognizer){
if gestureRecognizer.state == .began{
let hitNode = sceneView.hitTest(gestureRecognizer.location(in: sceneView), options: nil)
PCoordx = (hitNode.first?.worldCoordinates.x)!
PCoordz = (hitNode.first?.worldCoordinates.z)!
}
// when you start to pan in screen with your finger
// hittest gives new coordinates of touched location in sceneView
// coord-pcoord gives distance to move or distance paned in sceneview
if gestureRecognizer.state == .changed {
let hitNode = sceneView.hitTest(gestureRecognizer.location(in: sceneView), options: nil)
if let coordx = hitNode.first?.worldCoordinates.x{
if let coordz = hitNode.first?.worldCoordinates.z{
let action = SCNAction.moveBy(x: CGFloat(coordx-PCoordx), y: 0, z: CGFloat(coordz-PCoordz), duration: 0.1)
node.runAction(action)
PCoordx = coordx
PCoordz = coordz
}
}
gestureRecognizer.setTranslation(CGPoint.zero, in: sceneView)
}
if gestureRecognizer.state == .ended{
PCoordx = 0
PCoordz = 0
}
}
在我的情况下,只有一个节点,所以我没有检查所需的节点是否被录音。如果您有很多节点,您可以随时检查它。
【讨论】:
如果我理解正确,我会使用添加到 ARSCNView 的 UIPanGestureRecognizer 来执行此操作。
在我的情况下,我想检查是否在给定的虚拟对象上启动了平移并跟踪它是因为我可以有多个,但如果你只有一个对象,你可能不需要 targetNode em> 变量。 我用来划分的 700 常数是我通过反复试验得到的,以使翻译更流畅,您可能需要根据您的情况进行更改。
向上移动手指,使物体远离相机,向下移动,使其更靠近。手指的水平移动使对象向左/向右移动。
@objc func onTranslate(_ sender: UIPanGestureRecognizer) {
let position = sender.location(in: scnView)
let state = sender.state
if (state == .failed || state == .cancelled) {
return
}
if (state == .began) {
// Check pan began on a virtual object
if let objectNode = virtualObject(at: position).node {
targetNode = objectNode
latestTranslatePos = position
}
}
else if let _ = targetNode {
// Translate virtual object
let deltaX = Float(position.x - latestTranslatePos!.x)/700
let deltaY = Float(position.y - latestTranslatePos!.y)/700
targetNode!.localTranslate(by: SCNVector3Make(deltaX, 0.0, deltaY))
latestTranslatePos = position
if (state == .ended) {
targetNode = nil
}
}
【讨论】: