在 XZ 平面上拖动对象
Drag object on XZ plane
我正在开发增强现实应用程序,我希望能够在 space 中拖动对象。我在 SO 中找到的解决方案的问题是,建议使用 projectPoint
/unprojectPoint
的解决方案是它们沿着 XY 平面产生运动。
我试图使用手指在屏幕上的移动作为节点 x 和 z 坐标的偏移量。问题是有很多东西需要考虑(相机的位置、节点的位置、节点的旋转等等)
有更简单的方法吗?
如果我没理解错的话,我是使用添加到 ARSCNView 的 UIPanGestureRecognizer 来执行此操作的。
在我的例子中,我想检查是否在给定的虚拟对象上启动了平移并跟踪它是哪个,因为我可以有多个,但如果你只有一个对象,你可能不需要 targetNode 变量。
我用来除法的 700 常量是通过反复试验得到的,以使翻译更流畅,您可能需要根据您的情况进行更改。
向上移动手指,使物体远离相机,向下移动手指,使物体离相机更近。手指的水平移动移动对象 left/right.
@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
}
}
首先,您需要在原点下方几米(我有 10 米)处创建地板或非常大的平面。这可确保您的命中率始终为 returns 值。然后使用平移手势:
//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
}
}
在我的例子中只有一个节点,所以我没有检查是否录制了所需的节点。如果你有很多节点,你可以随时检查它。
我已经更新了 @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
}
}
我正在开发增强现实应用程序,我希望能够在 space 中拖动对象。我在 SO 中找到的解决方案的问题是,建议使用 projectPoint
/unprojectPoint
的解决方案是它们沿着 XY 平面产生运动。
我试图使用手指在屏幕上的移动作为节点 x 和 z 坐标的偏移量。问题是有很多东西需要考虑(相机的位置、节点的位置、节点的旋转等等)
有更简单的方法吗?
如果我没理解错的话,我是使用添加到 ARSCNView 的 UIPanGestureRecognizer 来执行此操作的。
在我的例子中,我想检查是否在给定的虚拟对象上启动了平移并跟踪它是哪个,因为我可以有多个,但如果你只有一个对象,你可能不需要 targetNode 变量。 我用来除法的 700 常量是通过反复试验得到的,以使翻译更流畅,您可能需要根据您的情况进行更改。
向上移动手指,使物体远离相机,向下移动手指,使物体离相机更近。手指的水平移动移动对象 left/right.
@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
}
}
首先,您需要在原点下方几米(我有 10 米)处创建地板或非常大的平面。这可确保您的命中率始终为 returns 值。然后使用平移手势:
//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
}
}
在我的例子中只有一个节点,所以我没有检查是否录制了所需的节点。如果你有很多节点,你可以随时检查它。
我已经更新了 @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
}
}