SceneKit中SCNNode缩放旋转后boundingBox未更新,如何获取更新后的包围盒?
如何获取SceneKit中应用变换后SCNNode的更新boundingBox?
我编写了以下Swift代码:
let box = SCNBox(width: 1, height: 2, length: 4, chamferRadius: 0.2) box.firstMaterial?.diffuse.contents = UIColor.yellow box.firstMaterial?.emission.contents = UIColor.green let boxNode = SCNNode(geometry: box) print(boxNode.boundingBox) boxNode.eulerAngles = SCNVector3(0.785, 0.5, 0.5) print(boxNode.boundingBox) boxNode.scale = SCNVector3(1.3, 2.3, 3.3) print(boxNode.boundingBox) scene.rootNode.addChildNode(boxNode)
得到的输出为:
(min: __C.SCNVector3(x: -0.5, y: -1.0, z: -2.0), max: __C.SCNVector3(x: 0.5, y: 1.0, z: 2.0)) (min: __C.SCNVector3(x: -0.5, y: -1.0, z: -2.0), max: __C.SCNVector3(x: 0.5, y: 1.0, z: 2.0)) (min: __C.SCNVector3(x: -0.5, y: -1.0, z: -2.0), max: __C.SCNVector3(x: 0.5, y: 1.0, z: 2.0))
可见,即便对SCNNode应用旋转或缩放操作,其boundingBox属性仍未更新,这与SpriteKit的行为不同。请问如何获取更新后的boundingBox?
解决方法
SceneKit中SCNNode的boundingBox返回的是节点本地坐标系下的包围盒,不会自动应用节点的变换(旋转、缩放、位移)。要获取变换后的包围盒,推荐以下两种方式:
1. 使用系统提供的boundingBox(for:)方法
调用节点的boundingBox(for: .world)方法,传入.world参数即可得到世界坐标系下的包围盒,该结果会自动考虑节点自身及所有父节点的变换。
示例代码:
// 应用变换后调用 let worldBoundingBox = boxNode.boundingBox(for: .world) print(worldBoundingBox)
2. 手动转换本地包围盒到目标坐标系
如果需要自定义计算逻辑,可以先获取本地包围盒的8个顶点,再通过节点的变换矩阵将顶点转换到目标坐标系,最后计算顶点的最小/最大坐标得到包围盒。
示例代码:
func calculateWorldBoundingBox(for node: SCNNode) -> (min: SCNVector3, max: SCNVector3) { let localBox = node.boundingBox // 生成本地包围盒的8个顶点 let localVertices = [ SCNVector3(localBox.min.x, localBox.min.y, localBox.min.z), SCNVector3(localBox.min.x, localBox.min.y, localBox.max.z), SCNVector3(localBox.min.x, localBox.max.y, localBox.min.z), SCNVector3(localBox.min.x, localBox.max.y, localBox.max.z), SCNVector3(localBox.max.x, localBox.min.y, localBox.min.z), SCNVector3(localBox.max.x, localBox.min.y, localBox.max.z), SCNVector3(localBox.max.x, localBox.max.y, localBox.min.z), SCNVector3(localBox.max.x, localBox.max.y, localBox.max.z) ] // 将本地顶点转换到世界坐标系 let worldVertices = localVertices.map { node.convertPosition($0, to: nil) } // 计算包围盒的最小/最大坐标 var minVec = worldVertices[0] var maxVec = worldVertices[0] for vertex in worldVertices { minVec.x = Swift.min(minVec.x, vertex.x) minVec.y = Swift.min(minVec.y, vertex.y) minVec.z = Swift.min(minVec.z, vertex.z) maxVec.x = Swift.max(maxVec.x, vertex.x) maxVec.y = Swift.max(maxVec.y, vertex.y) maxVec.z = Swift.max(maxVec.z, vertex.z) } return (min: minVec, max: maxVec) } // 使用示例 let customWorldBox = calculateWorldBoundingBox(for: boxNode) print(customWorldBox)
注意事项
- 若仅需考虑当前节点自身的变换(不包含父节点),可将转换目标设为父节点坐标系(
convertPosition(_:to: node.parent)),或传入对应坐标系参数到boundingBox(for:)。 - 优先使用
boundingBox(for:)方法,系统实现的性能更优且逻辑更严谨。
内容的提问来源于stack exchange,提问作者OMGPOP
相关产品推荐
相关产品推荐

