3

I'm trying to make a pause menu (like this), but if I use the same layer, I can't interact with the scene when it's paused (I guess?!). Everywhere I looked for "pause menu", people said to use another layer. I tried to do it a million times, but it doesn't work (or maybe I'm the one who doesn't know) =(

Thats what I've done so far:

**Declaring**

var gameLayer = SKNode()
var pauseLayer = SKNode()

var gameStarted = Bool()

var Ground = SKSpriteNode()
var Character = SKSpriteNode()

var pauseButton = SKSpriteNode()
var playButton = SKSpriteNode()


**Setup functions**

func setupGround(){

    //Ground
    Ground = SKSpriteNode(imageNamed: "Ground")
    Ground.setScale(0.5)
    Ground.position = CGPoint(x: self.frame.width / 2, y: 0)
    Ground.zPosition = 1

    Ground.physicsBody = SKPhysicsBody(rectangleOfSize: Ground.size)
    Ground.physicsBody?.categoryBitMask = PhysicsCategory.Ground
    Ground.physicsBody?.collisionBitMask = PhysicsCategory.Character
    Ground.physicsBody?.contactTestBitMask = PhysicsCategory.Character
    Ground.physicsBody?.affectedByGravity = false
    Ground.physicsBody?.dynamic = false
}
func setupCharacter(){

    //Character
    Character = SKSpriteNode(imageNamed: "Character")
    Character.size = CGSize (width: 60, height: 40)
    Character.position = CGPoint(x: self.frame.width / 2 - Character.frame.width - 100, y: self.frame.height / 2)
    Character.zPosition = 2

    Character.physicsBody = SKPhysicsBody(circleOfRadius: Character.frame.height / 2.5)
    Character.physicsBody?.categoryBitMask = PhysicsCategory.Character
    Character.physicsBody?.collisionBitMask = PhysicsCategory.Ground
    Character.physicsBody?.contactTestBitMask = PhysicsCategory.Ground
    Character.physicsBody?.affectedByGravity = false
    Character.physicsBody?.dynamic = true
    Character.physicsBody?.allowsRotation = false
}

func setupPauseButton (){

    //Pause
    pauseButton = SKSpriteNode (imageNamed: "pause")
    pauseButton.setScale(0.25)
    pauseButton.position = CGPoint(x: self.frame.width / 10, y: self.frame.height / 1.5)
    //pauseButton.zPosition = 3
}
func setupPlayButton(){

    //Play
    playButton = SKSpriteNode (imageNamed: "play")
    playButton.setScale(0.15)
    playButton.position = CGPoint(x: self.frame.width / 10, y: self.frame.height / 1.5)
    //playButton.zPosition = 3
}


**Creating scene**

func createScene(){

    self.physicsWorld.contactDelegate = self

    setupGround()
    gameLayer.addChild(Ground)

    setupCharacter()
    gameLayer.addChild(Character)

    setupPauseButton()
    gameLayer.addChild(pauseButton) //add pauseButton to gameLayer
}

func createPauseLayer(){

    setupPlayButton()
    pauseLayer.addChild(playButton) //add playButton to pauseLayer
}

override func didMoveToView(view: SKView) {
    /* Setup your scene here */

    self.addChild(gameLayer)

    createScene()
    createPauseLayer()
}


**Touches began**

override func touchesBegan(touches: Set<UITouch>, withEvent event: UIEvent?) {
    /* Called when a touch begins */

    //when touch buttons/screen
    for touch in touches {

        let location = touch.locationInNode(self)
        let node = nodeAtPoint(location)

        if node == pauseButton{
            pauseState()
        }

        else if node == playButton{
            playState()
        }

        else {

            if gameStarted == false{  //game didn't start yet

                gameStarted = true

                Character.physicsBody?.affectedByGravity = true //start falling when touch the screen

                //first jump
                Character.physicsBody?.velocity = CGVectorMake(0, 0)
                Character.physicsBody?.applyImpulse(CGVectorMake(0, 15))

                //first sound effect
                //self.runAction(SKAction.playSoundFileNamed("fly.mp3", waitForCompletion: false))
            }
            else{

                //jump
                Character.physicsBody?.velocity = CGVectorMake(0, 0)
                Character.physicsBody?.applyImpulse(CGVectorMake(0, 15))

                //sound effect
                //self.runAction(SKAction.playSoundFileNamed("fly.mp3", waitForCompletion: false))
            }
        }
    }
}

//states
func pauseState(){

    pauseButton.hidden = true //hide pauseButton
    gameLayer.paused = true //pause gameLayer
    self.addChild(pauseLayer) //add pauseLayer
}
func playState(){

    pauseButton.hidden = false //show pauseButton
    pauseLayer.removeFromParent() //remove pauseLayer
    gameLayer.paused = false //unpause gameLayer
}
Luiz
  • 1,275
  • 4
  • 19
  • 35

3 Answers3

4

I would not pause the scene, You should create your game setup like this

SKScene
--GameNode
----All other game related nodes
--OverlayNode
----Your Pause nodes

All the nodes you have besides the pause layer goes into the GameNode All the pause nodes go into the Overlay node

When you are ready to pause your scene, you actually pause your GameNode.

This keeps your scene alive, while your game is paused, so you can use the pause buttons.

When you are playing sounds, play them against nodes inside of the GameNode, not the scene, this way when you pause GameNode, the sounds automatically pause

class GameScene
{
  let pauseLayer = SKNode()
  let gameLayer = SKNode()
  func didMoveToView(view:SKView)
  {
    self.addChild(gameLayer)
  }

  func pauseButton()
  {
    gameLayer.paused = true
    self.physicsWorld.speed = 0;
    self.addChild(pauseLayer)
  }

  func unpauseButton()
  {
    pauseLayer.removeFromParent()
    self.physicsWorld.speed = 1;
    gameLayer.paused = false
  }
}
Knight0fDragon
  • 16,609
  • 2
  • 23
  • 44
  • Sorry for the newbie questions, as I said above, I'm new with coding. This GameNode and OverlayNode would be different layers, right? So they are independent? If it is, how do I create these layers? I tried to do it by using `pauseLayer` as SKNode and set `pauseLayer.something` to make a different layer for pause menu, but it didn't work =( – Luiz Apr 07 '16 at 13:54
  • I added some code to give you an idea on how it works – Knight0fDragon Apr 07 '16 at 13:58
  • I edited my question (code) with your suggestions, but I think I made something wrong because it isn't removing contents inside `pauseLayer` when `pauseLayer.removeFromParent()`. Ps: for start, I was trying to make the pause/play work with this; once it's working, then I can add the sound on/off. – Luiz Apr 07 '16 at 15:10
  • I don't know, is this what you said? `pauseLayer.addChild(playButton)`? I tried here but it is still crashing =( – Luiz Apr 07 '16 at 15:18
  • it is crashing? why is it crashing, what is the reason – Knight0fDragon Apr 07 '16 at 15:20
  • I edited the code because it wasn't showing pause/play properly. When I touch pause (change to play, but no action) touch play (change to pause, but no action) and then pause again, it crashes and shows me `Thread 1: signal SIGABRT` in appDelegate . – Luiz Apr 07 '16 at 15:29
  • you shouldnt be removing the pause button, paste your createscene code – Knight0fDragon Apr 07 '16 at 15:31
  • I pasted in the question – Luiz Apr 07 '16 at 15:38
  • when creating the scene, you do not add it to self, add it to gamelayer `gameLayer.addChild(Title)`...etc – Knight0fDragon Apr 07 '16 at 16:11
  • I do not see where you are building the pauseLayer though, is it just the button? add the button in the createScene function `pauseLayer.addChild(pauseButton)` – Knight0fDragon Apr 07 '16 at 16:13
  • It is inside PauseFunc. But pauseButton should be in gameLayer, shouldn't it? When I touch it, it'll show playButton, which is in pauseLayer. – Luiz Apr 07 '16 at 16:18
  • the pause button should be in game layer yes, I mean the unpause button – Knight0fDragon Apr 07 '16 at 16:25
  • I'm gonna try to insert these layers (changing addChild, etc) in the code here (there's much more than I pasted here hahah) and tell you if anything went wrong =) – Luiz Apr 07 '16 at 16:29
  • I'm trying to change it all but there are many thing that I don't know if I can change to `pauseLayer` (some `self.`, which are more than 100, for example). Isn't possible to create a new Swift file (or another file) where I could set this pause menu, and call from GameScene.swift when `node == pauseButton`? It would be a layer above GameScene (where I have my game layer). – Luiz Apr 07 '16 at 18:27
  • no, you can only have one scene running at any given time, I am not sure why you are having problems with this, it is a really easy concept, if you want to override SKNode for game layer and pause layer you can do that, but at your skill level, I think you will only be making things worse – Knight0fDragon Apr 07 '16 at 18:30
  • if you need to work through it, grab 2 pieces of paper, on one paper in the header write game layer, on the other write pause layer. Write all the sprites that go into game layer, write all the sprites that go into pause layer. Then in your create scene function, make sure that those sprites are added to the correct parent, if you see any self in the function, you know you have done something wrong. In the rest of the code, other than adding gamelayer in the beginning, the only node you should add / removal is pause layer, if you adding/removing any other node, you have done something wrong – Knight0fDragon Apr 07 '16 at 18:36
  • Thank you!! I'm gonna try it. But what is this `.self`? What does it refers to? – Luiz Apr 07 '16 at 18:37
  • self refers to whatever object you are inside of, currently your scene – Knight0fDragon Apr 07 '16 at 18:38
  • Would be the same to say that it refers to the currently active scene? – Luiz Apr 07 '16 at 18:40
  • no, an instance of `class GameScene` is currently your self, but if I go to a different class, `class GameNode`, self becomes an instance of this class. If you do not understand it, then you need to research tutorials to get a better understanding. – Knight0fDragon Apr 07 '16 at 18:44
  • I just understood!! Thank you =) – Luiz Apr 07 '16 at 18:45
  • Sorry for these newbie questions. I started coding for about 1 month only. – Luiz Apr 07 '16 at 18:45
  • Hi! I was trying to setup these layers you talked about, but it didn't work. I think I did everything right (I probably didn't). I edited my code up there with this new code. Would you mind taking a look? =) – Luiz Apr 10 '16 at 17:01
  • It didn't work. It isn't removing pauseButton when playButton is added (they're in the same position), but either way, the first pause (when game is loaded) isn't working =( – Luiz Apr 10 '16 at 18:59
  • and I had to move `gameLayer.addChild(pauseButton)` to touchesBegan, so pauseButton is added when game is started. – Luiz Apr 10 '16 at 19:04
  • dont do that, leave the pause in for now, figure out the problem first – Knight0fDragon Apr 10 '16 at 19:12
  • if play is over pause, your code should work correctly, use the hidden property when all is said and done – Knight0fDragon Apr 10 '16 at 19:13
  • oh dear god, you put functions inside of functions no wonder its not working – Knight0fDragon Apr 10 '16 at 19:32
  • Hi!! Sorry for taking so long to answer. I was a little busy. Just for know coding better, why can I not use a function inside another function? – Luiz Apr 11 '16 at 04:01
  • I tested the code but it didn't work. It still not pausing. After I tested as you edited, I added that hidden property you said above to hide the pauseButton -once playButton is over it- (tested again after using hidden property, it hide perfectly, but didn't pause). – Luiz Apr 11 '16 at 04:05
  • Hi! I even tried to use `.speed = 0` (or 1), instead of `.paused`, but it didn't work either =( – Luiz Apr 11 '16 at 17:40
  • do not use speed 0 or 1, you want pause, use pause. Learn how to use break points, step through your code, and find out why it is not working – Knight0fDragon Apr 11 '16 at 17:41
  • I tested it by all ways, and it's going through `gameLayer.paused = true` just well, but it's not pausing at all (at least no the character from jumping - `physicsBody?.applyImpulse`). I even moved `createPauseLayer()` from `didMoveToView` to `pauseState` (below `self.addChild(pauseLayer)`), so it will only add playButton (inside createPauseLayer) when this pauseState is called (when touch on `pauseButton`), and after `pauseLayer` is added. I'm trying to find it for about 4hrs and didn't find out the problem. Please, take a look on my code, I don't know what else to do. – Luiz Apr 11 '16 at 23:36
  • These audio files aren't being used by now. But then I can (I think?!) create a aux named audio as Boolean, so I that SKAction would only be played when audio was set to true. Anyway, I'm trying to make this pause/play work first, then I can move these sounds. – Luiz Apr 11 '16 at 23:42
  • no my phone sent me to the wrong message, share your project somewhere so i can see what you are doing wrong – Knight0fDragon Apr 11 '16 at 23:43
  • Yes, of course! Just uploaded [here](https://www.dropbox.com/sh/24mddrzjpx0i0iq/AAClnDsNJeD6hHvDWem4unota?dl=0). – Luiz Apr 11 '16 at 23:58
  • ahh yes, physics bodies to not listen to a nodes pause state, when pausing also set the physics world speed to 0 – Knight0fDragon Apr 12 '16 at 00:36
  • Just did this by adding `gameLayer.speed = 0` to `pauseState` and `gameLayer.speed = 1` to `playState`, but character is still not pausing. – Luiz Apr 12 '16 at 00:40
  • no, you need to pause the physics world, see my code edits and take your changes out – Knight0fDragon Apr 12 '16 at 00:41
  • I didn't find the edit. Where is it? here or dropbox? – Luiz Apr 12 '16 at 00:45
  • After 40 messages, it finally worked!! Thank you for your time and attention =) – Luiz Apr 12 '16 at 00:49
  • I didn't pay attention to the fact you were using the physics engine, I was too focused on pausing the audio – Knight0fDragon Apr 12 '16 at 00:50
  • That's my next step haha. Thank you!! – Luiz Apr 12 '16 at 00:52
  • this should pause the audio for you – Knight0fDragon Apr 12 '16 at 00:52
  • remember to run the audio against game layer, not the scene – Knight0fDragon Apr 12 '16 at 00:54
  • It's actually a audio "switch". I think that creating a aux variable, for example, `audio` as boolean would let me set audio to true or false when game is paused. This way whenever `pauseButton` is touched, it would add `mute` and `unmute` buttons which would be used to set `audio` to true or false. – Luiz Apr 12 '16 at 00:59
  • That SKAction to play the sound file would be inside a `If audio = true` then play. – Luiz Apr 12 '16 at 01:00
  • I figured you would pause the audio when the game is paused. if you want a mute button, just add a subnode to the gameLayer, and pause the subnode – Knight0fDragon Apr 12 '16 at 01:00
  • to gameLayer? Shouldn't I add to pauseLayer? Sorry, I didn't get it. – Luiz Apr 12 '16 at 01:06
  • no, the sound that plays is on game layer if you need to stop the sound mid way, then make sure that the sound you are playing is on a sub node, and pause the sub node when needed – Knight0fDragon Apr 12 '16 at 01:07
  • How would these subnodes work? I don't know much about it. – Luiz Apr 12 '16 at 01:10
  • Hi! I found a bug =( – Luiz Apr 19 '16 at 10:04
  • If, when game is launched, I touch pause (`gameLayer.paused == true`), leave the game (home button - not terminated) and come back without touching `unpauseButton`, `gameLayer.paused` turns to `false` out of nowhere. – Luiz Apr 19 '16 at 10:05
  • I basically printed "gameLayer.paused == true" if (if statement) it was true and "gameLayer.paused == false" if it was false. – Luiz Apr 19 '16 at 10:10
  • http://stackoverflow.com/questions/33179409/spritekit-keep-the-game-paused-when-didbecomeactive – Knight0fDragon Apr 19 '16 at 10:16
  • I'm trying to figure out what goes inside `func CBApplicationDidBecomeActive()`. What would be `//Other code`? Everything? Even `func pauseGame`? I didn't understand much of that part. =( – Luiz Apr 19 '16 at 10:41
  • Nothing goes inside it – Knight0fDragon Apr 19 '16 at 10:43
  • Ok. Another question: since I already have a pauseGame function called `pauseState()`, should I create another one (with the same content of `pauseState()`) in order to write that `notification:NSNotification` inside of the parenthesis? – Luiz Apr 19 '16 at 10:48
  • It wouldn't run I if didn't do it, so added one more function called `keepPauseState`, but when opening the game again, it crashes. – Luiz Apr 19 '16 at 11:40
  • Ok, sorry. I'm gonna start on researchs. – Luiz Apr 19 '16 at 13:54
  • Hi! Instead of flooding this question with a different subject, I created another [question](http://stackoverflow.com/questions/36782589/keep-layer-paused-when-resigning-active) explaining better what's happening. If you can take a look in there, I'd be appreciated =) (I did researched on NSNotification in order to save gameLayer pause state, but it was still changing the state of it) – Luiz Apr 22 '16 at 03:30
2

I think the best way to do it is to follow this stackoverflow answer (in other words to use SKTAudio class) where you can declare a sharedInstance audio library to easily manage all audio actions ..

With this class you can do for example about your question:

var audioManager : SKTAudio = SKTAudio.sharedInstance()

self.audioManager.pauseBackgroundMusic()
Community
  • 1
  • 1
Alessandro Ornano
  • 34,887
  • 11
  • 106
  • 133
  • But I need to change that `audio` to false, so the soundEffect won't be played. I guess a way to do it is to create a new layer, but I don't know how "insert" a boolean (`audio`) in it. For example, there will be a restart button in my pause menu, but I can't touch it when the scene is paused because `.paused = true` paused everything (I guess?!). – Luiz Apr 07 '16 at 13:40
  • So, simple store your current volume value, change volume to zero and when you want to restart you can retrieve last volume value – Alessandro Ornano Apr 07 '16 at 13:45
  • Sorry, I'm kinda new with coding, so I don't know much about it. How do I change the volume value? The restart button I mentioned above, is just like [this](http://www.ludumdare.com/compo/wp-content/uploads/2011/10/pause-menu.png) one. – Luiz Apr 07 '16 at 13:48
  • You're welcome, as you can see from the official reference: https://developer.apple.com/library/ios/documentation/AVFoundation/Reference/AVAudioPlayerClassReference/ there is a volume property that you can easily set with the float range 0.0(min) - 1.0(max) – Alessandro Ornano Apr 07 '16 at 14:14
0

I would use AVAudioPlayer, you can start and stop sounds easily. I've never used SKAction.playSoundFileNamed so I'm not sure if it's possible to stop.

Echizzle
  • 3,359
  • 5
  • 17
  • 28
  • I edited my question in order to be more specific. I tried to use a layer so my pauseLayer weren't paused when I pause the main game scene (didn't work. I might've done something wrong). But AVAudioPlayer doesn't pause when the scene is paused? – Luiz Apr 07 '16 at 04:00