对于体素艺术应用程序,目标是让用户在SceneKit场景中移动和旋转相机,然后点击以放置块 .

下面的代码允许用户通过平移来旋转相机 . 手势结束后,我们移动现有的块,使其在相机的Z轴上为-X单位(即相机前面的-X单位) .

cameraNode 是场景的观点,是 userNode 的孩子 . 当用户移动操纵杆时,我们更新 userNode. 的位置

Question: 其他SO帖子通过应用变换而不是更改旋转和位置属性来操纵相机节点 . 一种方法比另一种更好吗?

func sceneViewPannedOneFinger(sender: UIPanGestureRecognizer) {
    // Get pan distance & convert to radians
    let translation = sender.translationInView(sender.view!)
    var xRadians = GLKMathDegreesToRadians(Float(translation.x))
    var yRadians = GLKMathDegreesToRadians(Float(translation.y))

    // Get x & y radians
    xRadians = (xRadians / 6) + curXRadians
    yRadians = (yRadians / 6) + curYRadians

    // Limit yRadians to prevent rotating 360 degrees vertically
    yRadians = max(Float(-M_PI_2), min(Float(M_PI_2), yRadians))

    // Set rotation values to avoid Gimbal Lock
    cameraNode.rotation = SCNVector4(x: 1, y: 0, z: 0, w: yRadians)
    userNode.rotation = SCNVector4(x: 0, y: 1, z: 0, w: xRadians)

    // Save value for next rotation
    if sender.state == UIGestureRecognizerState.Ended {
        curXRadians = xRadians
        curYRadians = yRadians
    }

    // Set preview block
    setPreviewBlock()
}

private func setPreviewBlock(var futurePosition: SCNVector3 = SCNVector3Zero, reach: Float = 8) -> SCNVector3 {
    // Get future position
    if SCNVector3EqualToVector3(futurePosition, SCNVector3Zero) {
        futurePosition = userNode.position
    }

    // Get current position after accounting for rotations
    let hAngle = Float(cameraNode.rotation.w * cameraNode.rotation.x)
    let vAngle = Float(userNode.rotation.w * userNode.rotation.y)
    var position = getSphericalCoords(hAngle, t: vAngle, r: reach)
    position += userNode.position

    // Snap position to grid
    position = position.rounded()

    // Ensure preview block never dips below floor
    position.y = max(0, position.y)

    // Return if snapped position hasn't changed
    if SCNVector3EqualToVector3(position, previewBlock.position) {
        return position
    }

    // If here, animate preview block to new position
    SCNTransaction.begin()
    SCNTransaction.setAnimationDuration(AnimationTime)
    previewBlock.position = position
    SCNTransaction.commit()

    // Return position
    return position
}

func getSphericalCoords(s: Float, t: Float, r: Float) -> SCNVector3 {
    return SCNVector3(-(cos(s) * sin(t) * r),
                      sin(s) * r,
                      -(cos(s) * cos(t) * r))
}