Sam Sam - 3 months ago 14
Swift Question

Pass current GameScene instance into a class called within GameScene

I'm building a

PauseButton
class in my SpriteKit game, and I want to draw a label to the
GameScene
when it is activated. However, I'm required to instantiate the button outside of
didMoveToView
(right before it- in
GameScene
) so that I can access it more "globally" from the
touchBegan
method.

let pauseButton = PauseButton(theTexture: pauseButtonTexture,gameScene:GameScene)
override func didMoveToView(view: SKView) {


Ideally I would be able to pass in the view by instantiating it within
didMoveToView
but as of now I don't know how to do this and also access it from the rest of the scene. That said, is there a way to pass in the current
GameScene
instance I'm working with so that I can do something like
gameScene.addchild(label)
from inside a method of my button class? I couldn't find anything useful on this, so any help would be great!

Answer

You basically can do it 2 ways.

1) Just create the property like this

var pauseButton: PauseButton!

and than instantiate it in didMoveToView like so.

override func didMoveToView(view: SKView) {
     pauseButton = PauseButton(theTexture: pauseButtonTexture,gameScene: self)
     addChild(pauseButton)
}

2) Use lazy instantiation so you can do what you tried to do initially.

A lazy var in in simplest form allows you to use self without doing step 1.

lazy var pauseButton: PauseButton = PauseButton(theTexture: pauseButtonTexture,gameScene: self)

override func didMoveToView(view: SKView) { 

You could even do it like so to add more properties of this button all in the same spot.

lazy var pauseButton: PauseButton = {
       let button = PauseButton(theTexture: pauseButtonTexture,gameScene: self)
       button.zPosition = 200
       button.position = CGPoint(x: self.frame.midX, y: self.frame.midY)
       button.alpha = 0.5

       return button
}()

override func didMoveToView(view: SKView) {
        addChild(pauseButton)
}

Remember every SKNode/SKSpriteNode etc has a scene property. If your button class is a subclass of SKNode/SKSpriteNode than you can get its scene property and use the "as" operator to get a reference to GameScene without passing in the scene in the initialisation method.

e.g

class GameScene: SKScene {

       lazy var pauseButton: PauseButton = PauseButton(theTexture: pauseButtonTexture,gameScene: self)

       var someBool = false

       override func didMoveToView(view: SKView) {
            addChild(pauseButton)
            pauseButton.loadPausedLabel()
       }

       func someMethod() {

       }    
 }

class PauseButton: SKSpriteNode {

      func loadPauseLabel() {

            // Way 1, reference to current scene but not to specific class

            guard let scene = scene else { return } 
            // Scene property is optional and is nil until button is added to a Scene so add the guard check.


            scene.someBool = true      // NOT WORKING
            scene.someMethod()         // NOT WORKING

            let label = SKLabelNode(...
            scene.addChild(label)      // WORKING

            // Way 2, use "as" to cast to specific scene (e.g GameScene) so you can call properties/methods on it. This is the same as passing GameScene in the init method.

            guard let gameScene = scene as? GameScene else { return } 
            // Scene property is optional and is nil until button is added to a Scene so add the guard check.
            // The button must be added to the scene you are trying the as? cast with, in this example GameScene. 


            gameScene.someBool = true      // WORKING
            gameScene.someMethod()         // WORKING

            let label = SKLabelNode(...
            gameScene.addChild(label)      // WORKING
      }
}

As a tip, I would not add the pause label in the pauseButton subclass, I would add it from within the scene it self or from a pause menu subclass. Also I would not subclass each button, I would crate 1 class that can be used for all buttons in your game. You could create an enum with button names and pass that into the init method to distinguish between your buttons when they are pressed.

Your button subclass should only handle animations, textures etc. You can check out apples game DemoBots for an example.

Hope this helps

Comments