Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Creating a custom initalizer for SKScene that overrides convenience init?(fileNamed:)

I'm trying to create a convenience initializer that overrides the convenience init? (fileNamed:) initializer in SKScene so that I can pass some initial values to the scene while also unarchiving the .sks file. The problem is that when I try to do this, it seems that the subclass of SKScene (GameScene) is unable to see the convenience init? (fileNamed:) of the superclass. Here are some of my attempts:

Class GameScene : SKScene {

var stage : Int?

override init(size: CGSize) {
    super.init(size: size)
}

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

convenience init?(fileNamed: String, stage: Int) {
    self.init(fileNamed: fileNamed)  // Compiler error-- Argument labels '(filenamed:)' do not match any available overloads
    self.stage = stage
}

Another attempt I found suggested as a workaround:

Class GameScene : SKScene {

var stage : Int?

override init(size: CGSize) {
    super.init(size: size)
}

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

convenience init?(fileNamed: String) {
    self.init(fileNamed: fileNamed) // Error at run time: EXC_BAD_ACCESS (code=2, address=0x16fc1bff0) 
}

convenience init?(fileNamed: String, stage: Int) {
    self.init(fileNamed: fileNamed)  
    self.stage = stage
}

The debugger reveals an endless loop of GameScene.init(fileNamed : String) -> GameScene?

How do I accomplish this? I need to move on with my life!! (and this project...)

like image 912
derekFairholm Avatar asked Feb 13 '17 17:02

derekFairholm


2 Answers

Couldn't it be as simple as this?

if let gameScene = GameScene(fileNamed: "GameScene") {

        self.gameScene = gameScene
        self.gameScene.stage = 1
        self.gameScene.setupBasedOnStage()
        self.gameScene.scaleMode = .aspectFill
        self.gameScene.gameSceneDelegate = self.menuSceneDelegate as! GameSceneDelegate!
        self.view?.presentScene(self.gameScene, transition: SKTransition.reveal(with: .down, duration: 1.0))
    }

You are able to set the stage property before revealing the page, and if you needed to you can call a setup function to load info/graphics based on the stage.

I know it's not as elegant as what you are trying to do, but maybe sometimes the easiest answer is the best?

like image 88
Ron Myschuk Avatar answered Nov 15 '22 10:11

Ron Myschuk


Swift has rules around convenience initializers:

Rule 1: A designated initializer must call a designated initializer from its immediate superclass.

Rule 2: A convenience initializer must call another initializer from the same class.

Rule 3: A convenience initializer must ultimately call a designated initializer.

The initializer public convenience init?(fileNamed filename: String) is itself a convenience initilizer on SKNode, so attempting to call it from your own convenience initializer breaks Rule 2.

If you see in the Xcode quick tips, the only initializer available to call from your convenience initializer would be required init?(coder aDecoder: NSCoder) which satisfies Rule 3.

like image 42
Mark Brownsword Avatar answered Nov 15 '22 09:11

Mark Brownsword