andrello
andrello

Reputation: 121

SpriteKit Node gives me nil when subclassed (maybe?) to another node | Swift

Okay so i am trying to learn how to create a game... I want a node to be the camera, so that i can move it and center the view to my player node. When i subclass (i don't know if it's right to say that i subclass it, maybe not...) the player node to the world node, the application crashes. When player is simply a node, a "subclass" of GameScene (and not of the "world" node), it goes "fine", i mean i can move my player (yeah but the camera doesn't work).

so here is my code (few // lines in italian but not relevant)

import SpriteKit

class GameScene: SKScene {

var world: SKNode? //root node! ogni altro nodo del world dev'essere sottoclasse di questo
var overlay: SKNode? //node per l'HUD e altre cose che devono stare sopra al world
var camera: SKNode? //camera node. muovo questo per cambiare le zone visibili

//world sprites
var player: SKSpriteNode!

override func didMoveToView(view: SKView) {
    self.anchorPoint = CGPoint(x: 0, y: 0) //center the scene's anchor point at the center of the screen

    //world setup
    self.world = SKNode()
    self.world!.name = "world"
    self.addChild(world!)

    //camera setup
    self.camera = SKNode()
    self.camera!.name = "camera"
    self.world!.addChild(self.camera!)

    //UI setup
    self.overlay = SKNode()
    self.overlay?.zPosition = 10
    self.overlay?.name = "overlay"
    addChild(self.overlay!)

    player = world!.childNodeWithName("player") as SKSpriteNode!

}

var directionToMove = CGVectorMake(0, 0)

override func touchesBegan(touches: NSSet, withEvent event: UIEvent) {
    for touch: AnyObject in touches {
        let location = touch.locationInNode(self)

        directionToMove = CGVectorMake((directionToMove.dx + (location.x - player!.position.x)), (directionToMove.dy + (location.y - player!.position.y)))
    }
}

override func update(currentTime: CFTimeInterval) {
 //*these both print the string*
    if player == nil {
        println("player is nil")
    }
    if player?.physicsBody == nil {
        println("player.physicsBody is nil")
    }
 //*here it crashes*
    player!.physicsBody!.applyForce(directionToMove)
}

override func didFinishUpdate() {
    if self.camera != nil {
        self.centerOnNode(self.camera!)
    }
}

func centerOnNode(node: SKNode) {
    let cameraPositionInTheScene: CGPoint = node.scene!.convertPoint(node.position, fromNode: node.parent!)
    node.parent!.position = CGPoint(x: node.parent!.position.x - cameraPositionInTheScene.x, y: node.parent!.position.y - cameraPositionInTheScene.y)
}
}

thanks in advance : )

Upvotes: 0

Views: 709

Answers (2)

0x141E
0x141E

Reputation: 12773

Your player is nil because you are accessing it from the world node, which is empty. By default, the player is a child of scene. You can change this in the scene editor by setting the node's parent property (see Figure 1).

enter image description here

Figure 1. SpriteKit Scene Editor's Property Inspector

I suggest you make the following changes:

  1. Create the world, overlay, and camera nodes in the scene editor and access them with childNodeWithName
  2. Add the player to the scene, not the world, and have it fixed in middle of the scene
  3. Move the camera node not the player. The code in didFinishUpdate will automatically adjust the world to center the camera in the scene. You can add a physics body to the camera and move it by applying a force/impulse or by setting its velocity.
  4. Add the other sprites to the world (instead of to the scene)

If you add the other sprites to the world, they will move appropriately when you adjust the world's position to center the camera. You will then need to move the other sprites relative to the world. The scene is just a window to view a portion of the world at a time.

Upvotes: 1

hamobi
hamobi

Reputation: 8130

I'm not sure why you are using optionals everywhere. You only need to use an optional when a variable might be nil. Are your world, overlay, and camera ever going to be nil? I would guess probably not.

To answer your question: Youre trying to get player from world. I dont see that youve added any player sprite to the world node. It doesnt find it, and youre unwrapping an optional that isnt there. So you get an error.

You're going to have a lot more luck if you only use optionals when you need them. If you use them for everything youre going to add unnecessary complexity to your code.

Optionals should be the exception, not the rule. I could try to fix your code for you, but I'm not sure what you intended the player sprite to be?

Follow simple tutorials and start small. Things will make more and more sense over time.

Upvotes: 0

Related Questions