ARkit如何旋转一个物体以面对另一个物体

ARkit how to rotate an object to face another object

在开发 AR 游戏应用程序时,我 运行 遇到了一个(实际上是两个,但如果我想出解决这个问题的方法,那么还有另一个预期的问题)问题是我的 3D 对象不面对一个目标对象。第二个预期问题是这些物体是否有可能一直注视着其他移动的物体?

假设对于第一个问题,Xcode AR 的默认项目带有 spaceship 节点,它在 space 中浮动,当我添加一个object called like B,那么我想让spaceship的船头随时指向B对象。

第二个问题,如何监控ARSCNView中所有运动物体的位置,并通过跟踪运动物体的运动使节点面向运动物体?就像 As 正在移动,B 一直在跟踪 As 的位置,并在应用程序中始终将自己指向 As。

你能给我一些提示或解决方案来解决这个问题吗?

(1)。您可以使用 billboardConstraint 始终使用其自由轴将焦点对准相机视图,例如

    let billboardConstraint = SCNBillboardConstraint()
    billboardConstraint.freeAxes = SCNBillboardAxis.Y
    someNode.constraints = [billboardConstraint]

这个 someNode 总是聚焦在相机上,因为我们让 Y 轴可以自由旋转,而其他 2 轴相对于相机是固定的。

(2)。如何实现:2个节点将相互关注: 对于这种情况,我们可以使用 SCNLookAtConstraint。

赞:

    let lookAtConstraints = SCNLookAtConstraint(target: node3)
    node2.constraints = [lookAtConstraints] 

所以 node2 在整个会话期间始终查看 node3。

为了理解,可以参考下面一段代码:

 // Create different geometry nodes
 enum GeometryType {
     case Box
     case Pyramid
     case Capsule
     case Cone
    case Cylinder
  }

  func getGeometry(type: GeometryType) -> SCNGeometry {
    switch type {
    case .Box:          return SCNBox(width: 0.2, height: 0.2, length: 0.2, chamferRadius: 0.05)
    case .Pyramid:      return SCNPyramid(width: 0.2, height: 0.2, length: 0.2)
    case .Capsule:      return SCNCapsule(capRadius: 0.2, height: 0.1)
    case .Cone:         return SCNCone(topRadius: 0.0, bottomRadius: 0.2, height: 0.4)
    case .Cylinder:     return SCNCylinder(radius: 0.05, height: 0.2)
    }
}

// Let's do the experiment:

func threeNodesExperiment() {

    // always focus on camera view
    let geometry1 : SCNGeometry! = getGeometry(type: .Pyramid)
    geometry1.firstMaterial?.diffuse.contents = UIColor.red
    let node1 = SCNNode(geometry: geometry1)
    node1.position = SCNVector3Make(0.4, 0, -0.5)

    let billboardConstraint = SCNBillboardConstraint()
    billboardConstraint.freeAxes = SCNBillboardAxis.Y
    node1.constraints = [billboardConstraint]
    rootNode.addChildNode(node1)

    // two nodes focusing each other
    let geometry2 = getGeometry(type: .Cylinder)
    geometry2.firstMaterial?.diffuse.contents = UIColor.green
    let node2 = SCNNode(geometry: geometry2)
    node2.position = SCNVector3Make(-0.1, 0, -0.5)

    let geometry3 = getGeometry(type: .Box)
    geometry3.firstMaterial?.diffuse.contents = UIColor.blue
    let node3 = SCNNode(geometry: geometry3)
    node3.position = SCNVector3Make(0.2, 0, -0.5)

    let lookAtConstraints = SCNLookAtConstraint(target: node3)
    node2.constraints = [lookAtConstraints]

    [node2, node3].forEach{ rootNode.addChildNode([=12=]) }
}

输出:

node1(红色节点)始终聚焦于相机视图。 (改变相机位置并做实验)。

node2(绿色节点)和node3(蓝色节点)一直在看着对方。

对 3-D 节点使用一些 theta 值的自定义旋转:

(1)。使用 CAAnimation:

// (1). CAAnimation
private func rotateNode(node : SCNNode, theta : Double, with animation : Bool = false) {
    if animation {
        let rotation = CABasicAnimation(keyPath: "rotation")
        rotation.fromValue = SCNVector4Make(0,1,0,0) // along x-z plane
        rotation.toValue = SCNVector4Make(0,1,0,Float(theta))
        rotation.duration = 3.0
        rotation.repeatCount = Float.infinity
        node.addAnimation(rotation, forKey: "Rotate it")
    }
    node.rotation = SCNVector4Make(0, 1, 0, Float(theta))  // along x-z plane
    print("rotating node with angel :\(theta)")
}

(2)。 SCN事务

// (2). SCNTransaction
private func rotateUsingTransaction(node : SCNNode, theta : Double) {
    SCNTransaction.begin()
    SCNTransaction.animationDuration = 5.0
    node.rotation = SCNVector4Make(0, 1, 0, Float(theta))
    SCNTransaction.completionBlock = {
        print("Transaction completed")
    }
    SCNTransaction.commit()
}

(3)。 SCNAction

// (3). SCNAction
private func moveUpDown(node : SCNNode) {
    let moveUp = SCNAction.moveBy(x: 0, y: 1, z: 0, duration: 1)
    moveUp.timingMode = .easeInEaseOut
    let moveDown = SCNAction.moveBy(x: 0, y: -1, z: 0, duration: 1)
    moveDown.timingMode = .easeInEaseOut
    let moveSequence = SCNAction.sequence([moveUp,moveDown])
    let moveLoop = SCNAction.repeatForever(moveSequence)
    node.runAction(moveLoop)
 }