Load a Spritekit Scene from Another Bundle

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

How can I load a spritekit file different than GameScene.swift in my GameViewController.swift?

if you are in one scene for example level selection scene, lets say LevelSelectionScene.swift, you can go to another scene via SKView's -presentScene:transition: method.

class LevelSelectionScene: SKScene {

override func didMoveToView(view: SKView) { /* ... */}

func selectLevel(level: Int) {
let fadeTransition = SKTransition.fadeWithDuration(0.3)
if let selectedLevel = createLevelSceneWithLevel(level) {
self.view?.presentScene(selectedLevel, transition: fadeTransition)
}
}

// Not a good idea if you progressively adding new levels,
// it's totally depend on how you gonna organize your levels.
// Since its level input is not arbitrary, the output of this
// rarely nil, if it does, it must be the developer mistake.
func createLevelSceneWithLevel(level: Int) -> SKScene? {
let levelScene: SKScene?
switch level {
case 1: levelScene = Level1()
case 2: levelScene = Level2()
default: levelScene = nil
}
return levelScene
}

}

Load SpriteKit levels from filename

This can be done using NSClassFromString:

For Swift 2:

extension SKScene {
static func sceneWithClassNamed(className: String, fileNamed fileName: String) -> SKScene? {
guard let SceneClass = NSClassFromString("Your_App_Name.\(className)") as? SKScene.Type,
let scene = SceneClass.init(fileNamed: fileName) else {
return nil
}

return scene
}
}

Or Swift 1:

extension SKScene {
static func sceneWithClassNamed(className: String, fileNamed fileName: String) -> SKScene? {
if let SceneClass = NSClassFromString("Your_App_Name.\(className)") as? SKScene.Type,
let scene = SceneClass(fileNamed: fileName) {
return scene
}

return nil
}
}

Usage:

if let scene = SKScene.sceneWithClassNamed("MyScene", fileNamed: "MyScene") {
view.presentScene(scene)
}

Its important to note that for this to work correctly your SKScene subclass must implement init(coder aDecoder: NSCoder), for example:

required init?(coder aDecoder: NSCoder) {
// ...
super.init(coder: aDecoder)
}

Accessing var from another class or scene in SpriteKit

So let's consider the first time the user selects player 1. You store the selected player as a Bool in NSUserDefaults. This value will persist in local storage until you change it, even after the app is closed. So the next time the user selects player 2, you set the Bool for playerSelect2. The problem is, that in Scene2 you start your if statement checking if playerSelect1 == true, which it is because you set it to true once and never changed it back. With your current method, you would have to set the desired playerSelect to true, but also reset all of the others to false. I would recommend changing your strategy to store a String corresponding to the selected player in NSUserDefaults. Something like this should get you started.

When you want to set the selected player:

if self.nodeAtPoint(location) == self.player1 {
NSUserDefaults.standardUserDefaults().setObject("playerSelect1", forKey: "selectedPlayer")
else if self.nodeAtPoint(location) == self.player2 {
NSUserDefaults.standardUserDefaults().setObject("playerSelect2", forKey: "selectedPlayer")
else if ...

When you want to determine which player was selected in Scene2:

//grab the value from local storage
let selectedPlayer = NSUserDefaults.standardUserDefaults().objectForKey("selectedPlayer") as! String

if selectedPlayer == "playerSelect1" {
//handle player 1 case
}
else if selectedPlayer == "playerSelect2" {
//handle player 2 case
}

SpriteKit scene editor

because the label is a child of player not self

childNode(withName: "playerHealthLbl") is the same as self.childNode(withName: "playerHealthLbl")

and self is the scene (in this instance), and scene doesn't have a child named
"playerHealthLbl"

so you want to do player.childNode(withName: "playerHealthLbl")

or childNode(withName: "//playerHealthLbl") which transverses through all children regardless of layer

Add SKReferenceNode/SKScene to another SKScene in SpriteKit

I do that, I don't know if is the best way to do this, but works:

I've 2 file Dragon.swift and sks

Sample Image

I've added a "main" node like DragonNode and other node children of this

Sample Image

Now, the DragonNode is a custom class, set it in sks file:

Sample Image

The DragonNode is a normal SKSpriteNode

class DragonNode: SKSpriteNode, Fly, Fire {

var head: SKSpriteNode!
var body: SKSpriteNode!
var shadow: SKSpriteNode!
var dragonVelocity: CGFloat = 250

required init?(coder aDecoder: NSCoder) {
super.init(coder: aDecoder)

//Example other node from sks file
body = self.childNodeWithName("Body") as! SKSpriteNode
head = body.childNodeWithName("Head") as! SKSpriteNode
shadow = self.childNodeWithName("Shadow") as! SKSpriteNode
shadow.name = "shadow"
}

//Dragon Func
func fireAction () {}
func flyAction () {}
}

Inside the scene, add a SKReferenceNode:

Sample Image

In the SKScene code:

    let dragonReference = self.childNodeWithName("DragonReference") as! SKReferenceNode

let dragonNode = dragonReference.getBasedChildNode() as! DragonNode
print(dragonNode)
//Now you can use the Dragon func
dragonNode.flyAction()

getBasedChildNode() is an extension to find your based node (the first one)

extension SKReferenceNode {
func getBasedChildNode () -> SKNode? {
if let child = self.children.first?.children.first {return child}
else {return nil}
}
}

How to load a scene async so you can have a loading screen?

You can schedule a block for concurrent execution using dispatch_async. Load scene in async block then perform callback method on the main thread like this:

__weak MyClass *weakself = self; 
dispatch_async(dispatch_get_global_queue( DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^(void){
//Background thread
//Load scene here
dispatch_async(dispatch_get_main_queue(), ^(void){
//Main thread
//Call your callback method here
[weakself sceneLoaded:loadedScene];
});
});


Related Topics



Leave a reply



Submit