uz7 uz7 - 21 days ago 7
iOS Question

Implementing Game over into swift game

I have created a game in Xcode, however the i would like to cut to a game over screen in when the player loses, at the moment the main.storyboard file is blank. I have found this solution Using a view controller for a game over scene

However, i am unsure on how to implement this into my code. I have attached my code below, if anyone could help me on this it would be greatly appreciated.

import SpriteKit
import GameplayKit

class GameScene: SKScene, SKPhysicsContactDelegate {

var bird = SKSpriteNode()

var bg = SKSpriteNode()

var scoreLabel = SKLabelNode()

var score = 0

var gameOverLabel = SKLabelNode()

var timer = Timer()

enum ColliderType: UInt32 {

case Bird = 1
case Object = 2
case Gap = 4

}

var gameOver = false

func makePipes() {

let movePipes = SKAction.move(by: CGVector(dx: -2 * self.frame.width, dy: 0), duration: TimeInterval(self.frame.width / 100))

let gapHeight = bird.size.height * 4

let movementAmount = arc4random() % UInt32(self.frame.height / 2)

let pipeOffset = CGFloat(movementAmount) - self.frame.height / 4

let pipeTexture = SKTexture(imageNamed: "pipe1.png")

let pipe1 = SKSpriteNode(texture: pipeTexture)

pipe1.position = CGPoint(x: self.frame.midX + self.frame.width, y: self.frame.midY + pipeTexture.size().height / 2 + gapHeight / 2 + pipeOffset)

pipe1.run(movePipes)

pipe1.physicsBody = SKPhysicsBody(rectangleOf: pipeTexture.size())
pipe1.physicsBody!.isDynamic = false

pipe1.physicsBody!.contactTestBitMask = ColliderType.Object.rawValue
pipe1.physicsBody!.categoryBitMask = ColliderType.Object.rawValue
pipe1.physicsBody!.collisionBitMask = ColliderType.Object.rawValue

pipe1.zPosition = -1

self.addChild(pipe1)

let pipe2Texture = SKTexture(imageNamed: "pipe2.png")

let pipe2 = SKSpriteNode(texture: pipe2Texture)

pipe2.position = CGPoint(x: self.frame.midX + self.frame.width, y: self.frame.midY - pipe2Texture.size().height / 2 - gapHeight / 2 + pipeOffset)

pipe2.run(movePipes)

pipe2.physicsBody = SKPhysicsBody(rectangleOf: pipeTexture.size())
pipe2.physicsBody!.isDynamic = false

pipe2.physicsBody!.contactTestBitMask = ColliderType.Object.rawValue
pipe2.physicsBody!.categoryBitMask = ColliderType.Object.rawValue
pipe2.physicsBody!.collisionBitMask = ColliderType.Object.rawValue

pipe2.zPosition = -1

self.addChild(pipe2)

let gap = SKNode()

gap.position = CGPoint(x: self.frame.midX + self.frame.width, y: self.frame.midY + pipeOffset)

gap.physicsBody = SKPhysicsBody(rectangleOf: CGSize(width: pipeTexture.size().width, height: gapHeight))

gap.physicsBody!.isDynamic = false

gap.run(movePipes)

gap.physicsBody!.contactTestBitMask = ColliderType.Bird.rawValue
gap.physicsBody!.categoryBitMask = ColliderType.Gap.rawValue
gap.physicsBody!.collisionBitMask = ColliderType.Gap.rawValue

self.addChild(gap)

}

func didBegin(_ contact: SKPhysicsContact) {

if gameOver == false {

if contact.bodyA.categoryBitMask == ColliderType.Gap.rawValue || contact.bodyB.categoryBitMask == ColliderType.Gap.rawValue {

score += 1

scoreLabel.text = String(score)

} else {

self.speed = 0

gameOver = true

timer.invalidate()

gameOverLabel.fontName = "Helvetica"

gameOverLabel.fontSize = 30

gameOverLabel.text = "Game Over! Tap to play again."

gameOverLabel.position = CGPoint(x: self.frame.midX, y: self.frame.midY)

self.addChild(gameOverLabel)

}
}

}


override func didMove(to view: SKView) {

self.physicsWorld.contactDelegate = self

setupGame()


}

func setupGame() {

timer = Timer.scheduledTimer(timeInterval: 3, target: self, selector: #selector(self.makePipes), userInfo: nil, repeats: true)

let bgTexture = SKTexture(imageNamed: "bg.png")

let moveBGAnimation = SKAction.move(by: CGVector(dx: -bgTexture.size().width, dy: 0), duration: 7)
let shiftBGAnimation = SKAction.move(by: CGVector(dx: bgTexture.size().width, dy: 0), duration: 0)
let moveBGForever = SKAction.repeatForever(SKAction.sequence([moveBGAnimation, shiftBGAnimation]))

var i: CGFloat = 0

while i < 3 {

bg = SKSpriteNode(texture: bgTexture)

bg.position = CGPoint(x: bgTexture.size().width * i, y: self.frame.midY)

bg.size.height = self.frame.height

bg.run(moveBGForever)

bg.zPosition = -2

self.addChild(bg)

i += 1

}


let birdTexture = SKTexture(imageNamed: "flappy1.png")
let birdTexture2 = SKTexture(imageNamed: "flappy2.png")

let animation = SKAction.animate(with: [birdTexture, birdTexture2], timePerFrame: 0.1)
let makeBirdFlap = SKAction.repeatForever(animation)

bird = SKSpriteNode(texture: birdTexture)

bird.position = CGPoint(x: self.frame.midX, y: self.frame.midY)

bird.run(makeBirdFlap)

bird.physicsBody = SKPhysicsBody(circleOfRadius: birdTexture.size().height / 2)

bird.physicsBody!.isDynamic = false

bird.physicsBody!.contactTestBitMask = ColliderType.Object.rawValue
bird.physicsBody!.categoryBitMask = ColliderType.Bird.rawValue
bird.physicsBody!.collisionBitMask = ColliderType.Bird.rawValue

self.addChild(bird)

let ground = SKNode()

ground.position = CGPoint(x: self.frame.midX, y: -self.frame.height / 2)

ground.physicsBody = SKPhysicsBody(rectangleOf: CGSize(width: self.frame.width, height: 1))

ground.physicsBody!.isDynamic = false

ground.physicsBody!.contactTestBitMask = ColliderType.Object.rawValue
ground.physicsBody!.categoryBitMask = ColliderType.Object.rawValue
ground.physicsBody!.collisionBitMask = ColliderType.Object.rawValue


self.addChild(ground)

scoreLabel.fontName = "Helvetica"

scoreLabel.fontSize = 60

scoreLabel.text = "0"

scoreLabel.position = CGPoint(x: self.frame.midX, y: self.frame.height / 2 - 220)

self.addChild(scoreLabel)





}

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

if gameOver == false {


bird.physicsBody!.isDynamic = true

bird.physicsBody!.velocity = CGVector(dx: 0, dy: 0)

bird.physicsBody!.applyImpulse(CGVector(dx: 0, dy: 80))

} else {

gameOver = false

score = 0

self.speed = 1

self.removeAllChildren()

setupGame()

}

}


override func update(_ currentTime: TimeInterval) {
// Called before each frame is rendered
}



}

