Can't hide or unhide GameScene elements from touchNode input

Hello,


I am making my first game and am running into an issue. I have a pause button in my game and when it's pressed I want my "pauseMenu" to unhide, but it's not working. Here's code from my GameScene:


if touchNode == pauseButton {

pauseGame()

}


func pauseGame() {

self.view?.isPaused = true

print("Before isHidden is set to false: \(pauseMenu?.isHidden)")

pauseMenu?.isHidden = false

print("After isHidden is set to false: \(pauseMenu?.isHidden)")

}

Here's what prints:

Before isHidden is set to false: Optional(true)

After isHidden is set to false: Optional(false)


As you can see, the pauseGame() function gets called successfully, and the view pauses, but the pauseMenu stays hidden. It's weird because it successfully unhides if I call pauseMenu?.isHidden = false elsewhere, like in my function that is called when the game ends:


func gameOver() {

...

toggleViews()

}


func toggleViews() {

...

print("Before isHidden is set to false: \(pauseMenu?.isHidden)")

pauseMenu?.isHidden = false

print("After isHidden is set to false: \(pauseMenu?.isHidden)")

}

This successfuly unhides the pauseMenu and prints the same code:

Before isHidden is set to false: Optional(true)

After isHidden is set to false: Optional(false)


I think the problem has something to do with not being able to hide or unhide something from a touchNode, if I call toggleViews() like this, nothing happens to the gameScene.

if touchNode == pauseButton {

toggleViews()

}


I'm at a loss here, I've been trying to fix this for a few hours. Any help is greatly appreciated

Accepted Reply

Thanks for showing the outputs. Sorry for my requests being confusing, I'll try to clarify my requests if next chance.


I wanted to clarify that

- `touchesBegan` is called and finishes execution normally

- Other parts of `touchesBegan` is not affecting the isHidden property


As for now, both clarified, I have no clue where to investigate.

I'll check your code again taking more time, and when I find something (possibly) wrong, I will write a comment about that.

Replies

Here's my full GameScene code if it helps:

import SpriteKit
import GameplayKit


class GameScene: SKScene {
    
    
    
    private var up = SKSpriteNode(imageNamed: "arrow")
    private var down = SKSpriteNode(imageNamed: "arrow")
    private var left = SKSpriteNode(imageNamed: "arrow")
    private var right = SKSpriteNode(imageNamed: "arrow")
    
    private let swipeRightRec = UISwipeGestureRecognizer()
    private let swipeLeftRec = UISwipeGestureRecognizer()
    private let swipeUpRec = UISwipeGestureRecognizer()
    private let swipeDownRec = UISwipeGestureRecognizer()
    
    var useArrowControls = true
    
    private var gameOverScoreLabel : SKLabelNode?
    private var gameOverHighScoreLabel : SKLabelNode?
    private var gameOverBG : SKSpriteNode?
    
    private var runningScoreLabel : SKLabelNode?
    private var runningScorePanel : SKSpriteNode?
    private var smallBorder : SKSpriteNode?
    private var fullBorder : SKSpriteNode?
    private var gameBG : SKSpriteNode?
    private var arrowBG : SKSpriteNode?
    
    private var verticalPanel : SKSpriteNode?
    private var finalScoreLabel : SKLabelNode?
    private var highScoreLabel : SKLabelNode?
    private var mainMenuButton : SKSpriteNode?
    private var playAgainButton : SKSpriteNode?
    
    private var pauseButton : SKSpriteNode?
    private var pauseMenu : SKSpriteNode?
    private var pauseMenuResumeGameButton : SKSpriteNode?
    private var pauseMenuRestartGameButton : SKSpriteNode?
    private var pauseMenuExitGameButton : SKSpriteNode?
    
    
    
    private var snake = Snake()
   // let size = 40
    
    var score = Score()
    
    private var direction = -1
   // var timer: Timer?
  
    private let moveDistance = CGFloat(32)
    private var food = Food()
    
    var viewController: GameViewController?
    
