GlorySaber
GlorySaber

Reputation: 367

Load a spritekit scene from another bundle?

I am making a SpriteKit framework using swift 4.2 and want to include some .sks files for scenes and actions. I have tried to load the scene from the bundle using the code below:

class func newGameScene() -> GameScene {

guard let gameScenePath = Bundle(for: self).path(forResource: "GameScene", ofType: "sks") else { assert(false) }

guard let gameSceneData = FileManager.default.contents(atPath: gameScenePath) else { assert(false) }

let gameSceneCoder = NSKeyedUnarchiver(forReadingWith: gameSceneData)

guard let scene = GameScene(coder: gameSceneCoder) else { assert(false) }


// Set the scale mode to scale to fit the window
scene.scaleMode = .aspectFill

return scene

}

I load the scene and present it. (This code is mostly from Apple's template for SpriteKit as Im testing this issue.)

guard let view = view else {
      return nil
    }

    let scene = GameScene.newGameScene()

    view.presentScene(scene)

    view.ignoresSiblingOrder = true
    view.showsFPS = true
    view.showsNodeCount = true
    return nil

The GameScene.sks and the code is unchanged from Apples template in this case. This code and the .sks assets are in the dynamic framework and imported into another project.

When having the framework load the scene into a view I pass it, it shows the fps and node count but not the "Hello, World!" text.

In the code below, also copied from the template, a break point shows that these are not called when mousing down.

    #if os(OSX)
// Mouse-based event handling
extension GameScene {

  override func mouseDown(with event: NSEvent) {
    if let label = self.label {
      label.run(SKAction.init(named: "Pulse")!, withKey: "fadeInOut")
    }
    self.makeSpinny(at: event.location(in: self), color: SKColor.green)
  }

  override func mouseDragged(with event: NSEvent) {
    self.makeSpinny(at: event.location(in: self), color: SKColor.blue)
  }

  override func mouseUp(with event: NSEvent) {
    self.makeSpinny(at: event.location(in: self), color: SKColor.red)
  }

}
#endif

I know it must have to do with how SpritKit loads the scene but cannot find a solution. I have to use an NSKeyedUnarchiver becuase SpritKit's built in file initializer:

GameScene(fileNamed: "GameScene")

Only loads from the Main Bundle.

Now in the above I assumed that the file can be loaded by using a coder but Tomato made the point that sks most likely was not saved using a coder. In that case, It may be impossible to load an sks file from another bundle in sprite-kit using the provided api from apple. The answer may not include coders.

Upvotes: 2

Views: 1100

Answers (2)

ignkarman
ignkarman

Reputation: 41

I have compiled the above discussion/solution into a single extension function on SKScene. Hope this helps someone!

import SpriteKit

extension SKScene {
    static func fromBundle(fileName: String, bundle: Bundle?) -> SKScene? {
        guard let bundle = bundle else { return nil }
        guard let path = bundle.path(forResource: fileName, ofType: "sks") else { return nil }
        if let data = FileManager.default.contents(atPath: path) {
            return NSKeyedUnarchiver.unarchiveObject(with: data) as? SKScene
        }
        return nil
    }
}

Upvotes: 4

Knight0fDragon
Knight0fDragon

Reputation: 16827

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

Upvotes: 3

Related Questions