You must be a Pro member to watch this episode.

Get Access Now

Sign in now if you're already Pro.

1

Create a SpriteKit Game Project

11:21FreeDone
2

Lay Out the Game UI

8:54FreeDone
3

Define the Game Piece Model

13:16FreeDone
4

Create the Level Model

5:47FreeDone
5

Draw the Rune Sprites

6:02FreeDone
6

Detecting Taps on Runes

5:27ProsDone
7

Determine Swipe Direction

6:18ProsDone
8

Add Physics to Shatter Runes

14:00ProsDone
9

Build Out the UI Text

12:24ProsDone

Keeping Score

8:41ProsDone
11

Displaying Score Feedback

8:18ProsDone

Keeping Score

Published by Chris Slowik

Episode Notes

Extra tips from the author

Rules to Define the Game

The basis of a game like this is probably the scoring rules. There are other details to hammer out later, but without a score there's no real measure of your progress or skill! Before adding any more visual or environment refinements, I think we should get started actually defining how this game is scored.

So.. how are we going to score smashed runes, anyway? To introduce a bit of strategy to the swiping action, we’re going to be rewarding the player for streaks of the same color. We’ll start at a minimum of 1 point, then go up exponentially - 1, 2, 4, 8, 16, 32, etc - max of 128.

To get started, add some lines to the GameViewController defining a few variables we'll need to keep track of things. Obviously there's a running score total that starts at 0. There's also a multiplier which, in combination with the exponent base runeValue, determines the rune score. If later we decide to make the rune score go up more dramatically with streaks, we can tweak the runeValue. There's a maxRuneScore which will be the absolute highest a standard rune can score, and a variable to store the lastColor swiped.

var score = 0
var multiplier: Double = 0
var lastColor: RuneColor?
let runeValue = 2.0         // powers of 2!
let maxRuneScore = 128      // highest score possible

These variables basically define the rules of the game.

Add the Scoring Function

Our scoring function is fairly simple. It takes only two parameters, a RuneColor and a RuneType and returns an Int score.

First we check to see if the color matches the last color swiped. If so, we can increase the multiplier, otherwise we'll reset it to 0. Once we've determined if we have a streak, we can calculate the runeScore by applying the multiplier to the base value with pow, Swift's exponent function. Using min, we can make sure the rune score never goes over 128, no matter the multiplier.

Then, we check if the rune is a force rune - the diamond shape. These are bonus runes, and carry a value 10x a normal rune. This introduces a bit of strategy to the game because you'd want to hit the force rune near the end of a streak if possible for the maximum score. If we hit a force rune, simply multiply the runeScore by 10.

func addScoreForSwipe(_ color: RuneColor, type: RuneType) -> Int {
    if color == lastColor {
        multiplier = multiplier + 1
    } else {
        multiplier = 0
    }
    var runeScore = min(Int(pow(runeValue, multiplier)), maxRuneScore)
    if type == "force" {
        runeScore = 10 * runeScore
    }
    score = score + runeScore
    lastColor = color
    updateText()
    return runeScore
}

Once the score is calculated, we'll add it to score, set the lastColor to what we just swiped, and update our UI text. We end the function by returning runeScore - we'll need the score value passed out of the function to animate a score effect.

Controller-Scene Communication

We need a way for our GameViewController to tell the GameScene to animate once a score is made. The scoring calculation is part of the game rules, and really doesn't belong in the GameScene, but the animation definitely doesn't belong in the view controller. So, in the GameScene, we'll add a variable called scoreHandler.

var scoreHandler: ((RuneColor, RuneType) -> Int)?

It looks odd, but this variable's type is a function that takes a RuneColor and a RuneType, and returns an Int. It's been made optional since we'll set it after creating the scene. It can't be created at scene initialization. So - flip back to the GameViewController and right after creating the scene, add a line that sets the scoreHandler.

scene.scoreHandler = addScoreForSwipe

We can just use the name of the function we want to use as the scoreHandler. Our scene has no opinions on what the function should do - only the function type (the parameters and return type).

Using the scoreHandler

In the GameScene, we can now use the scoreHandler as if we had defined a function in the scene called that. We're going to finish off this lesson by starting to lay the groundwork for animating the rune score.

func scoreRune(_ rune: Rune) {
    var runeScore: Int
    if let handler = scoreHandler {
        runeScore = handler(rune.runeColor, rune.runeType)
        // TODO: Add Score Animation
    }
}

This function is pretty straightforward. If we've set up a scoreHandler, we just call that function with our rune's color and type. Notice how we can use a function just like any other type. In Swift, functions are first class objects so they can be passed around and used just like any other variable. We'll finish this function in the next lesson.

Episode Topics: