SpriteKit无法从sks文件解归档SKEmitterNode子类问题求助
解决SKEmitterNode子类解归档返回nil的问题
问题场景
我编写了如下Swift代码,尝试从sks文件加载自定义的PassthroughEmitterNode(继承自SKEmitterNode):
final class PassthroughEmitterNode: SKEmitterNode { static func load(fnWithoutExtension: String, in bundle: Bundle) -> SKEmitterNode? { guard let sksPath = bundle.path(forResource: fnWithoutExtension, ofType: "sks"), let sksData = try? Data(contentsOf: URL(fileURLWithPath: sksPath)), let unarchiver = try? NSKeyedUnarchiver(forReadingFrom: sksData), let texturePath = bundle.path(forResource: fnWithoutExtension, ofType: "png"), let textureImage = UIImage(contentsOfFile: texturePath) else { return nil } // Required to decode into subclass unarchiver.setClass(self.classForKeyedUnarchiver(), forClassName: "SKEmitterNode") let emitter = unarchiver.decodeObject(forKey: NSKeyedArchiveRootObjectKey) as? PassthroughEmitterNode unarchiver.finishDecoding() guard let emitter else { return nil } // 纹理文件不在主bundle中,需手动设置 emitter.particleTexture = SKTexture(image: textureImage) // 需启用用户交互以接收触摸事件 emitter.isUserInteractionEnabled = true return emitter } }
这段代码的思路和从sks文件读取SKScene子类实例的方式类似,但解归档时始终返回nil。直接将sks文件解归档为SKEmitterNode本身是可行的,但子类就不行。
解决方案
问题出在类映射的API使用和调用逻辑上,以下是修正步骤:
修正类映射调用
替换原代码中unarchiver.setClass(self.classForKeyedUnarchiver(), forClassName: "SKEmitterNode")为:unarchiver.setClass(PassthroughEmitterNode.self, forClassName: "SKEmitterNode")classForKeyedUnarchiver()是Objective-C兼容API,Swift中直接使用子类.self更可靠,能确保解归档器正确识别子类类型。调整解归档流程
把空值判断提前,避免无效的解归档后续操作,同时确保finishDecoding()在所有解码操作后调用。
修正后的完整代码:
final class PassthroughEmitterNode: SKEmitterNode { static func load(fnWithoutExtension: String, in bundle: Bundle) -> SKEmitterNode? { guard let sksURL = bundle.url(forResource: fnWithoutExtension, withExtension: "sks"), let sksData = try? Data(contentsOf: sksURL), let unarchiver = try? NSKeyedUnarchiver(forReadingFrom: sksData), let textureURL = bundle.url(forResource: fnWithoutExtension, withExtension: "png"), let textureImage = UIImage(contentsOfFile: textureURL.path) else { return nil } // 正确映射子类到归档中的SKEmitterNode类名 unarchiver.setClass(PassthroughEmitterNode.self, forClassName: "SKEmitterNode") // 提前判断解码结果,避免无效操作 guard let emitter = unarchiver.decodeObject(forKey: NSKeyedArchiveRootObjectKey) as? PassthroughEmitterNode else { unarchiver.finishDecoding() return nil } unarchiver.finishDecoding() // 手动设置纹理 emitter.particleTexture = SKTexture(image: textureImage) // 启用用户交互 emitter.isUserInteractionEnabled = true return emitter } }
额外优化建议
- 优先使用
URL而非字符串路径获取资源,减少路径拼接的潜在错误 - 可以将
try?替换为do-catch块,捕获具体错误信息,方便调试定位问题
内容的提问来源于stack exchange,提问作者OMGPOP
相关产品推荐
相关产品推荐

