Day 79 - 100 Days of Swift
Project 23 (part 3)
Day 79 is the third part of the twenty-third project, where you review what you’ve learned and he gives you a few challenges to polish up the game. He challenges you to clean up your code a little bit, removing some of the magic numbers from the createEnemy
method. He challenges you to create a fast-moving type of enemy that is worth extra points. And he challenges you to add a “Game Over” node once when the player loses all their lives.
The first challenge I did to a level I was satisfied with as I followed along with his code in the first place. So I didn’t really change much on that front today.
For the second challenge I first added a new enemy type in createEnemy
:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
var enemyType = Int.random(in: 0...7)
// in the big if statement
else if enemyType == 7 {
enemy = SKSpriteNode(imageNamed: "penguin")
run(SKAction.playSoundFileNamed("launch.caf",
waitForCompletion: false))
enemy.name = "bonus"
}
let xPositionRange = enemy.name == "bonus" ? 520...760 : 64...960
let randomX = Int.random(in: xPositionRange)
let yVelocityRange = enemy.name == "bonus" ? 48...64 : 24...32
let randomYVelocity = Int.random(in: yVelocityRange)
Then I made sure to destroy it in touchesMoved
:
1
2
3
4
5
6
7
8
switch node.name {
case "enemy", "bonus":
destroyPenguin(node)
case "bomb":
destroyBomb(node)
default:
break
}
And update the score in destroyPenguin
:
1
2
let modifier = node.name == "bonus" ? 5 : 1
score += modifier
Then I just needed to remove them if they fall off screen. I decided I didn’t want the player to lose a life for missing these guys, because they sometimes fly off screen pretty quickly, so I grouped them in with the bombs for the logic in update
:
1
2
3
4
5
else if node.name == "bombContainer" || node.name == "bonus" {
node.name = ""
node.removeFromParent()
activeEnemies.remove(at: index)
}
For the third challenge, I just set up a gameOverLabel
:
1
2
3
4
5
6
7
8
9
10
11
let gameOverLabel: SKLabelNode = {
let node = SKLabelNode(fontNamed: "Chalkduster")
node.horizontalAlignmentMode = .center
node.fontSize = 120
node.text = "Game Over!"
node.setScale(0.001)
node.position = CGPoint(x: 512, y: 384)
return node
}()
And then added it when the game is over:
1
2
3
addChild(gameOverLabel)
let scaleAction = SKAction.scale(to: 1.0, duration: 0.5)
gameOverLabel.run(scaleAction)
I also wanted to give the user the ability to restart the game without having to force quit the app, so I pulled some of the logic out into a new restartGame
method:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
func restartGame() {
isGameEnded = false
gameOverLabel.removeFromParent()
gameOverLabel.setScale(0.001)
activeEnemies.forEach { $0.removeFromParent() }
activeEnemies.removeAll()
popupTime = 0.9
sequencePosition = 0
chainDelay = 3.0
nextSequenceQueued = true
physicsWorld.speed = 0.85
score = 0
createLives()
sequence = [.oneNoBomb,
.oneNoBomb,
.twoWithOneBomb,
.twoWithOneBomb,
.three,
.one,
.chain]
let possible = SequenceType.allCases
(0 ... 1000).forEach { _ in
if let nextSequence = possible.randomElement() {
sequence.append(nextSequence)
}
}
DispatchQueue.main.asyncAfter(deadline: .now() + 2)
{ [weak self] in
self?.tossEnemies()
}
}
And then called it in didMove(to:)
and touchesBegan
:
1
2
3
4
5
6
7
8
// End of didMove(to:)
restartGame()
// Beginning of touchesBegan
guard !isGameEnded else {
restartGame()
return
}
With all that done, it leads to a game that looks like this:
You can find my version of this project at the end of day 79 on Github here.