如何在SceneKit与SwiftUI中实现稳定的3D模型重置按钮?
问题描述
在SwiftUI开发的ModelViewer中,通过CustomSceneView展示3D模型,需实现重置按钮将模型及相机恢复至初始状态。当前实现存在以下问题:
- 当模型处于运动状态时点击重置按钮,复位后的位置会随机偏移
- 最初缩放后复位无法恢复缩放比例,添加
self.view.pointOfView?.camera?.fieldOfView = camera.fieldOfView后缩放问题解决,但运动中复位偏移的问题仍存在 - 限制条件:无法修改模型,只能通过相机和节点定位调整
修复方案
问题根源在于开启allowsCameraControl后,SceneKit内置相机控制逻辑会在后台持续修改相机节点属性,与复位事务产生冲突。以下是针对性修复:
- 临时禁用相机控制:复位前关闭
allowsCameraControl,避免内置逻辑干扰,复位完成后重新开启 - 直接操作固定相机节点:使用预先创建的
cameraNode而非view.pointOfView,避免引用不稳定的节点实例 - 补充正交投影参数重置:正交模式下缩放依赖
orthographicScale,需同步重置该参数 - 清理未完成事务:复位前结束所有未完成的SCNTransaction,防止动画叠加导致偏移
修改后的代码
补充Camera结构体参数
struct Camera { static let `default` = Camera( position: SCNVector3(x: 0.805120, y: 0.057330, z: 1.4), rotation: SCNVector4(0, 0, 0, 0), orientation: SCNVector4(x: 0, y: 0, z: 0, w: 1.0), fieldOfView: 60, orthographicScale: 1.0 ) let position: SCNVector3 let rotation: SCNVector4 let orientation: SCNVector4 let fieldOfView: CGFloat let orthographicScale: CGFloat init(position: SCNVector3, rotation: SCNVector4, orientation: SCNVector4, fieldOfView: CGFloat, orthographicScale: CGFloat) { self.position = position self.rotation = rotation self.orientation = orientation self.fieldOfView = fieldOfView self.orthographicScale = orthographicScale } init(from node: SCNNode) { let camera = node.camera ?? SCNCamera() self.init( position: node.position, rotation: node.rotation, orientation: node.orientation, fieldOfView: camera.fieldOfView, orthographicScale: camera.orthographicScale ) } }
修复CustomSceneView的Coordinator逻辑
class Coordinator: NSObject, SCNSceneRendererDelegate { // 原有初始化代码保持不变 func set(camera: Camera?) { guard let camera else { return } // 清理未完成的事务,避免动画冲突 SCNTransaction.flush() // 临时禁用相机控制,阻止内置逻辑干扰复位 view.allowsCameraControl = false SCNTransaction.begin() SCNTransaction.animationDuration = 1 // 直接操作预先创建的cameraNode,确保节点引用稳定 cameraNode.position = camera.position cameraNode.rotation = camera.rotation cameraNode.orientation = camera.orientation // 重置相机的所有参数,包括正交缩放 if let cameraComponent = cameraNode.camera { cameraComponent.fieldOfView = camera.fieldOfView cameraComponent.orthographicScale = camera.orthographicScale } // 动画完成后恢复相机控制 SCNTransaction.completionBlock = { [weak self] in self?.view.allowsCameraControl = true } SCNTransaction.commit() } // 原有渲染代理方法保持不变 }
内容的提问来源于stack exchange,提问作者brinxi11
相关产品推荐
相关产品推荐