Answer

You should setup a GameOverScene and display this scene when needed. The RayWenderlich site has alot of tutorials that will show you how to do this, it is likely too much to explain here

Here is a link to the Apple Game Frameworks Section

This page shows you how to implement a level complete overlay)

But basically you just create a new Scene, add any UI elements to it that you need and then from the view controller you can call PresentScene, like so

view.presentScene(scene)

Example code:

class GameScene: SKScene {

    var notificationLabel = SKLabelNode(text: "Tap the screen to win")
    override func didMove(to view: SKView) {

        self.backgroundColor = SKColor.black

        addChild(notificationLabel)
        notificationLabel.fontSize = 32.0
        notificationLabel.color = SKColor.white
        notificationLabel.fontName = "Thonburi-Bold"
        notificationLabel.position = CGPoint(x: size.width / 2, y: size.height / 2)
    }

    func displayGameOver() {

        let gameOverScene = GameOverScene(size: size)
        gameOverScene.scaleMode = scaleMode

        let reveal = SKTransition.flipHorizontal(withDuration: 0.5)
        view?.presentScene(gameOverScene, transition: reveal)
    }

    override func touchesEnded(_ touches: Set<UITouch>, with event: UIEvent?) {

        displayGameOver()
    }
}

class GameOverScene: SKScene {

    var notificationLabel = SKLabelNode(text: "You Won")

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

        self.backgroundColor = SKColor.darkGray

        addChild(notificationLabel)
        notificationLabel.fontSize = 32.0
        notificationLabel.color = SKColor.white
        notificationLabel.fontName = "Thonburi-Bold"
        notificationLabel.position = CGPoint(x: size.width / 2, y: size.height / 2)
    }

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

    override func touchesEnded(_ touches: Set<UITouch>, with event: UIEvent?) {

        let gameScene = GameScene(size: size)
        gameScene.scaleMode = scaleMode

        let reveal = SKTransition.flipHorizontal(withDuration: 0.5)
        view?.presentScene(gameScene, transition: reveal)
    }
}

Tapping the screen in either scene will present the other scene. You should also be able to manage this via the viewcontroller and do pretty much the same thing, But I haven't done that yet, I'm still fairly new to SpriteKit myself.

Hope this helps