UPGRADE YOUR SKILLS: Learn advanced Swift and SwiftUI on Hacking with Swift+! >>

Help with Extra Alert controller in Project 11 Challenges.

Forums > 100 Days of Swift

@AE542  

Somehow I managed to complete this challenge with some hints and now the last thing I want to do is add an alert controller that ends and resets the game when the number of balls hits 0. I created a AC with an objc func to handle the reset but it just won't show in game when the balls reach 0...Any help would be appreciated! Also really sorry if my code is messy i'm a beginner. Code is as follows:

import SpriteKit

class GameScene: SKScene, SKPhysicsContactDelegate { var scoreLabel: SKLabelNode! // var ballLabel: SKLabelNode!

 let noOfBalls = 0
var usedBalls = 5 {
    didSet {
        ballLabel.text = "Balls: \(usedBalls)"
    }
}
var score = 0 {
    didSet {//property observer.
        scoreLabel.text = "Score: \(score)"
    }
}
var editLabel: SKLabelNode!

var editingMode: Bool = false {
    didSet {
        if editingMode {
            editLabel.text = "Done"
        } else {
            editLabel.text = "Edit"
        }
    }
}

let allBalls = ["ballRed","ballYellow", "ballBlue", "ballGreen","ballBlue","ballCyan", "ballPurple", "ballGrey"] 

override func didMove(to view: SKView) {

let background = SKSpriteNode(imageNamed: "background")//loads the background for the app SKSpriteNode is the actual image or colour that's loaded in from the assets here.
    background.position = CGPoint(x: 512, y: 384)
    background.blendMode = .replace //determines how a node is drawn. Also works faster in simulator
    background.zPosition = -1 
    addChild(background)

    scoreLabel = SKLabelNode(fontNamed: "ChalkDuster")
    scoreLabel.text = "Score: 0"
    scoreLabel.horizontalAlignmentMode = .right //left/right/center alignment setting.
    scoreLabel.position = CGPoint(x: 980, y: 700)
    addChild(scoreLabel)// creates score Label and the property observer changes when the score is updated and it is position on the screen

    ballLabel = SKLabelNode(fontNamed: "ChalkDuster")
    ballLabel.text = "Balls: 5"
    ballLabel.horizontalAlignmentMode = .center
    ballLabel.position = CGPoint(x: 650, y: 700)
    addChild(ballLabel)

    editLabel = SKLabelNode(fontNamed: "ChalkDuster")
    editLabel.text = "Edit"
    editLabel.position = CGPoint(x: 80, y: 700)
    addChild(editLabel)

    physicsBody = SKPhysicsBody(edgeLoopFrom: frame)// this allows the boxes to have physics and fall when they appear.

    physicsWorld.contactDelegate = self // comes from SKScene

    makeSlot(at: CGPoint(x: 128, y: 0), isGood: true)
    makeSlot(at: CGPoint(x: 384, y: 0), isGood: false)
    makeSlot(at: CGPoint(x: 640, y: 0), isGood: true)
    makeSlot(at: CGPoint(x: 896, y: 0), isGood: false)

    makeBouncer(at: CGPoint(x:0, y:0))
    makeBouncer(at: CGPoint(x:256, y:0))
    makeBouncer(at: CGPoint(x:512, y:0))
    makeBouncer(at: CGPoint(x:768, y:0))
    makeBouncer(at: CGPoint(x:1024, y:0))

}
override func touchesBegan(_ touches: Set<UITouch>, with event: UIEvent?) {// called when users touch the screen.
    guard let touch = touches.first else { return }
    let location = touch.location(in: self )// find where touch was in game scene

    let objects = nodes(at: location)

    if objects.contains(editLabel) {
        editingMode.toggle()// flips boolean value same as editingMode = !editingMode
    } else {
        if editingMode {

            let size = CGSize(width: Int.random(in: 16...128), height: 16)// make random size
            let box = SKSpriteNode(color: UIColor(red: CGFloat.random(in: 0...1), green: CGFloat.random(in: 0...1), blue: CGFloat.random(in: 0...1), alpha: 1), size: size)// SKSprite node with random colours.
            box.zRotation = CGFloat.random(in: 0...5)//rotate a random number of radients
            box.position = location

            box.physicsBody = SKPhysicsBody(rectangleOf: box.size)//give rectangle physics body
            box.physicsBody?.isDynamic = false
            addChild(box)

        } else if usedBalls != noOfBalls {

            let ball = SKSpriteNode(imageNamed: allBalls.randomElement() ?? "ballRed")

    ball.physicsBody = SKPhysicsBody(circleOfRadius: ball.size.width / 2.0)//is half the width of the ball.
    ball.physicsBody?.restitution = 0.4/
    ball.physicsBody?.contactTestBitMask = ball.physicsBody?.collisionBitMask ?? 0

            ball.position = CGPoint(x: 512, y: 700)

    ball.name = "ball" 

    addChild(ball)//creates ball

            usedBalls -= 1

        } else if noOfBalls == 0 {
        let ac = UIAlertController(title: "Game Over", message: "Your score is \(String(describing: scoreLabel))", preferredStyle: .alert)
        ac.addAction(UIAlertAction(title: "Restart", style: .default, handler: Reset))
        }

}

}

func makeBouncer(at position: CGPoint) {
        let bouncer = SKSpriteNode(imageNamed: "bouncer")//from Assets
               bouncer.position = position //not CG Float and coordinates x256 and y0 because that just creates the bouncer in one place.
               bouncer.physicsBody = SKPhysicsBody(circleOfRadius: bouncer.size.width / 2)
               bouncer.physicsBody?.isDynamic = false // object will collide with other objects
               addChild(bouncer)
    }
func makeSlot(at position: CGPoint, isGood: Bool) {
    var slotBase: SKSpriteNode
    var slotGlow: SKSpriteNode// allows a glow to the slotbases
    if isGood {
        slotBase = SKSpriteNode(imageNamed: "slotBaseGood")
        slotGlow = SKSpriteNode(imageNamed: "slotGlowGood")
        slotBase.name = "good"

    } else {
        slotBase = SKSpriteNode(imageNamed: "slotBaseBad")
        slotGlow = SKSpriteNode(imageNamed: "slotGlowBad")
        slotBase.name = "bad" //don't forget to rename to the opposite. This was labelled as "good" so the score kept going up even when it hit a bad slotBase. Changed it to "bad" and now it works.
    }
    slotBase.position = position
    slotGlow.position = position //don't forget to initialise it here

    slotBase.physicsBody = SKPhysicsBody(rectangleOf: slotBase.size)
    slotBase.physicsBody?.isDynamic = false //don't want the slotBases to move when they are hit by another object.
    addChild(slotBase)
    addChild(slotGlow)//and addChild so it shows up in game

    let spin = SKAction.rotate(byAngle: .pi, duration: 10)
    let spinForever = SKAction.repeatForever(spin)
    slotGlow.run(spinForever)
}

