如何使SceneKit SCNCameraController的frameNodes正确设置目标?
解决SCNCameraController frameNodes函数多节点目标设置bug的方法
我也遇到过这个问题,确实frameNodes(_:)在iOS 11.x版本处理多节点时存在明确的bug——它只会调整相机视角让所有节点进入视野,但完全不会按照头文件注释所说的,将相机目标设置为所有节点包围球的中心,默认会 fallback 到原点(0,0,0)。下面是我验证过的解决思路和代码:
简便计算多节点包围体中心的方法
要手动获取多个节点的整体包围体中心,你可以遍历所有节点的boundingBox,合并出一个能包含所有节点的最小包围盒,再计算这个包围盒的中心坐标。这个方法轻量且可靠:
func calculateCombinedBoundingBoxCenter(nodes: [SCNNode]) -> SCNVector3 { guard !nodes.isEmpty else { return SCNVector3Zero } // 初始化最小/最大边界为第一个节点的包围盒 var minVector = nodes[0].boundingBox.min var maxVector = nodes[0].boundingBox.max // 遍历剩余节点,扩展边界以包含所有节点 for node in nodes.dropFirst() { let nodeMin = node.boundingBox.min let nodeMax = node.boundingBox.max minVector.x = min(minVector.x, nodeMin.x) minVector.y = min(minVector.y, nodeMin.y) minVector.z = min(minVector.z, nodeMin.z) maxVector.x = max(maxVector.x, nodeMax.x) maxVector.y = max(maxVector.y, nodeMax.y) maxVector.z = max(maxVector.z, nodeMax.z) } // 返回包围盒中心 return SCNVector3( x: (minVector.x + maxVector.x) / 2, y: (minVector.y + maxVector.y) / 2, z: (minVector.z + maxVector.z) / 2 ) }
修复后的Playground代码
把这个计算方法整合到你的示例代码中,手动设置相机控制器的target,就能解决旋转目标错误的问题:
import UIKit import SceneKit import PlaygroundSupport var sceneView = SCNView(frame: CGRect(x: 0, y: 0, width: 400, height: 400)) var scene = SCNScene() sceneView.scene = scene PlaygroundPage.current.liveView = sceneView sceneView.autoenablesDefaultLighting = true var cameraNode = SCNNode() cameraNode.camera = SCNCamera() cameraNode.position = SCNVector3(x: 0, y: 0, z: 10) scene.rootNode.addChildNode(cameraNode) sceneView.allowsCameraControl = true sceneView.defaultCameraController.interactionMode = .orbitTurntable sceneView.defaultCameraController.pointOfView = sceneView.pointOfView var torus = SCNTorus(ringRadius: 1, pipeRadius: 0.5) var torusNode = SCNNode(geometry: torus) torusNode.position = SCNVector3(0, 0, 0) scene.rootNode.addChildNode(torusNode) torus.firstMaterial?.diffuse.contents = UIColor.red torus.firstMaterial?.specular.contents = UIColor.white var cube = SCNBox(width: 1, height: 1, length: 1, chamferRadius: 0) var cubeNode = SCNNode(geometry: cube) cubeNode.position = SCNVector3(4, 0, 0) scene.rootNode.addChildNode(cubeNode) cube.firstMaterial?.diffuse.contents = UIColor.blue cube.firstMaterial?.specular.contents = UIColor.white // 计算多节点的包围盒中心 let targetCenter = calculateCombinedBoundingBoxCenter(nodes: [torusNode, cubeNode]) SCNTransaction.begin() SCNTransaction.animationDuration = 1.0 sceneView.defaultCameraController.frameNodes([torusNode, cubeNode]) // 手动设置正确的旋转目标 sceneView.defaultCameraController.target = targetCenter SCNTransaction.commit() // 新增的包围盒计算函数 func calculateCombinedBoundingBoxCenter(nodes: [SCNNode]) -> SCNVector3 { guard !nodes.isEmpty else { return SCNVector3Zero } var minVector = nodes[0].boundingBox.min var maxVector = nodes[0].boundingBox.max for node in nodes.dropFirst() { let nodeMin = node.boundingBox.min let nodeMax = node.boundingBox.max minVector.x = min(minVector.x, nodeMin.x) minVector.y = min(minVector.y, nodeMin.y) minVector.z = min(minVector.z, nodeMin.z) maxVector.x = max(maxVector.x, nodeMax.x) maxVector.y = max(maxVector.y, nodeMax.y) maxVector.z = max(maxVector.z, nodeMax.z) } return SCNVector3( x: (minVector.x + maxVector.x) / 2, y: (minVector.y + maxVector.y) / 2, z: (minVector.z + maxVector.z) / 2 ) }
修改后,相机会正确围绕圆环和立方体之间的中心旋转,这个方法在iOS 11.2.1及后续测试过的版本中都能稳定工作,直到苹果修复frameNodes的底层bug。
内容的提问来源于stack exchange,提问作者LenK
相关产品推荐
相关产品推荐

