Get child node of SKReferenceNode in SpriteKit SWIFT - swift

I create a scene Case.sks (using Level Editor), inside one SKSpriteNode (name : square), and one SKLabel (name : label).
In my main scene, GameScene.sks, I use a SKReferenceNode with "Case" for reference.
I need to access to the "square" sprite from my main scene.
My first idea was to call directly the child node:
let firstSquare = childNode(withName: "square") as! SKSpriteNode
But I got :
Fatal error: unexpectedly found nil while unwrapping an Optional value
So I tried :
let caseRef = childNode(withName: "Case") as! SKReferenceNode
let firstSquare = caseRef.childNode(withName: "square") as! SKSpriteNode
But I got on the firstSquare line :
Fatal error: unexpectedly found nil while unwrapping an Optional value
How can get a child node of a reference scene ?

Try to call it with this code:
override func sceneDidLoad() {
if let sprite = self.childNode(withName: "//square") as? SKSpriteNode {
// do whatever you want with the sprite
}
...
}

Related

Found nil when unwrapping texture value which I have previously set

so I am trying to add an SKSpritenode to my scene level2, and I want to give it a physics body with a texture that comes from an image I have uploaded into the assets folder of my project. the image is there and the name is right, but when launch the app I get a fatal error. I don't get where I'm going wrong as the app used to run smoothly until I added that last line of code.
import UIKit
import SpriteKit
import GameplayKit
class level2: SKScene {
var entities = [GKEntity]()
var graphs = [String : GKGraph]()
var perno = SKSpriteNode(fileNamed: "perno")
override func sceneDidLoad () {
print ("view2 Loaded")
self.perno = self.childNode(withName: "perno") as? SKSpriteNode
self.perno?.texture = SKTexture(imageNamed: "perno")
self.perno?.size.width = self.perno!.size.width * 5
self.perno!.physicsBody = SKPhysicsBody(texture: (perno?.texture)!, size: (perno?.texture?.size())!)
}
any hints?
Thanks!
Your optional handling is really inconsistent. Sometimes you use optional casting and optional chaining just to then use force unwrapping on the same variable in the same line. You should force unwrap the return value of SKSpriteNode(fileNamed: "perno"), since if that returns nil that's a programmer error and should be caught ASAP.
Your immediate issue seems to be that you try to overwrite perno in sceneDidLoad:
self.perno = self.childNode(withName: "perno") as? SKSpriteNode
There's no childNode added to your scene, so you just set perno to nil instead of its previously set value using var perno = SKSpriteNode(fileNamed: "perno"). Simply remove that line from sceneDidLoad, since it makes no sense, perno is already loaded during initialisation, since you assign a value to it.
class Level2: SKScene {
var entities = [GKEntity]()
var graphs = [String : GKGraph]()
let perno = SKSpriteNode(fileNamed: "perno")!
override func sceneDidLoad () {
print ("view2 Loaded")
self.perno.texture = SKTexture(imageNamed: "perno")
self.perno.size.width = self.perno.size.width * 5
self.perno.physicsBody = SKPhysicsBody(texture: perno.texture!, size: perno.texture!.size())
}
}
A better way to protect yourself is to use the guard statement. guard allows you to safely check if a variable is not nil without having to nest your code.
class Level2: SKScene {
var entities = [GKEntity]()
var graphs = [String : GKGraph]()
let perno = SKSpriteNode(fileNamed: "perno")!
override func sceneDidLoad () {
print ("view2 Loaded")
guard let texture = SKTexture(imageNamed: "perno")
else{
fatalError("Unable to find texture "perno")
}
self.perno.texture = texture
self.perno.size.width = self.perno.size.width * 5
self.perno.physicsBody = SKPhysicsBody(texture: texture, size: texture.size())
}
}
As you can see, we are not excessively unwrapping self.perno.texture, which means there will be less chances for us to accidentally use a ? instead of a !, and we have a clear cut error as to why our code is crashing.

swift - use an integer for loop in a variable's name

I' like to doe something like this :
for i in 0...3 {
let skspritenode\(i) = SKSpriteNode(imageNamed: "Layer_\(i)")
self.addChild(skspritenode\(i))
}
that doesn't work because my skspritenode(i) is a let and not a string
and I have tried this:
for i in 0...3 {
let skspritenode = SKSpriteNode(imageNamed: "Layer_\(i)")
self.addchild(skspritenode)
}
which doesn't work because skspritenode already has a parent.
Is there a way of doing this in a for loop or do i have to declare each let separately? Thank you in advanced
Why would you need to name each node differently? skspritenode variable is scoped in for loop, thus the following code should work just fine:
for i in 0...3 {
let skspritenode = SKSpriteNode(imageNamed: "Layer_\(i)")
self.addchild(skspritenode)
}
Your point
which doesn't work because skspritenode already has a parent
is incorrect, since you're creating new SKSpriteNode on each iteration.

Init subclass of SKSpriteNode using .sks file Xcode 8

In my GameScene.sks I have a SKSpriteNode which represents a "ball".
I need to subclass SKSpriteNode as Ball:
class Ball:SKSpriteNode {
//custom init
}
In my scene, I would like init my Ball using the SKSpriteNode in the .sks.
As I'm using Xcode 8, I tried to use the custom class in my SKSpriteNode:
self.ball = self.childNode(withName: "ball") as! Ball
But my app crashes at this line...
Also I'm not sure how to create a custom initializer for my subclass.
FYI I would prefer to avoid having something like:
class Ball:SKNode {
var sprite:SKSpriteNode!
}
let ball = Ball()
ball.sprite = self.childNode(withName: "ball") as? SKSpriteNode
I think the problem here is that you are not assigning a name to the sprite you add in the scene editor.
You could retrieve your sprite searching for type instead that searching for name
Try this
self.ball = self.children.flatMap { $0 as? Ball }.first!

Change texture of SKNode that is an SKSpriteNode

In a game I'm making I can get the node the user is touching with
var node = self.nodeAtPoint(positionInScene)
However, this gets me an SKNode (even if the node the user touches is an SKSpriteNode) and I cannot use node.texture to change its texture. How can I change that SKNode to an SKSpriteNode or change its texture? (Actions won't work here because I need to pause the scene and they won't work when the scene is paused).
Casting is what you are looking for.
For instance:
If you are sure that the casting will be successful and your SKNode() is actually a SKSpriteNode, you can do the following:
let s = SKNode()
let ss: SKSpriteNode = (s as? SKSpriteNode)!
However, it is always better to use the optional specially in casting:
let ss2: SKSpriteNode? = s as? SKSpriteNode

Scene created in Sprite kit level editor is not working

I'm trying to do this for a while. I have a main scene for a game named PlayScene. I have a pause button there. When player is tapping that button I want to load another scene, named PauseScene. For visualy creating that scene I use Sprite kit level editor. So I have two files PauseScene.swiftand PauseScene.sks. But .sks content ( just a background for now) isn't unarchiving. I don't really know where could I make a mistake.
So this is my transition via pause button, located in PlayScene
for touch: AnyObject in touches {
let location = touch.locationInNode(self)
if self.nodeAtPoint(location) == self.pause {
var scene = PauseScene(size: self.size)
scene.paused = true
let skView = self.view as SKView!
skView.ignoresSiblingOrder = true
scene.scaleMode = .AspectFill
scene.size = skView.bounds.size
skView.presentScene(scene)
}
}
And this is what I have in PauseScene.swift:
class PauseScene: SKScene {
override func didMoveToView(view: SKView) {
self.initPauseScene()
}
func initPauseScene() {
}
}
I tried this and it's not working very well. However that tutorial is closest I could get with my problem. It's not crushing and isn't showing any errors, but when I tap pause button it's transitioning to that scene without my background, just standard grey scene. This answer is not working for me. First suggestion show errors and when I tried second and tap the pause – game is crushing.
I don't know, that level editor is made for easier work but for know it just doubles it for me. I even thought that problem is in my background image maybe. But its standard .png. Why my content isn't showing up?
So I tried to copy GameViewController default code for loading GameScene and ended up with crashing. It's not the first time I have that error while trying to unarchive this. What does it mean? I got this when i tried to replace var scene = PauseScene(size: self.size) with a if let scene = PauseScene.unarchiveFromFile("PauseScene") as? PauseScene
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.