iOS: SceneKit中如何让节点绕特定轴旋转
iOS: How to make a node rotate around a specific axis in SceneKit
我是 Swift 编程和 SceneKit 框架的新手。
我正在尝试构建一个 Rubik's twist 应用程序,到目前为止我得到的是位于相机中心的链条。
Rubik's twist chain
我是这样实现的:
var snake = [SCNNode]()
var bounds_z = Float()
var bounds_x = Float()
var j = 0
for i in 0...11{
let piece_scene_1 = SCNScene(named: "piece.scnassets/piece.scn")!
let piece_scene_2 = SCNScene(named: "piece.scnassets/piece.scn")!
piece_scene_1.rootNode.childNodes[0].scale = SCNVector3(0.9, 0.9, 0.9)
piece_scene_2.rootNode.childNodes[0].scale = SCNVector3(0.9, 0.9, 0.9)
bounds_z = Float(2*(piece_scene_1.rootNode.childNodes[0].geometry?.boundingBox.max.z ?? 0.0))
bounds_x = Float(2*(piece_scene_1.rootNode.childNodes[0].geometry?.boundingBox.min.x ?? 0.0))
snake.append(piece_scene_1.rootNode.childNodes[0])
snake.append(piece_scene_2.rootNode.childNodes[0])
snake[j].name = "piece_\(j)"
snake[j+1].name = "piece_\(j+1)"
snake[j+1].rotation = SCNVector4(0, 1, 0, Float.pi)
snake[j].position = SCNVector3(x: Float(i)*bounds_x,y: Float(0),z: Float(i)*bounds_z)
snake[j+1].position = SCNVector3(x: Float(i)*bounds_x,y: Float(0),z: Float(i+1)*bounds_z)
scene.rootNode.addChildNode(snake[j])
scene.rootNode.addChildNode(snake[j+1])
j+=2
}
cameraNode.look(at: snake[(snake.count/2)-1].position)
我现在的问题是让一组棋子在点击时旋转。我的想法是制作一个容器节点,在被攻丝的节点之前添加所有节点,然后围绕被攻丝面(与容器相邻)的法线轴旋转容器。
这是我的尝试:
@objc func handleTap(_ gestureRecognize: UIGestureRecognizer){
let sceneView = self.view as! SCNView
let p = gestureRecognize.location(in: sceneView)
let hitResults = sceneView.hitTest(p, options: [:])
if hitResults.count > 0 {
// retrieved the first clicked object
let result: SCNHitTestResult = hitResults[0]
let container = SCNNode()
let id_name = result.node.name
let id_array = id_name?.components(separatedBy: CharacterSet.decimalDigits.inverted)
// add nodes to container
for item in id_array! {
if let id = Int(item){
for i in 0...id-1{
container.addChildNode(scene.rootNode.childNode(withName: "piece_\(i)", recursively: true)!)
}
// add container to scene
scene.rootNode.addChildNode(container)
// get container orientation
var GLKQuat = GLKQuaternionMake(container.orientation.x, container.orientation.y, container.orientation.z, container.orientation.w)
// get future orientation
let multiplier = GLKQuaternionMakeWithAngleAndAxis(Float.pi/2, 0, 0, 1)
// assign new orientation to container
GLKQuat = GLKQuaternionMultiply(GLKQuat, multiplier)
container.orientation = SCNQuaternion(GLKQuat.x, GLKQuat.y, GLKQuat.z, GLKQuat.w)
// maintain childs position after rotation
for childnode in container.childNodes{
let child_transform = childnode.parent!.convertTransform(childnode.transform, to: scene.rootNode)
childnode.removeFromParentNode()
childnode.transform = child_transform
scene.rootNode.addChildNode(childnode)}
}
}
}
}
问题在于,由于容器是 scene.rootNode.child,将围绕其 z 轴旋转
像这样:
Rubiks twist chain after rotation
绕z轴旋转就是这行代码引起的
let multiplier = GLKQuaternionMakeWithAngleAndAxis(Float.pi/2, 0, 0, 1)
如果您想绕另一个轴旋转,您必须指定 x、y 和 z 分量:
from the Apple documentation
经过几天的挣扎,我终于找到了解决办法:
let container = SCNNode()
for i in 0...id-1{
container.addChildNode(scene.rootNode.childNode(withName: "piece_\(i)", recursively: true)!)
}
container.name = "body_to_rotate"
// add the container to scene root node
scene.rootNode.addChildNode(container)
// transform from container to tapped node
let container_transform = container.parent!.convertTransform(container.transform, to: result.node)
container.transform = container_transform
// add container as child node to tapped node
result.node.addChildNode(container)
// make the rotation happen
let old_rotation = result.node.orientation
var quat_rot = GLKQuaternionMake(old_rotation.x, old_rotation.y, old_rotation.z, old_rotation.w)
let multiplier = GLKQuaternionMakeWithAngleAndAxis(Float.pi/2, 0, 0, 1)
quat_rot = GLKQuaternionMultiply(quat_rot, multiplier)
result.node.orientation = SCNQuaternion(quat_rot.x, quat_rot.y, quat_rot.z, quat_rot.w)
for childnode in container.childNodes{
let child_transform = childnode.parent!.convertTransform(childnode.transform, to: scene.rootNode)
childnode.removeFromParentNode()
childnode.transform = child_transform
scene.rootNode.addChildNode(childnode)
}
// delete container
container.removeFromParentNode()
我是 Swift 编程和 SceneKit 框架的新手。 我正在尝试构建一个 Rubik's twist 应用程序,到目前为止我得到的是位于相机中心的链条。 Rubik's twist chain
我是这样实现的:
var snake = [SCNNode]()
var bounds_z = Float()
var bounds_x = Float()
var j = 0
for i in 0...11{
let piece_scene_1 = SCNScene(named: "piece.scnassets/piece.scn")!
let piece_scene_2 = SCNScene(named: "piece.scnassets/piece.scn")!
piece_scene_1.rootNode.childNodes[0].scale = SCNVector3(0.9, 0.9, 0.9)
piece_scene_2.rootNode.childNodes[0].scale = SCNVector3(0.9, 0.9, 0.9)
bounds_z = Float(2*(piece_scene_1.rootNode.childNodes[0].geometry?.boundingBox.max.z ?? 0.0))
bounds_x = Float(2*(piece_scene_1.rootNode.childNodes[0].geometry?.boundingBox.min.x ?? 0.0))
snake.append(piece_scene_1.rootNode.childNodes[0])
snake.append(piece_scene_2.rootNode.childNodes[0])
snake[j].name = "piece_\(j)"
snake[j+1].name = "piece_\(j+1)"
snake[j+1].rotation = SCNVector4(0, 1, 0, Float.pi)
snake[j].position = SCNVector3(x: Float(i)*bounds_x,y: Float(0),z: Float(i)*bounds_z)
snake[j+1].position = SCNVector3(x: Float(i)*bounds_x,y: Float(0),z: Float(i+1)*bounds_z)
scene.rootNode.addChildNode(snake[j])
scene.rootNode.addChildNode(snake[j+1])
j+=2
}
cameraNode.look(at: snake[(snake.count/2)-1].position)
我现在的问题是让一组棋子在点击时旋转。我的想法是制作一个容器节点,在被攻丝的节点之前添加所有节点,然后围绕被攻丝面(与容器相邻)的法线轴旋转容器。 这是我的尝试:
@objc func handleTap(_ gestureRecognize: UIGestureRecognizer){
let sceneView = self.view as! SCNView
let p = gestureRecognize.location(in: sceneView)
let hitResults = sceneView.hitTest(p, options: [:])
if hitResults.count > 0 {
// retrieved the first clicked object
let result: SCNHitTestResult = hitResults[0]
let container = SCNNode()
let id_name = result.node.name
let id_array = id_name?.components(separatedBy: CharacterSet.decimalDigits.inverted)
// add nodes to container
for item in id_array! {
if let id = Int(item){
for i in 0...id-1{
container.addChildNode(scene.rootNode.childNode(withName: "piece_\(i)", recursively: true)!)
}
// add container to scene
scene.rootNode.addChildNode(container)
// get container orientation
var GLKQuat = GLKQuaternionMake(container.orientation.x, container.orientation.y, container.orientation.z, container.orientation.w)
// get future orientation
let multiplier = GLKQuaternionMakeWithAngleAndAxis(Float.pi/2, 0, 0, 1)
// assign new orientation to container
GLKQuat = GLKQuaternionMultiply(GLKQuat, multiplier)
container.orientation = SCNQuaternion(GLKQuat.x, GLKQuat.y, GLKQuat.z, GLKQuat.w)
// maintain childs position after rotation
for childnode in container.childNodes{
let child_transform = childnode.parent!.convertTransform(childnode.transform, to: scene.rootNode)
childnode.removeFromParentNode()
childnode.transform = child_transform
scene.rootNode.addChildNode(childnode)}
}
}
}
}
问题在于,由于容器是 scene.rootNode.child,将围绕其 z 轴旋转 像这样: Rubiks twist chain after rotation
绕z轴旋转就是这行代码引起的
let multiplier = GLKQuaternionMakeWithAngleAndAxis(Float.pi/2, 0, 0, 1)
如果您想绕另一个轴旋转,您必须指定 x、y 和 z 分量: from the Apple documentation
经过几天的挣扎,我终于找到了解决办法:
let container = SCNNode()
for i in 0...id-1{
container.addChildNode(scene.rootNode.childNode(withName: "piece_\(i)", recursively: true)!)
}
container.name = "body_to_rotate"
// add the container to scene root node
scene.rootNode.addChildNode(container)
// transform from container to tapped node
let container_transform = container.parent!.convertTransform(container.transform, to: result.node)
container.transform = container_transform
// add container as child node to tapped node
result.node.addChildNode(container)
// make the rotation happen
let old_rotation = result.node.orientation
var quat_rot = GLKQuaternionMake(old_rotation.x, old_rotation.y, old_rotation.z, old_rotation.w)
let multiplier = GLKQuaternionMakeWithAngleAndAxis(Float.pi/2, 0, 0, 1)
quat_rot = GLKQuaternionMultiply(quat_rot, multiplier)
result.node.orientation = SCNQuaternion(quat_rot.x, quat_rot.y, quat_rot.z, quat_rot.w)
for childnode in container.childNodes{
let child_transform = childnode.parent!.convertTransform(childnode.transform, to: scene.rootNode)
childnode.removeFromParentNode()
childnode.transform = child_transform
scene.rootNode.addChildNode(childnode)
}
// delete container
container.removeFromParentNode()