 func collision(between ball: SKNode, object: SKNode) {//called when a ball collides with another object
    if object.name == "good" {
        destroy(ball: ball) //destroys object
        score += 1
        usedBalls += 1 // ok so this added balls when it hit the good slotBase

    } else if object.name == "bad" {
        destroy(ball: ball)
        score -= 1

}

func destroy(ball: SKNode) {

    if let fireParticles = SKEmitterNode(fileNamed: "FireParticles") {//creates high performance particle effects.
        fireParticles.position = ball.position
        addChild(fireParticles)
    }

    ball.removeFromParent() /

}
func didBegin(_ contact: SKPhysicsContact) {
    guard let nodeA = contact.bodyA.node else { return }
    guard let nodeB = contact.bodyB.node else { return }

    if nodeA.name == "ball" {
        collision(between: nodeA, object: nodeB)
    } else if nodeB.name == "ball" {
        collision(between: nodeB, object: nodeA)
    }

}
@objc func Reset(action: UIAlertAction) {
    if usedBalls < noOfBalls {
        score = 0
        usedBalls = 5

    }
}

}

3      

Hi! You need to show the alert with the present method available on the UIViewController. So you need a way to inform your game view controller from the game scene that it should show this alert.

4      

@AE542  

@nemecek-filip Thanks for your reply. I added the ac.present(ac, animated: true) method at the end but I'm still having problems loading it... The app crashes and with the error "application tried to present modal view controller on itself. presenting controller is <uialertcontroller> so I after doing some reading you can't call the Alert controler on itself...I feel like there's something I've learnt before about calling viewcontrollers between swift files but I'm having a mind blank. Anyway I appreciate the help!

3      

Hi you can't call "present" in SpriteKit as is not a subclass of UIViewController, thats why you getting that error.

3      

Hacking with Swift is sponsored by RevenueCat

SPONSORED Take the pain out of configuring and testing your paywalls. RevenueCat's Paywalls allow you to remotely configure your entire paywall view without any code changes or app updates.

Learn more here

Sponsor Hacking with Swift and reach the world's largest Swift community!

Archived topic

This topic has been closed due to inactivity, so you can't reply. Please create a new topic if you need to.

All interactions here are governed by our code of conduct.

 
Unknown user

You are not logged in

Log in or create account
 

Link copied to your pasteboard.