Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How do you get the frame.width of GameScene in a custom class?

I'm having trouble trying to get the frame.width of GameScene into a custom class because I need the scene width for positioning nodes in my custom class init.

HudController.swift snippet

import SpriteKit

    class HudController:SKNode {
        var width:CGFloat

        override init(){
            width = GameScene().frame.width //I was hoping to add the scene width here
        }
    }

The following code crashes on me and I've tried heaps of other solutions with no success.

Can someone please help me with this issue?

Thanks!

UPDATED CODE ===

GameScene.swift

import SpriteKit

class GameScene: SKScene {
    var hud = HudController(gameScene: self) 
    // set as global because I'm using it to also call hud functions in my game

}

HudController.swift

import SpriteKit

class HudController:SKNode {

    var width:CGFloat

    init(gameScene:SKScene){
        width = gameScene.size.width
    }

    required init?(coder aDecoder: NSCoder) {
        fatalError("init(coder:) has not been implemented")
    }
}
like image 770
Troy R Avatar asked Feb 09 '23 20:02

Troy R


1 Answers

Every node in SpriteKit can retrieve the scene where it lives using the scene property.

let node = SKNode()
node.scene

The scene property returns a SKScene?, the value is an optional because if the current node does not belong to a scene, of course there is not scene to retrieve.

Now, you could be tempted to use the self.scene property inside the initializer of your HudController.

class HudController: SKNode {
    var width:CGFloat

    override init() {
        super.init() //  error: property 'self.width' not initialized at super.init call
        width = self.scene!.frame.width
    }

    required init?(coder aDecoder: NSCoder) {
        fatalError("init(coder:) has not been implemented")
    }
}

But it will now work because you cannot invoke super.init before having initialized the width property.

Fine, then you could try this.

class HudController: SKNode {
    var width:CGFloat

    override init() {
        width = self.scene!.frame.width // error: use of 'self' in property access 'scene' before super.init initializes self

        super.init()
    }

    required init?(coder aDecoder: NSCoder) {
        fatalError("init(coder:) has not been implemented")
    }
}

Again compiler error because you are using self before having called super.init().

And finally there is another problem, when the initializer is executed, the node it is being created has not a parent yet, so the self.scene property does return nil.

How to solve this?

Solution

First of all declare you initializer to receive a GameScene.

class HudController: SKNode {
    var width: CGFloat

    init(gameScene:SKScene) {
        width = gameScene.frame.width
        super.init()
    }

    required init?(coder aDecoder: NSCoder) {
        fatalError("init(coder:) has not been implemented")
    }
}

Now, when you create this object (I hope you are doing it from the scene or from a node already added to the scene otherwise we are in trouble) you simply write

class Foo : SKNode {

    func foo() {
        guard let scene = self.scene else { return }
        let controller = HudController(gameScene: scene)
    }
}

On the other hand if you are creating HudController from you scene you simply write:

class MyScene : SKScene {

    override func didMoveToView(view: SKView) {
        super.didMoveToView(view)
        let controller = HudController(gameScene: self)
    }
}

That's it.

Update

If you want an instance property of HudController inside your GameScene this is the code.

class HudController: SKNode {
    var width: CGFloat

    init(gameScene:SKScene) {
        width = gameScene.frame.width
        super.init() // <-- do not forget this
    }

    required init?(coder aDecoder: NSCoder) {
        fatalError("init(coder:) has not been implemented")
    }
}

class GameScene: SKScene {
    var hud : HudController?

    override func didMoveToView(view: SKView) {
        super.didMoveToView(view)
        hud = HudController(gameScene: self)
    }
}
like image 91
Luca Angeletti Avatar answered May 31 '23 00:05

Luca Angeletti