SceneKit:使用平移手势将相机朝其面向的方向移动
SceneKit: move camera towards direction its facing with pan gesture
我在我的 SceneKit 游戏中设置了一些自定义相机控件。我在基于相机 y 欧拉角的平移手势自动适应方面遇到问题。我的平移手势通过在 x 和 z 轴上平移相机(通过使用手势转换)来工作。问题是,尽管相机旋转,相机将继续在 x 和 z 轴上平移。我想要它,以便相机在其面向的轴上平移。
这是我用来 pan/rotate:
的手势
平移:
var previousTranslation = SCNVector3(x: 0.0,y: 15,z: 0.0)
var lastWidthRatio:Float = 0
var angle:Float = 0
@objc func pan(gesture: UIPanGestureRecognizer) {
gesture.minimumNumberOfTouches = 1
gesture.maximumNumberOfTouches = 1
if gesture.numberOfTouches == 1 {
let view = self.view as! SCNView
let node = view.scene!.rootNode.childNode(withName: "Node", recursively: false)
let secondNode = view.scene!.rootNode.childNode(withName: "CameraHandler", recursively: false)
let translation = gesture.translation(in: view)
let constant: Float = 30.0
angle = secondNode!.eulerAngles.y
//these were the previous values I was using to handle panning, they worked but provided really jittery movement. You can change the direction they rotate by multiplying the sine/cosine .pi values by any integer.
//var translateX = Float(translation.y) * sin(.pi) / cos(.pi) - Float(translation.x) * cos(.pi)
//var translateY = Float(translation.y) * cos(.pi) / cos(.pi) + Float(translation.x) * sin(.pi)
//these ones work a lot smoother
var translateX = Float(translation.x) * Float(Double.pi / 180)
var translateY = Float(translation.y) * Float(Double.pi / 180)
translateX = translateX * constant
translateY = translateY * constant
switch gesture.state {
case .began:
previousTranslation = node!.position
break;
case .changed:
node!.position = SCNVector3Make((previousTranslation.x + translateX), previousTranslation.y, (previousTranslation.z + translateY))
break
default: break
}
}
}
旋转:
@objc func rotate(gesture: UIPanGestureRecognizer) {
gesture.minimumNumberOfTouches = 2
gesture.maximumNumberOfTouches = 2
if gesture.numberOfTouches == 2 {
let view = self.view as! SCNView
let node = view.scene!.rootNode.childNode(withName: "CameraHandler", recursively: false)
let translate = gesture.translation(in: view)
var widthRatio:Float = 0
widthRatio = Float(translate.x / 10) * Float(Double.pi / 180)
switch gesture.state {
case .began:
lastWidthRatio = node!.eulerAngles.y
break
case .changed:
node!.eulerAngles.y = lastWidthRatio + widthRatio
print(node!.eulerAngles.y)
break
default: break
}
}
}
CameraHandler
节点是Camera
节点的父节点。一切正常,只是没有像我想要的那样工作。希望这对你们来说已经足够清楚了。
在Objective C。关键部分是最后三行,特别是最后一行中矩阵的乘法顺序(导致移动发生在局部space)。如果 transmat 和 cammat 切换,它会像现在一样再次运行(在世界中移动 space)。重构部分只适用于我的特定情况,在这种情况下,透视和正交相机都是可能的。
-(void)panCamera :(CGPoint)location {
CGFloat dx = _prevlocation.x - location.x;
CGFloat dy = location.y - _prevlocation.y;
_prevlocation = location;
//refactor dx and dy based on camera distance or orthoscale
if (cameraNode.camera.usesOrthographicProjection) {
dx = dx / 416 * cameraNode.camera.orthographicScale;
dy = dy / 416 * cameraNode.camera.orthographicScale;
} else {
dx = dx / 720 * cameraNode.position.z;
dy = dy / 720 * cameraNode.position.z;
}
SCNMatrix4 cammat = self.cameraNode.transform;
SCNMatrix4 transmat = SCNMatrix4MakeTranslation(dx, 0, dy);
self.cameraNode.transform = SCNMatrix4Mult(transmat, cammat);
}
根据 Xartec 的回答,我明白了。我将其翻译成 swift 和 retro-fitted 来满足我的需要。我对它不是很满意,因为运动不流畅。今天晚些时候我会努力把它弄平。
平移手势:此手势沿相机旋转的方向在场景周围平移相机父节点。它完全符合我的要求。
@objc func pan(gesture: UIPanGestureRecognizer) {
gesture.minimumNumberOfTouches = 1
gesture.maximumNumberOfTouches = 1
if gesture.numberOfTouches == 1 {
let view = self.view as! SCNView
let node = view.scene!.rootNode.childNode(withName: "CameraHandler", recursively: false)
let translation = gesture.translation(in: view)
var dx = previousTranslation.x - translation.x
var dy = previousTranslation.y - translation.y
dx = dx / 100
dy = dy / 100
print(dx,dy)
let cammat = node!.transform
let transmat = SCNMatrix4MakeTranslation(Float(dx), 0, Float(dy))
switch gesture.state {
case .began:
previousTranslation = translation
break;
case .changed:
node!.transform = SCNMatrix4Mult(transmat, cammat)
break
default: break
}
}
}
旋转手势:此手势用两根手指旋转相机。
@objc func rotate(gesture: UIPanGestureRecognizer) {
gesture.minimumNumberOfTouches = 2
gesture.maximumNumberOfTouches = 2
if gesture.numberOfTouches == 2 {
let view = self.view as! SCNView
let node = view.scene!.rootNode.childNode(withName: "CameraHandler", recursively: false)
let translate = gesture.translation(in: view)
var widthRatio:Float = 0
widthRatio = Float(translate.x / 10) * Float(Double.pi / 180)
switch gesture.state {
case .began:
lastWidthRatio = node!.eulerAngles.y
break
case .changed:
node!.eulerAngles.y = lastWidthRatio + widthRatio
break
default: break
}
}
}
要获得与我相同的功能,您需要将 cameraNode
附加到父节点。像这样:
//create and add a camera to the scene
let cameraNode = SCNNode()
//cameraHandler is declared outside viewDidLoad.
let cameraHandler = SCNNode()
cameraNode.camera = SCNCamera()
cameraNode.name = "Camera"
cameraNode.position = SCNVector3(x: 0.0,y: 10.0,z: 20.0)
//This euler angle only rotates the camera downward a little bit. It is not neccessary
cameraNode.eulerAngles = SCNVector3(x: -0.6, y: 0, z: 0)
cameraHandler.addChildNode(cameraNode)
cameraHandler.name = "CameraHandler"
scene.rootNode.addChildNode(cameraHandler)
我在我的 SceneKit 游戏中设置了一些自定义相机控件。我在基于相机 y 欧拉角的平移手势自动适应方面遇到问题。我的平移手势通过在 x 和 z 轴上平移相机(通过使用手势转换)来工作。问题是,尽管相机旋转,相机将继续在 x 和 z 轴上平移。我想要它,以便相机在其面向的轴上平移。
这是我用来 pan/rotate:
的手势平移:
var previousTranslation = SCNVector3(x: 0.0,y: 15,z: 0.0)
var lastWidthRatio:Float = 0
var angle:Float = 0
@objc func pan(gesture: UIPanGestureRecognizer) {
gesture.minimumNumberOfTouches = 1
gesture.maximumNumberOfTouches = 1
if gesture.numberOfTouches == 1 {
let view = self.view as! SCNView
let node = view.scene!.rootNode.childNode(withName: "Node", recursively: false)
let secondNode = view.scene!.rootNode.childNode(withName: "CameraHandler", recursively: false)
let translation = gesture.translation(in: view)
let constant: Float = 30.0
angle = secondNode!.eulerAngles.y
//these were the previous values I was using to handle panning, they worked but provided really jittery movement. You can change the direction they rotate by multiplying the sine/cosine .pi values by any integer.
//var translateX = Float(translation.y) * sin(.pi) / cos(.pi) - Float(translation.x) * cos(.pi)
//var translateY = Float(translation.y) * cos(.pi) / cos(.pi) + Float(translation.x) * sin(.pi)
//these ones work a lot smoother
var translateX = Float(translation.x) * Float(Double.pi / 180)
var translateY = Float(translation.y) * Float(Double.pi / 180)
translateX = translateX * constant
translateY = translateY * constant
switch gesture.state {
case .began:
previousTranslation = node!.position
break;
case .changed:
node!.position = SCNVector3Make((previousTranslation.x + translateX), previousTranslation.y, (previousTranslation.z + translateY))
break
default: break
}
}
}
旋转:
@objc func rotate(gesture: UIPanGestureRecognizer) {
gesture.minimumNumberOfTouches = 2
gesture.maximumNumberOfTouches = 2
if gesture.numberOfTouches == 2 {
let view = self.view as! SCNView
let node = view.scene!.rootNode.childNode(withName: "CameraHandler", recursively: false)
let translate = gesture.translation(in: view)
var widthRatio:Float = 0
widthRatio = Float(translate.x / 10) * Float(Double.pi / 180)
switch gesture.state {
case .began:
lastWidthRatio = node!.eulerAngles.y
break
case .changed:
node!.eulerAngles.y = lastWidthRatio + widthRatio
print(node!.eulerAngles.y)
break
default: break
}
}
}
CameraHandler
节点是Camera
节点的父节点。一切正常,只是没有像我想要的那样工作。希望这对你们来说已经足够清楚了。
在Objective C。关键部分是最后三行,特别是最后一行中矩阵的乘法顺序(导致移动发生在局部space)。如果 transmat 和 cammat 切换,它会像现在一样再次运行(在世界中移动 space)。重构部分只适用于我的特定情况,在这种情况下,透视和正交相机都是可能的。
-(void)panCamera :(CGPoint)location {
CGFloat dx = _prevlocation.x - location.x;
CGFloat dy = location.y - _prevlocation.y;
_prevlocation = location;
//refactor dx and dy based on camera distance or orthoscale
if (cameraNode.camera.usesOrthographicProjection) {
dx = dx / 416 * cameraNode.camera.orthographicScale;
dy = dy / 416 * cameraNode.camera.orthographicScale;
} else {
dx = dx / 720 * cameraNode.position.z;
dy = dy / 720 * cameraNode.position.z;
}
SCNMatrix4 cammat = self.cameraNode.transform;
SCNMatrix4 transmat = SCNMatrix4MakeTranslation(dx, 0, dy);
self.cameraNode.transform = SCNMatrix4Mult(transmat, cammat);
}
根据 Xartec 的回答,我明白了。我将其翻译成 swift 和 retro-fitted 来满足我的需要。我对它不是很满意,因为运动不流畅。今天晚些时候我会努力把它弄平。
平移手势:此手势沿相机旋转的方向在场景周围平移相机父节点。它完全符合我的要求。
@objc func pan(gesture: UIPanGestureRecognizer) {
gesture.minimumNumberOfTouches = 1
gesture.maximumNumberOfTouches = 1
if gesture.numberOfTouches == 1 {
let view = self.view as! SCNView
let node = view.scene!.rootNode.childNode(withName: "CameraHandler", recursively: false)
let translation = gesture.translation(in: view)
var dx = previousTranslation.x - translation.x
var dy = previousTranslation.y - translation.y
dx = dx / 100
dy = dy / 100
print(dx,dy)
let cammat = node!.transform
let transmat = SCNMatrix4MakeTranslation(Float(dx), 0, Float(dy))
switch gesture.state {
case .began:
previousTranslation = translation
break;
case .changed:
node!.transform = SCNMatrix4Mult(transmat, cammat)
break
default: break
}
}
}
旋转手势:此手势用两根手指旋转相机。
@objc func rotate(gesture: UIPanGestureRecognizer) {
gesture.minimumNumberOfTouches = 2
gesture.maximumNumberOfTouches = 2
if gesture.numberOfTouches == 2 {
let view = self.view as! SCNView
let node = view.scene!.rootNode.childNode(withName: "CameraHandler", recursively: false)
let translate = gesture.translation(in: view)
var widthRatio:Float = 0
widthRatio = Float(translate.x / 10) * Float(Double.pi / 180)
switch gesture.state {
case .began:
lastWidthRatio = node!.eulerAngles.y
break
case .changed:
node!.eulerAngles.y = lastWidthRatio + widthRatio
break
default: break
}
}
}
要获得与我相同的功能,您需要将 cameraNode
附加到父节点。像这样:
//create and add a camera to the scene
let cameraNode = SCNNode()
//cameraHandler is declared outside viewDidLoad.
let cameraHandler = SCNNode()
cameraNode.camera = SCNCamera()
cameraNode.name = "Camera"
cameraNode.position = SCNVector3(x: 0.0,y: 10.0,z: 20.0)
//This euler angle only rotates the camera downward a little bit. It is not neccessary
cameraNode.eulerAngles = SCNVector3(x: -0.6, y: 0, z: 0)
cameraHandler.addChildNode(cameraNode)
cameraHandler.name = "CameraHandler"
scene.rootNode.addChildNode(cameraHandler)