2

I'm having trouble with implementing a button that will take you to the main menu from the GameScene.

This is my GameScene code:

let stoneLayer: SKNode = SKNode()
let cardLayer: SKNode = SKNode()
let menuPausaLayer: SKNode = SKNode()

class GameScene: SKScene {

override func didMoveToView(view: SKView) {
    /* Setup your scene here */
    backgroundColor = SKColor.blackColor()

    addChild(stoneLayer)
    addChild(cardLayer)
    addChild(menuPausaLayer)

    //Creazione posizioni carte
    for i in 0...15 {
        let cardLocation = SKSpriteNode(color: .orangeColor(), size: CGSize(width: cardWidth, height: cardHeight))
        cardLocation.position = CGPointMake(cardLocPosition[i].x, cardLocPosition[i].y)
        cardLocation.texture = SKTexture(imageNamed: "location")
        cardLocation.zPosition = -90
        addChild(cardLocation)
        //grid[i] = 0
    }

    //Pulsanti
    pauseButton.position = pauseButtonPosition
    addChild(pauseButton)


    loadDeck()

}

When I pause the Game, I add the exitButton to the menuPausaLayer. Here the code i use for this button:

let exitButton = button(buttonTexture: "pauseMenuButton", 
                        buttonWidth: menuPausaBottonWidth, 
                        buttonHeight: menuPausaBottonHeight, 
                        action: {exit()}
})

func exit() {
    print("Back to Main Menu")

    let newScene = MainMenuScene(size: UIScreen.mainScreen().bounds.size)
    newScene.scaleMode = .AspectFill
    scene?.view?.presentScene(newScene)   
}

But it did not works... There is a way to create a function to change scene outside of the SKScene class?? Thanks in advance :)

UPDATE: Here the custom class i use for the button:

class button: SKSpriteNode {

//NSCoder non supportato
required init(coder aDecoder: NSCoder) {
    fatalError("NSCoding not supported")
}

var buttonTexture: SKTexture
var buttonWidth: CGFloat
var buttonHeight: CGFloat
var action: () -> Void

init(buttonTexture: String, buttonWidth: CGFloat, buttonHeight: CGFloat, action: () -> Void) {

    self.buttonTexture = SKTexture(imageNamed: buttonTexture)
    self.buttonWidth = buttonWidth
    self.buttonHeight = buttonWidth
    self.action = action

    super.init(texture: self.buttonTexture, color: .whiteColor(), size: CGSize(width: buttonWidth, height: buttonHeight))
    userInteractionEnabled = true
}

override func touchesBegan(touches: Set<UITouch>, withEvent event: UIEvent?) {
    for _ in touches {

        let scale = SKAction.scaleTo(1.1, duration: 0.1)
        runAction(scale)
    }
}

override func touchesEnded(touches: Set<UITouch>, withEvent event: UIEvent?) {
    for touch in touches {

        let location = touch.locationInNode(scene!)
        let touchedNode = nodeAtPoint(location)

        let scale = SKAction.scaleTo(1.0, duration: 0.1)
        runAction(scale)

        if touchedNode.containsPoint(location) {
           action()
        }

    }
}
Fabio
  • 23
  • 4

3 Answers3

1

As explained to the Apple documents, SKNode has also an attribute named parent so SKSpriteNode is inherited from SKNode then in your class button: SKSpriteNode (that in your code is directly added to the scene) you can do:

if let parent = self.parent where parent is SKScene {
   let pScene = parent as! SKScene
   // do whatever you want with your scene
}
Alessandro Ornano
  • 34,887
  • 11
  • 106
  • 133
  • 1
    Thanks for the help! I've putted in the exit() function the following code: if let parent = exitButton.parent where parent is SKScene { let scene = parent as! SKScene ... etc etc ... ... } and it works properly! – Fabio Aug 03 '16 at 00:16
  • This is totally incorrect. To get to a scene from a node, you use the "scene" property of a node, which will be nil if the node is not part of a scene `if let pScene = scene {...` – Daniel K Aug 03 '16 at 18:30
  • This is correct, the node is a button directly added to the self.scene according to the OP code so scene is the parent, have you read the OP code? – Alessandro Ornano Aug 03 '16 at 18:35
0

You really should not be accessing ViewController related actions from an SKScene, which is what you want to do. Instead, you should have UI related actions in your storyboard.

However, you can get to the parent view controller from an SKScene.

Using This example you can get the view controller as follows:

extension UIView {
    var parentViewController: UIViewController? {
        var parentResponder: UIResponder? = self
        while parentResponder != nil {
            parentResponder = parentResponder!.nextResponder()
            //swift 3: parentResponder = parentResponder!.next()
            if let viewController = parentResponder as? UIViewController {
                return viewController
            }
        }
        return nil
    }
}

Then in your Game Scene you would use

(view!.parentViewController as! GameViewController).dismiss(animated: true, completion: nil)

In one node, you would use:

(scene!.view!.parentViewController as! GameViewController).dismiss(animated: true, completion: nil)

Or you could add a custom function to your view Controller to do the above.

Swift 3 code is in the comments above

Again, not recommended. You should storyboard your UI and leave the game scene to play the game. A navigation Controller is an ideal way to do that.

Community
  • 1
  • 1
Daniel K
  • 1,119
  • 10
  • 20
  • First of all OP speak about SKNode parent, not SKScene. You also cannot have UI related actions in storyboard using SpriteKit, you cannot have directly a storyboard and all you have can work better. Your example is related to UIKit , nothing concerned about this question. A navigationController for what? Swift 3?? Can you explain better your answer? – Alessandro Ornano Aug 02 '16 at 17:35
  • I have updated to get from node to scene. Which is just `node.scene?`. You absolutely can have UI elements over a sprite kit scene. You must include in your storyboard a view for the game (class SKView) and also any other elements you like OVER the skview. Skview is just ONE view out of many in the view controller. My advice is to create another view and show it when you need it. Clear? – Daniel K Aug 03 '16 at 18:28
  • I've my games since the first sprite-kit version without storyboard, I don't really know what do you talking about.Why you need another view together with SKView, sorry but I can continue to not understand.. – Alessandro Ornano Aug 03 '16 at 18:39
0

Using the following function all works properly! Thanks!

func exit() {
print("Back to Main Menu")

if let parent = exitButton.parent where parent is SKScene {
    let parentScene = parent as! SKScene
    // do whatever you want with your scene
    print ("YES")

    let skView = parentScene.view! as SKView
    skView.showsFPS = true
    skView.showsNodeCount = true
    skView.multipleTouchEnabled = false

    /* Sprite Kit applies additional optimizations to improve rendering performance */
    skView.ignoresSiblingOrder = true

    /* Set the scale mode to scale to fit the window */
    let mainMenuScene = MainMenuScene(size: skView.bounds.size)
    mainMenuScene.scaleMode = .ResizeFill //.AspectFill

    skView.presentScene(mainMenuScene)

}
Fabio
  • 23
  • 4