Scenekit使用SpriteKit场景作为材质时移除节点崩溃如何解决
问题原因
当SKScene作为SCNMaterial的内容使用时,其执行环境和纯SpriteKit项目存在两个核心差异,是崩溃的直接诱因:
- 线程执行上下文不同:纯SpriteKit项目的
update(_:)回调固定在主线程执行,而绑定到SceneKit材质的SKScene的更新回调可能被SceneKit的独立渲染线程触发,此时直接操作SKNode节点树会触发跨线程访问冲突,导致节点内存状态异常。 - 渲染读写冲突:你调用
removeFromParent()释放节点的同时,SceneKit可能正在读取SKScene的内容来生成材质纹理,直接释放正在被读取的节点会触发野指针访问崩溃。
解决方案
最优方案:复用节点避免频繁增删
完全不需要每次更新都销毁再重建指针节点,直接修改已有节点的属性即可,从根源避免节点删除操作:
override func update(_ currentTime: TimeInterval) { // 优先获取已有节点,不存在才创建 let fulcro: SKNode if let existFulcro = self.childNode(withName: "fulcro") { fulcro = existFulcro // 移除旧的指针线 fulcro.childNode(withName: "EGT_LINE")?.removeFromParent() } else { fulcro = SKNode() fulcro.name = "fulcro" fulcro.position = CGPoint(x: 300, y: 260) self.addChild(fulcro) } // 添加新的指针线 let line = makeLine(startPos: CGPoint(x: 0, y: 0), endPos: getCoordinate(angleDeg: getCurrentEGT()), name: "EGT_LINE") fulcro.addChild(line) }
如果你把指针的旋转逻辑直接封装到fulcro节点的zRotation属性上,连子节点都不需要删改,性能会更高。
兼容方案:如果必须做节点增删操作
把所有节点树修改的逻辑派发到主线程执行,同时加安全校验,避免访问已经被释放的节点:
override func update(_ currentTime: TimeInterval) { DispatchQueue.main.async { [weak self] in guard let self = self else { return } guard let fulcroToRemove = self.childNode(withName: "fulcro"), fulcroToRemove.parent == self else { return } fulcroToRemove.removeFromParent() let fulcro = SKNode() fulcro.name = "fulcro" fulcro.position = CGPoint(x: 300, y: 260) let line = makeLine(startPos: CGPoint(x: 0, y: 0), endPos: getCoordinate(angleDeg: getCurrentEGT()), name: "EGT_LINE") fulcro.addChild(line) self.addChild(fulcro) } }
内容的提问来源于stack exchange,提问作者Damiano Miazzi
相关产品推荐
相关产品推荐

