Day 63 - 100 Days of Swift

4 minute read

Project 17 (part 2)

Day 63 is the second part of the seventeenth project. You review the things you covered yesterday and then he gives you three challenges to extend the game. The first challenge is to stop the player from cheating by lifting their finger and “teleporting” the space ship to another point. The second challenge is to reduce the amount of time that the timer takes, after a certain number of enemies have been created so that the game gets harder over time. The third challenge is to stop creating enemies after the player has died.

For the first challenge I added an isTracking property and updated it in touchesBegan and touchesEnded:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
var isTracking = false

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

    guard let touch = touches.first else { return }
    let location = touch.location(in: self)

    let nodes = Set(self.nodes(at: location))
    // Only start tracking if the touch is in the spaceship
    if nodes.contains(player) { isTracking = true }
}

// At the beginning of touchesMoved
guard isTracking else { return }

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

For the second challenge I added enemyCounter and timeInterval properties and then used them in createEnemy :

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
var enemyCounter = 0
var timeInterval: TimeInterval = 0.35

// At the end of createEnemy
enemyCounter += 1

if enemyCounter >= 20 {
    enemyCounter = 0
    timeInterval -= 0.01
    gameTimer?.invalidate()
    gameTimer = Timer.scheduledTimer(timeInterval: timeInterval,
                                     target: self,
                                     selector: #selector(createEnemy),
                                     userInfo: nil, repeats: true)
}

This means that after every 20 enemies are created, the time between enemy creation will be reduce by 0.01 seconds. That may not seem like a lot, but it gets pretty difficult fairly quickly.

For the third challenge, I pulled out the end of game code into its own function. To stop creating enemies I just invalidated the timer:

1
2
3
4
func endGame() {
    isGameOver = true
    gameTimer?.invalidate()
}

And added a few extra things for myself. First, I wanted to emphasize the score when the game ended, so I animated that in:

1
2
3
4
5
// In endGame
let moveAction = SKAction.move(to: CGPoint(x: 512, y: 360), duration: 0.6)
let scaleAction = SKAction.scale(to: 3, duration: 0.6)
let groupAction = SKAction.group([moveAction, scaleAction])
scoreLabel.run(groupAction)

This brings the score to the center of the screen and makes it way bigger, so it is hard to miss.

I also wanted to be able to restart the game, so I pulled some of the code out of didMove into a new function called resetGame:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
func resetGame() {
    isGameOver = false

    player?.removeFromParent()
    player = SKSpriteNode(imageNamed: "player")
    player.position = CGPoint(x: 100, y: 384)
    player.physicsBody = SKPhysicsBody(texture: player.texture!,
                                       size: player.size)
    player.physicsBody?.contactTestBitMask = 1
    addChild(player)

    scoreLabel?.removeFromParent()
    scoreLabel = SKLabelNode(fontNamed: "Chalkduster")
    scoreLabel.position = CGPoint(x: 120, y: 16)
    scoreLabel.horizontalAlignmentMode = .center
    addChild(scoreLabel)

    score = 0

    gameTimer = Timer.scheduledTimer(timeInterval: timeInterval,
                                     target: self,
                                     selector: #selector(createEnemy),
                                     userInfo: nil, repeats: true)
}

Then I just called that in touchesBegan:

1
2
3
4
guard !isGameOver else {
    resetGame()
    return
}

This resets the game if the player taps anywhere on screen when we are in the game over state.

When it is all said and done it looks like this:

Gif of working app.

You can find my version of this project at the end of day 63 on Github here.

Reflections

If I were to keep working on this the next thing I would do would be to remove the explosion emitter after it had finished, so that they don’e just keep invisibly stacking up if the user plays a bunch of rounds. I haven’t found a good way to get those SKEmitterNodes to clean themselves up yet, but I’m sure there is a way to do it. Other than that, I had a lot of fun with this game and it is pretty playable already.