1
votes

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
}



}
1

1 Answers

2
votes

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