如何存储预计算的SKPhysicsBody数组,避免重复计算提升旧设备性能?
问题:如何持久化SKPhysicsBody数组以避免重复计算?
我在应用首次加载时于后台线程运行代码,提前生成SKPhysicsBody数组供节点加载时使用(参考Stack Overflow中@mfessenden关于SKPhysicsBody纹理动画的实现),以此提升性能。新设备上该操作可在应用intro阶段完成,影响不大,但旧设备可能因计算超时影响性能。我尝试将计算后的数组存储至UserDefaults,却触发NSInvalidArgumentException错误(提示插入非属性列表对象)。请问何种方式可存储该数据,使其仅需计算一次?
代码示例:
var Sat1shapes: [SKPhysicsBody] = [] var Sat1Frames: [SKTexture] = [] for frame in Sat1Frames { let node6 = SKSpriteNode() node6.physicsBody = SKPhysicsBody(texture: frame, size: CGSize(width: 414 * wR, height: 233 * hR)) Sat1shapes.append(node6.physicsBody!) } let Sat2AnimatedAtlas = SKTextureAtlas(named: "satsmall2") let numImagesSat2 = Sat2AnimatedAtlas.textureNames.count for i in 0...numImagesSat2 { let Sat2TextureName = "\(i)" Sat2Frames.append(Sat2AnimatedAtlas.textureNamed(Sat2TextureName)) }
解决方案
方法1:使用归档(NSKeyedArchiver/NSKeyedUnarchiver)
SKPhysicsBody遵循NSCoding协议,可将数组归档为Data后存储,支持存入UserDefaults或本地文件。
归档存储代码:
// 归档Sat1shapes数组 do { let data = try NSKeyedArchiver.archivedData(withRootObject: Sat1shapes, requiringSecureCoding: false) UserDefaults.standard.set(data, forKey: "StoredSat1PhysicsBodies") } catch { print("归档失败:\(error)") }
解归档读取代码:
// 启动时读取已存储的数组 if let data = UserDefaults.standard.data(forKey: "StoredSat1PhysicsBodies") { do { if let storedShapes = try NSKeyedUnarchiver.unarchiveTopLevelObjectWithData(data) as? [SKPhysicsBody] { Sat1shapes = storedShapes // 无需再重新计算 } } catch { print("解归档失败:\(error)") generatePhysicsBodies() } } else { // 无存储数据时重新计算 generatePhysicsBodies() } // 封装生成物理体的函数 func generatePhysicsBodies() { for frame in Sat1Frames { let node6 = SKSpriteNode() node6.physicsBody = SKPhysicsBody(texture: frame, size: CGSize(width: 414 * wR, height: 233 * hR)) Sat1shapes.append(node6.physicsBody!) } }
方法2:存储物理体生成参数(更轻量可靠)
若归档出现兼容性问题,可存储生成SKPhysicsBody的关键参数,下次直接重建:
- 记录每个物理体对应的纹理名称、尺寸等参数
- 启动时读取参数,重新从图集加载纹理并生成物理体
示例代码:
// 存储参数:假设Sat1Frames来自名为"sat1Atlas"的纹理图集 let sat1TextureNames = Sat1Frames.map { $0.name ?? "" } UserDefaults.standard.set(sat1TextureNames, forKey: "Sat1TextureNames") UserDefaults.standard.set(["width": 414 * wR, "height": 233 * hR], forKey: "Sat1PhysicsSize") // 读取并重建 if let textureNames = UserDefaults.standard.array(forKey: "Sat1TextureNames") as? [String], let sizeDict = UserDefaults.standard.dictionary(forKey: "Sat1PhysicsSize") as? [String: CGFloat], let width = sizeDict["width"], let height = sizeDict["height"] { let size = CGSize(width: width, height: height) let atlas = SKTextureAtlas(named: "sat1Atlas") Sat1shapes = textureNames.compactMap { name in let texture = atlas.textureNamed(name) return SKPhysicsBody(texture: texture, size: size) } } else { generatePhysicsBodies() }
方法3:本地文件存储(适合大数据量)
若SKPhysicsBody数组较大,可直接将归档数据存入应用Documents目录:
存储代码:
do { let data = try NSKeyedArchiver.archivedData(withRootObject: Sat1shapes, requiringSecureCoding: false) let documentsDir = FileManager.default.urls(for: .documentDirectory, in: .userDomainMask).first! let fileURL = documentsDir.appendingPathComponent("Sat1PhysicsBodies.data") try data.write(to: fileURL) } catch { print("写入文件失败:\(error)") }
读取代码:
let documentsDir = FileManager.default.urls(for: .documentDirectory, in: .userDomainMask).first! let fileURL = documentsDir.appendingPathComponent("Sat1PhysicsBodies.data") if FileManager.default.fileExists(atPath: fileURL.path) { do { let data = try Data(contentsOf: fileURL) if let storedShapes = try NSKeyedUnarchiver.unarchiveTopLevelObjectWithData(data) as? [SKPhysicsBody] { Sat1shapes = storedShapes } } catch { print("读取文件失败:\(error)") generatePhysicsBodies() } } else { generatePhysicsBodies() }
内容的提问来源于stack exchange,提问作者Guitarman4
相关产品推荐
相关产品推荐

