Adding Game Center Leaderboards

Since roguelikes are highly deterministic, global scoreboards are a great way to let players see who is the best.

This article explains how I implemented leaderboards using Swift and GameKit, along with some design decisions that worked well for this type of game.


Designing the Leaderboards

Rocco Rogue has three difficulty levels:

Initially I considered using a single leaderboard with score multipliers. For example:


easy = 1x
normal = 10x
hard = 100x

However, this quickly leads to several problems:

Instead, I chose a much simpler design.

Each difficulty has its own leaderboard.

Difficulty Leaderboard
Easy Rocco Rogue — Easy
Normal Rocco Rogue — Normal
Hard Rocco Rogue — Hard

This keeps the competition fair and the system easy to understand.


Creating Leaderboards in App Store Connect

Before writing any Swift code, the leaderboards must be created in App Store Connect.

Steps:

  1. Open App Store Connect
  2. Select the app
  3. Go to Features → Game Center
  4. Add three Classic Leaderboards

These leaderboard IDs are referenced by the game code, so they should never be changed after release.


Mapping Difficulty to Leaderboards

Each difficulty corresponds to a leaderboard ID.

enum Difficulty {

    case easy
    case normal
    case hard

    var leaderboardID: String {
        switch self {
        case .easy: return "rocco_easy"
        case .normal: return "rocco_normal"
        case .hard: return "rocco_hard"
        }
    }
}

This keeps the leaderboard logic simple and easy to maintain.


Submitting Scores

Scores are submitted when the player exits a session, wins, or loses.

func submitScore(_ score: Int,
                 difficulty: Difficulty,
                 cheated: Bool) {

    guard GKLocalPlayer.local.isAuthenticated else { return }
    guard !cheated else { return }

    GKLeaderboard.submitScore(
        score,
        context: gameState.seed,
        player: GKLocalPlayer.local,
        leaderboardIDs: [difficulty.leaderboardID]
    )
}

You may notice the use of:

context: gameState.seed

Since it is not practical to create separate leaderboards for every seed, the seed value is stored in the leaderboard context field. This allows players to know which seed generated the score while still sharing the same leaderboard.


Displaying Local Best Scores

Besides Game Center rankings, I also display local best scores on the main menu.

Example:

Best Scores

Easy   12345
Normal 6789
Hard   98765

Players often enjoy trying to beat their own records, so showing local scores can be very motivating.


Syncing Scores with iCloud

To sync best scores across devices, I used:

NSUbiquitousKeyValueStore

It requires enabling iCloud → Key-Value Storage in the Xcode project, but it does not require CloudKit code.

The game still keeps a local copy, while iCloud simply synchronizes the data across the user's devices.


Final Thought

Game Center leaderboards are surprisingly easy to add to an iOS game.

For Rocco Rogue, the key decisions were:

These small features significantly increase replayability for a roguelike game.

Players now have a global scoreboard to compete on while still tracking their own progress locally.


⬅️ Previous | 🏠 Index | Next ➡️