    var minY = 400
    
    
    
    
    override func didMove(to view: SKView) {
        
        MusicPlayer.shared.stopBackgroundMusic()
        MusicPlayer.shared.startBackgroundMusic(songInt: 1)
        
        pauseButton = self.childNode(withName: "pauseButton") as? SKSpriteNode
        pauseButton?.name = "pauseButton"
        pauseMenu = self.childNode(withName: "pauseMenu") as? SKSpriteNode
        pauseMenuResumeGameButton = self.childNode(withName: "pauseMenuResumeGameButton") as? SKSpriteNode
        pauseMenuResumeGameButton?.name = "pauseMenuResumeGameButton"
        pauseMenuRestartGameButton = self.childNode(withName: "pauseMenuRestartGameButton") as? SKSpriteNode
        pauseMenuRestartGameButton?.name = "pauseMenuRestartGameButton"
        pauseMenuExitGameButton = self.childNode(withName: "pauseMenuExitGameButton") as? SKSpriteNode
        pauseMenuExitGameButton?.name = "pauseMenuExitGameButton"
        
        
        verticalPanel = self.childNode(withName: "verticalPanel") as? SKSpriteNode
        finalScoreLabel = self.verticalPanel?.childNode(withName: "finalScoreLabel") as? SKLabelNode
        highScoreLabel = self.verticalPanel?.childNode(withName: "highScoreLabel") as? SKLabelNode
        playAgainButton = self.childNode(withName: "playAgainButton") as? SKSpriteNode
        playAgainButton?.name = "playAgainButton"
        
        mainMenuButton = self.childNode(withName: "mainMenuButton") as? SKSpriteNode
        mainMenuButton?.name = "mainMenuButton"
        
        smallBorder = self.childNode(withName: "smallBorder") as? SKSpriteNode
        fullBorder = self.childNode(withName: "fullBorder") as? SKSpriteNode
        gameBG = self.childNode(withName: "gameBG") as? SKSpriteNode
        arrowBG = self.childNode(withName: "arrowBG") as? SKSpriteNode
        runningScorePanel = self.childNode(withName: "runningScorePanel") as? SKSpriteNode
        runningScoreLabel = self.childNode(withName: "runningScoreLabel") as? SKLabelNode
        runningScoreLabel?.text = "Score: \(score.getScore())" //used to set initial score label to display a score of 0
        
        
        
        if useArrowControls {
            minY = 400
            fullBorder?.isHidden = true
            up.position = CGPoint(x: frame.midX, y: frame.minY + 325)
            up.scale(to: CGSize(width: 175, height: 175))
            up.zRotation = CGFloat.pi / 2.0
            up.zPosition = 1
            addChild(up)
            
            down.position = CGPoint(x: frame.midX, y: frame.minY + 75)
            down.scale(to: CGSize(width: 175, height: 175))
            down.zRotation = 3 * CGFloat.pi / 2.0
            down.name = "down"
            addChild(down)
            
            left.position = CGPoint(x: frame.midX - 150, y: frame.minY + 200)
            left.scale(to: CGSize(width: 175, height: 175))
            left.zRotation = CGFloat.pi
            left.name = "left"
            addChild(left)
            
            right.position = CGPoint(x: frame.midX + 150, y: frame.minY + 200)
            right.scale(to: CGSize(width: 175, height: 175))
            right.name = "right"
            addChild(right)
        } else {
            arrowBG?.isHidden = true
            minY = 0
            smallBorder?.isHidden = true
            
            swipeRightRec.addTarget(self, action: #selector(GameScene.swipedRight) )
            swipeRightRec.direction = .right
            self.view!.addGestureRecognizer(swipeRightRec)
            
            swipeLeftRec.addTarget(self, action: #selector(GameScene.swipedLeft) )
            swipeLeftRec.direction = .left
            self.view!.addGestureRecognizer(swipeLeftRec)
            
            
            swipeUpRec.addTarget(self, action: #selector(GameScene.swipedUp) )
            swipeUpRec.direction = .up
            self.view!.addGestureRecognizer(swipeUpRec)
            
            swipeDownRec.addTarget(self, action: #selector(GameScene.swipedDown) )
            swipeDownRec.direction = .down
            self.view!.addGestureRecognizer(swipeDownRec)
        }

        food.setFood(scene: self, useSmallBoard: useArrowControls)
        snake.setHead(scene: self)
        setFramerate(framerate: 3)
    }
    
    func toggleViews() {
        verticalPanel?.isHidden = !verticalPanel!.isHidden
        gameOverScoreLabel?.isHidden = !gameOverScoreLabel!.isHidden
        playAgainButton?.isHidden = !playAgainButton!.isHidden
        gameOverBG?.isHidden = !gameOverBG!.isHidden
        runningScorePanel?.isHidden = !runningScorePanel!.isHidden
        runningScoreLabel?.isHidden = !runningScoreLabel!.isHidden
    }
    
    override func touchesBegan(_ touches: Set<UITouch>, with event: UIEvent?) {
        for touch in touches {
            
            let touchPosition = touch.location(in: self)
            let touchNode = self.atPoint(touchPosition)
            
            if touchNode == up {
                changeDirection(direction: 0)
            } else if touchNode.name == "right" {
                changeDirection(direction: 1)
            } else if touchNode.name == "down" {
                changeDirection(direction: 2)
            } else if touchNode.name == "left" {
                changeDirection(direction: 3)
            } else if touchNode.name == "playAgainButton" {
                startNewGame()
            } else if touchNode.name == "mainMenuButton" {
                returnToMenu()
            } else if touchNode == pauseButton {
                pauseGame()
            } else if touchNode.name == "pauseMenuResumeGameButton" {
                resumeGameFromPause()
            } else if touchNode.name == "pauseMenuRestartGameButton" {
                resumeGameFromPause()
                gameOver()
                startNewGame()
            } else if touchNode.name == "pauseMenuExitGameButton" {
                resumeGameFromPause()
                gameOver()
                returnToMenu()
            }
        }
    }
    
    func returnToMenu() {
        MusicPlayer.shared.stopBackgroundMusic()
        MusicPlayer.shared.startBackgroundMusic(songInt: 0)
        viewController?.returnToMenu()
    }
    
    
    override func touchesMoved(_ touches: Set<UITouch>, with event: UIEvent?) {
    }
    
    override func touchesEnded(_ touches: Set<UITouch>, with event: UIEvent?) {
    }
    
    override func touchesCancelled(_ touches: Set<UITouch>, with event: UIEvent?) {
    }
    
    @objc func swipedUp() {
        changeDirection(direction: 0)
    }
    
    @objc func swipedRight() {
        changeDirection(direction: 1)
    }
    
    @objc func swipedDown() {
        changeDirection(direction: 2)
    }
    
    @objc func swipedLeft() {
        changeDirection(direction: 3)
    }
    
    func pauseGame() {
        self.view?.isPaused = true
        print("Before isHidden is set to false:  \(pauseMenu?.isHidden)")
        pauseMenu?.isHidden = false
        pauseMenu?.isHidden = false
        print("Before isHidden is set to false:  \(pauseMenu?.isHidden)")
    }
    
    
    func resumeGameFromPause() {
        self.view?.isPaused = false
        togglePauseViews()
    }
    
    func togglePauseViews() {
        pauseMenu?.isHidden = !pauseMenu!.isHidden
        pauseButton?.isHidden = !pauseButton!.isHidden
    }
    
    
    func changeDirection(direction: Int) {
        if !self.view!.isPaused { //this if statement doesn't let the user change direction when the game is paused.
            if direction == 0 {
                if (snake.getSnake()[0].getDirection() != 2) {
                    snake.turnSnake(direction: 0)
                   // body[0].setDirection(newDirection: 0)
                }
            } else if direction == 1 {
                if (snake.getSnake()[0].getDirection() != 3) {
                    snake.turnSnake(direction: 1)
                    //  body[0].setDirection(newDirection: 1)
                }
            } else if direction == 2 {
                if (snake.getSnake()[0].getDirection() != 0) {
                    snake.turnSnake(direction: 2)
                    //body[0].setDirection(newDirection: 2)
                }
            } else if direction == 3 {
                if (snake.getSnake()[0].getDirection() != 1) {
                    snake.turnSnake(direction: 3)
                    //body[0].setDirection(newDirection: 3)
                }
            }
        }
    }
    
    
    override func update(_ currentTime: TimeInterval) {
        // Called before each frame is rendered
        
        if !checkCollisions() {
            snake.update(scene: self)
        } else {
            gameOver()
        }
           
    }
    
    func gameOver() {
        self.view?.isPaused = true
        finalScoreLabel?.text = "Score: \(score.getScore())"
        score.calculateHighScore()
        highScoreLabel?.text = "Highscore: \(score.getHighScore())"
        
        toggleViews()
    }
    
    /*
     Checks to see if the head of the snake has collided with the snake itself, with the wall, or with food
     returns true if there is a collision with the wall or itself, and false if there are no collisions or if it collided with food. If it collided with food, it calls the dropFood() method of the Food class to move the food to a randomly generated spot on the map.
     */
    func startNewGame() {
        score.resetScore()
        snake.clearSnake()
        runningScoreLabel?.text = "Score: \(score.getScore())"
        snake.setHead(scene: self)
        food.dropFood(scene: self)
        setFramerate(framerate: 3)
        toggleViews()
        self.view?.isPaused = false
        MusicPlayer.shared.playSoundEffect(soundType: 1)
        
    }
    
    
    func checkCollisions() -> Bool{
        let body = snake.getSnake()
        let head = body[0]
        //Checks if head hits food
        if isCollided(o1: head.getSegment(), o2: food.getFood()) {
            food.dropFood(scene: self)
            snake.addSegment(scene: self)
            score.addPoint()
            runningScoreLabel?.text = "Score: \(score.getScore())"
            MusicPlayer.shared.playSoundEffect(soundType: 2)
            if score.getScore() == 5 {
               setFramerate(framerate: 4)
            } else if score.getScore() == 10 {
                setFramerate(framerate: 5)
            } else if score.getScore() == 15 {
                setFramerate(framerate: 6)
            }
        }
        //Checks if head hits wall
        var minYToCheck = 0
        if useArrowControls {
            minYToCheck = 400
        } else {
            minYToCheck = 18
        }
//        print("minY: \(minYToCheck)")
        if head.getSegment().position.x + (head.getSegment().size.width / 2) >= frame.maxX - 32 || head.getSegment().position.x - (head.getSegment().size.width / 2) <= frame.minX + 32 || head.getSegment().position.y + (head.getSegment().size.width / 2) >= frame.maxY - 32 || head.getSegment().position.y - (head.getSegment().size.width / 2) <= CGFloat(minYToCheck) {
            print(head.getSegment().position.y)
            MusicPlayer.shared.playSoundEffect(soundType: 3)
            return true
        }
        
        //Checks if head hits body piece
        var i = 1
        while i < body.count {
            if isCollided(o1: head.getSegment(), o2: body[i].getSegment()) {
                MusicPlayer.shared.playSoundEffect(soundType: 3)
                return true
            }
            i += 1
        }
        return false
    }
    
    //UPDATE NAMES
    func isCollided(o1: SKSpriteNode, o2: SKSpriteNode) -> Bool{
        let width = o1.size.width
        var collision = false
        
        if abs(o1.position.x - o2.position.x) < width &&
            abs(o1.position.y - o2.position.y) < width {
            collision = true
            
        }
        return collision
    }
    
    func setFramerate(framerate: Int) {
        self.view?.preferredFramesPerSecond = framerate
    }
   

What do you get if you put `print("End of touchesBegan: \(pauseMenu?.isHidden)")`, at the end of `touchesBegan(_:with:)` (just before the line 193.) ?

Nothing, even when I press on arrow keys or press the pause button. Also never prints anything when I put it inside the for loop (after line 191)

I don't care inside for loop. Please tell me what do you get putting the `print` outside the for loop.


    override func touchesBegan(_ touches: Set<UITouch>, with event: UIEvent?) {
        print("Start of touchesBegan:  \(pauseMenu?.isHidden)")
        for touch in touches {
              
            let touchPosition = touch.location(in: self)
            let touchNode = self.atPoint(touchPosition)
              
            if touchNode == pauseButton {
                pauseGame()
            }
        }
        print("End of touchesBegan:  \(pauseMenu?.isHidden)")
    }


If `print("End of touchesBegan: \(pauseMenu?.isHidden)")` shows really nothing, your project is broken.

Nothing prints. I was saying that I tried both inside and outside.

You have no need to try inside. If the `print` placed in a non-conditional part of `touchesBegan` and it outputs nothing, that means `touchesBegan` is not executed, or is stopped execution somewhere. Please try simplifying your `touchesBegan` and see what happens.

Sorry for the confusion. I ran it again and it printed this after I pressed the pause button:

Start of touchesBegan: Optional(true)

End of touchesBegan: Optional(false)


Everytime I touch the screen elsewhere than the pause button it prints:

Start of touchesBegan: Optional(true)

End of touchesBegan: Optional(true)

Thanks for showing the outputs. Sorry for my requests being confusing, I'll try to clarify my requests if next chance.


I wanted to clarify that

- `touchesBegan` is called and finishes execution normally

- Other parts of `touchesBegan` is not affecting the isHidden property


As for now, both clarified, I have no clue where to investigate.

I'll check your code again taking more time, and when I find something (possibly) wrong, I will write a comment about that.

I just figured it out. It wasn't working because I was pausing the view. It worked after I removed it.


Thank you for your help!

Thanks for reporting, `isPaused` is one thing that I should have noticed and checked.


Anyway, happy to hear you have found the reason. May your app be great, good luck.


One last thing, maybe SpriteKit is a better place to post this sort of issues.