这是一个合法的问题,因为SceneKit 没有提供从主包之外的文件初始化粒子系统的开箱即用解决方案(唯一的 init 方法 SCNParticleSystem.init(named:inDirectory:) 意味着 SCNParticleSystem.scnp文件在主包中)。
幸运的是我们.scnp 文件只是编码/存档SCNParticleSystem 实例,我们可以使用NSKeyedUnarchiver 轻松解码/取消存档:
extension SCNParticleSystem {
static func make(fromFileAt url: URL) -> SCNParticleSystem? {
guard let data = try? Data(contentsOf: url),
let object = try? NSKeyedUnarchiver.unarchiveTopLevelObjectWithData(data),
let system = object as? SCNParticleSystem else { return nil }
return system
}
}
如果您不需要支持iOS 9 和iOS 10,您可以使用NSKeyedUnarchiver.unarchivedObject(ofClass: SCNParticleSystem.self, from: data) 代替NSKeyedUnarchiver.unarchiveTopLevelObjectWithData(_:) 和类型转换,这是在iOS 11.0 中引入的。
您最有可能遇到的另一个问题是缺少粒子图像。这是因为默认情况下SceneKit 会在主包中查找它们。截至当前版本的 iOS(iOS 12)和 Xcode(Xcode 10).scnp 文件(particleImage 属性)中的粒子图像是 String 值,它们是主包中的纹理文件名(可能会改变,但可能不会,但是我们可以使用的其他东西不多)。
所以我的建议是采用该文件名并在.scnp 文件所在的同一目录中查找具有相同名称的纹理文件:
extension SCNParticleSystem {
static func make(fromFileAt url: URL) -> SCNParticleSystem? {
guard let data = try? Data(contentsOf: url),
let object = try? NSKeyedUnarchiver.unarchiveTopLevelObjectWithData(data),
let system = object as? SCNParticleSystem else { return nil }
if let particleImageName = system.particleImage as? String {
let particleImageURL = url
.deletingLastPathComponent()
.appendingPathComponent(particleImageName)
if FileManager.default.fileExists(atPath: particleImageURL.path) {
system.particleImage = particleImageURL
}
}
return system
}
}
您可以只设置图像文件的URL,SceneKit 将从那里处理它。
附带说明一下,可下载内容的推荐目录是Application Support 目录,而不是Documents。
应用程序支持:使用此目录存储所有应用程序数据文件,但与用户文档关联的文件除外。例如,您可以使用此目录来存储应用程序创建的数据文件、配置文件、模板或其他由应用程序管理的固定或可修改资源。应用程序可能会使用此目录来存储最初包含在应用程序包中的资源的可修改副本。游戏可能会使用此目录来存储用户购买并从服务器下载的新关卡。
(来自File System Basics)