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
}
}
}