假设SceneKit场景提供类似于Minecraft的透视图 . 有一台相机 . 摄像机节点的行为类似于人的“头部”,而其父节点的行为类似于“身体” . 摄像机节点是父节点的子节点,称为“userNode” .
目标:
-
让用户用一根手指平移以旋转相机 . 换句话说,相机位置保持固定,但“头部”旋转并让用户观看场景,就好像在一个圆圈中转身一样 .
-
让用户用两根手指平移以围绕给定点绕相机运行 . 换句话说,相机位置和旋转现在改变,以便让用户从不同角度观看相同的点 .
到目前为止,下面的代码实现了单指平移行为 . 问题是如何结合双指泛行为 .
选项1:按照this answer,解释如何绕摄像机旋转 . 然而,这似乎不是最理想的,因为场景中已经存在摄像机节点并且需要额外的节点,同时还在不同摄像机之间移动场景的视点 .
选项2:计算摄像机的新位置和旋转,这样可以避免添加另一个摄像机节点和另一个轨道节点 . 这感觉就像是更好的答案,但可能更复杂 .
func sceneViewPannedTwoFingers(sender: UIPanGestureRecognizer) {
// Get pan distance & convert to radians
let translation = sender.translationInView(sender.view!)
var xRadians = GLKMathDegreesToRadians(Float(translation.x))
// Get X radians only since only moving horizontally
xRadians = (xRadians / 10) + curXRadians
// Rotate & move camera horizontally
userNode.rotation = SCNVector4(x: 0, y: 1, z: 0, w: -xRadians)
// Compute new position for camera?
}
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 / 10) + curXRadians
yRadians = (yRadians / 10) + 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()
}
问题:
1)在SceneKit中有更简单的方法来实现这一点吗?
2)如果没有,两个选项是否只有两个选项,如果是,那么哪个选择更好?
3)对于选项2,更新 userNode
或"body"的位置似乎很简单:只计算给定中心原点的圆的新位置,即轨道点 . 对于"head",除了更新 cameraNode
(或"head")的 rotation
属性之外还有什么需要注意的吗?似乎将摄像机定向为始终面向轨道点可能更复杂,因为有一部分SceneKit API专用于它,例如 SCNLookAtConstraint.