Cannot Use Unarchivefromfile to Set Gamescene in Spritekit

Cannot use unarchiveFromFile to set GameScene in SpriteKit

You should use the init(fileNamed:) initialiser, which is available from iOS 8 onwards. For example:

if let gameOverScene = GameOverScene(fileNamed: "GameOverScene") {
// ...
}

It's important to note that init(fileNamed:) is a convenience initialiser on SKNode:

convenience init?(fileNamed filename: String)

Therefore, for GameOverScene to automatically inherit init(fileNamed:), GameOverScene must adhere to the following rules from The Swift Programming Language: Initialisation (rule 2 especially):

Assuming that you provide default values for any new properties you introduce in a subclass, the following two rules apply:

Rule 1 If your subclass doesn’t define any designated initializers, it
automatically inherits all of its superclass designated initializers.

Rule 2 If your subclass provides an implementation of all of its
superclass designated initializers—either by inheriting them as per
rule 1, or by providing a custom implementation as part of its
definition—then it automatically inherits all of the superclass
convenience initializers.

SpriteKit unarchiveFromFile does not load textures from asset catalogues and atlases

[Answering my own question here]

As far as I can tell, at this moment, loading texture from asset catalogues - Images.xcassets - while unarchiving or deserializing an SKScene file does not work on OSX based on my attempts and the devforumns post referenced above.

Loading of the textures from image atlases, however, works by forcing SKTexture to load the image. Forcing or 'touch'ing SKTexture can be done via the preloadWithCompletionHandler(_:) or preloadTextures(_:, withCompletionHandler:) methods, or simply by printing the description of the sprite's SKTexture as i have discovered.

For the benefit of anybody who might need further assistance, here is a code snippet that preloads the textures after unarchiving an SKS file:

if let scene = GameScene.unarchiveFromFile("GameScene") as? GameScene {
for child in scene.children as [SKNode] {
if child is SKSpriteNode {
let sprite = child as SKSpriteNode
sprite.texture?.preloadWithCompletionHandler({ })
/* or even a simple debug log of the texture */
/* println(sprite.texture) */
}
}
/* Do your other stuff ... */
...
}

If I'm wrong please correct me. Until somebody does, or Apple fixes the discrepancy between SpriteKit's behavior between iOS and OSX, I will not be looking for another solution, and will follow Murphey's law:

If it works, don't fix it

How do I link a .sks file to a .swift file in SpriteKit

This method will unarchive an sks file and initialise it as whichever SKNode subclass it is called on. This means you can unarchive a file as an SKNode (so the root node will be an SKNode) and add it as a child to your scene. You can also unarchive a file as GameScene or any SKNode subclass.

extension SKNode {
class func unarchiveFromFile(file : NSString) -> SKNode? {
if let path = NSBundle.mainBundle().pathForResource(file, ofType: "sks") {
var sceneData = NSData(contentsOfFile: path, options: .DataReadingMappedIfSafe, error: nil)!
var archiver = NSKeyedUnarchiver(forReadingWithData: sceneData)

archiver.setClass(self.classForKeyedUnarchiver(), forClassName: "SKScene")
let scene = archiver.decodeObjectForKey(NSKeyedArchiveRootObjectKey) as SKNode
archiver.finishDecoding()
return scene
} else {
return nil
}
}
}

Use it like this:

let scene = GameScene.unarchiveFromFile("GameScene")! as GameScene

or

let levelStuff = SKNode.unarchiveFromFile("Level 1")!
self.addChild(levelStuff)

Scene created in Sprite kit level editor is not working

It may be transition to a grey scene due to spelling errors or the file not even being in the mainBundle. I have tested this and it works. You need to make sure that you are writing the exact name of the .sks file when loading or else you get a grey screen.

Example

If the file is called GameScreen.sks and the class is called GameScene then if you put the class name when loading the .sks file you will get a grey screen.

let doors = SKTransition.doorwayWithDuration(1.0)
let archeryScene = GameScene(fileNamed: "GameScreen")
self.view?.presentScene(archeryScene, transition: doors)

So if we look at the second line of code, We see that it is loading the .sks file with the name GameScreen opposed to the class name GameScene. We also abort using the file extension because the compiler know by default it is a .sks file we are searching for in the mainBundle.

If you do keep getting a grey screen, Then it may be an error in spelling or ing you GameViewController, In the SKNode Extension, Be sure to change

extension SKNode {
class func unarchiveFromFile(file : NSString) -> SKNode? {
if let path = NSBundle.mainBundle().pathForResource(file, ofType: "sks") {
var sceneData = NSData(contentsOfFile: path, options: .DataReadingMappedIfSafe, error: nil)!
var archiver = NSKeyedUnarchiver(forReadingWithData: sceneData)

archiver.setClass(self.classForKeyedUnarchiver(), forClassName: "SKScene")
let scene = archiver.decodeObjectForKey(NSKeyedArchiveRootObjectKey) as GameScene
archiver.finishDecoding()
return scene
} else {
return nil
}
}
}

to this..

 extension SKNode {
class func unarchiveFromFile(file : NSString) -> SKNode? {
if let path = NSBundle.mainBundle().pathForResource(file, ofType: "sks") {
var sceneData = NSData(contentsOfFile: path, options: .DataReadingMappedIfSafe, error: nil)!
var archiver = NSKeyedUnarchiver(forReadingWithData: sceneData)

archiver.setClass(self.classForKeyedUnarchiver(), forClassName: "SKScene")
let scene = archiver.decodeObjectForKey(NSKeyedArchiveRootObjectKey) as SKScene
archiver.finishDecoding()
return scene
} else {
return nil
}
}
}

We just edited this line of code,

 let scene = archiver.decodeObjectForKey(NSKeyedArchiveRootObjectKey) as GameScene

to this

 let scene = archiver.decodeObjectForKey(NSKeyedArchiveRootObjectKey) as SKScene

This will allow us to load multiple .sks files without any errors or crashes.

Load a spritekit scene from another bundle?

Just as I thought let gameSceneCoder = NSKeyedUnarchiver(forReadingWith: gameSceneData) was not creating a proper coder for you.

Just do

guard let scene = NSKeyedUnarchiver.unarchiveObject(with: gameSceneData) as? SKScene
else{
assert(false)
}

This will unarchive the file properly for you.

Note, if you want to use GameScene, make sure GameScene is set in the custom class of the SKS file

GameScene size is wider than my device screen size

Try to use viewWillLayoutSubviews instead of viewDidLoad and initialize your scene with skView.bounds.size:

override func viewWillLayoutSubviews() {
super.viewWillLayoutSubviews()

if let scene = GameScene.unarchiveFromFile("GameScene") as? GameScene {
// Configure the view.
let skView = self.view as SKView
skView.showsFPS = true
skView.showsNodeCount = true
skView.showsPhysics = true
skView.showsDrawCount = true

/* Sprite Kit applies additional optimizations to improve rendering performance */
skView.ignoresSiblingOrder = true

/* Set the scale mode to scale to fit the window */

if(skView.scene == nil){

scene.scaleMode = .AspectFill
scene.size = skView.bounds.size
skView.presentScene(scene)
}

}
}

In viewDidLoad the final size of view may not be know yet and viewWillLayoutSubviews is sometimes better place to initialize the scene.

When scene is loaded from .sks file it has default size of 1024x768. That's why your scene is sometimes wider than expected, and this can be changed like from the code I've posted.

If you have trouble with wrong view's size (but I doubt that) check your launch images...If those are wrongly selected, a view can have a wrong size.



Related Topics



Leave a reply



Submit