I am willing to implement an iOS SpriteKit game that contains small games, each small game will be in separate swift packages.
I am using this initializer to load a scene from a sks file, SKScene(fileNamed: "GameScene"), however, the docs say that the file must be in the main bundle, is there a way to load the scene from a module? something similar to what we have with colors Color("colorName", bundle: .module)?
One solution would be to get the contents of the file as Data from whatever Bundle you put the scene in and then convert it to SKScene using NSKeyedUnarchiver
Unfortuenlly these workarounds won't work as expected, we still need to add our textures in the main bundle, what's happening is that SpriteKit is looking for textures inside the main bundle instead of the module, we can for sure re-set the texture from the bundle, but it doesn't makes sense.
How badly you want it? If the answer is "very" – you may swizzle +[SKTexture textureWithImageNamed:] and substitute it with a call that looks for the texture in your non-main bundle.
It's a missing feature and obviously many things could go wrong down that rabbit hole, so I can't recommend it.
Turns out that this solution will load the scene without any nodes
The other solution provided by @iGabriel works, but textures should be in the main bundle
In my testing not loading properly has to do with init(coder:) and secure decoding. I'm afraid the only way to make it work is to unarchive it yourself. It will look uglier because of not using the init but it definitely works
Here's some working code:
static func load(_ name: String, bundle: Bundle) -> Self? {
guard let url = bundle.url(forResource: name, withExtension: nil), let data = try? Data(contentsOf: url) else {
return nil
}
do {
let unarchiver = try NSKeyedUnarchiver(forReadingFrom: data)
unarchiver.requiresSecureCoding = false
unarchiver.setClass(self.classForKeyedUnarchiver(), forClassName: "SKScene")
let scene = unarchiver.decodeObject(forKey: NSKeyedArchiveRootObjectKey)
unarchiver.finishDecoding()
return scene as? Self
} catch {
return nil
}
}
Try this droid. That's undocumented land so ... here be dragons:
class MyTexture: SKTexture {
required init?(coder: NSCoder) {
super.init(coder: coder)
let name = value(forKey: "_imgName")
print(name)
// get image from a proper bundle
// override texture
}
}
Edit: "imgName" (no underscore prefix) works as well.