Tutorial: Saving high scores in your game with castle.storage

Ben Roth
Castle Games Blog
Published in
6 min readOct 16, 2019

Hello! In this tutorial, we’ll create a tiny Castle game in which the player clicks a box to earn points. Next, we’ll save the player’s best score using Castle’s storage API. Finally, we’ll load their score the next time the game loads.

If you just want to skip to the end and see the final code for the completed tutorial, check out the finished example on GitHub.

Why do we want this?

When a player exits your game and loads it again later, they lose all their game state. Often, you want to remember some information for next time. One example is to show the player their all-time best score, so they can try to beat it later. Castle’s Storage API helps with this kind of thing. It will save player-specific or game-specific data to Castle’s server so your game can load it later.

Create a new Castle Project

Within the Castle Client, click Create, and then select New blank project. If this is your first time creating a Castle project, you might be interested in following this guide to become more familiar with creating projects in Castle.

Creating a new project in Castle

Make a clickable box

We’ll start by making game where you can click a box to earn points. Since this tutorial is about saving a high score, we won’t go into much detail about this part of the code. We just need a way to earn points. Hopefully, your own game is way more fun.

To start the tutorial, put these ~30 lines of code in main.lua for your project, then reload your game in Castle.

A box clicking game

Try clicking the box a few times. Congratulations! You are an expert box clicker. Try not to get carried away.

Use castle.storage to save the score

Try reloading your box game a few times by pressing Ctrl/Cmd+R in Castle. Notice that the score resets to zero every time the game reloads. What if we want to track your box clicking progress? Our goal is to remember the highest number of times you have ever clicked the box.

castle.storage is an API built in to Castle that provides key-value storage for your game data on Castle’s servers. This lets you save information from your game (in our case, the player’s high score) which will persist after someone closes the game and comes back later.

First, we’ll need to add a high score to our local game State.

Adding a place to store the high score in the game state.

Next, we want to check if the player’s current score is their best score ever, and if so, save the high score for later.

Comparing the player’s score to their highest score.

Lastly, we need to implement that method saveHighScore(). This is where we’ll put our first call to the castle.storage API. Write this new method at the end of main.lua:

local HIGH_SCORE_STORAGE_KEY = 'highscore'function saveHighScore(score)
State.highScore = score
network.async(function()
castle.storage.set(HIGH_SCORE_STORAGE_KEY, score)
end)
end

The most important line is the call to castle.storage.set(), which takes a key and a value. We picked the key “highscore” because it seems like a nice way to remember what this is for, but it could be whatever you want. For the value, we store the new score provided to the method.

The castle.storage.set() method saves the information to Castle’s server for later retrieval, but doesn’t have any effect on our game yet. Therefore, we also update State.highScore in our local game state.

Check out the full diff from this step of the tutorial. If you reload your project now, it will work, but it won’t display anything new, because we still need to load the high score and draw it.

What about this stuff with network.async()? We’ll come back to that later.

Use castle.storage to load the high score

Saving the high score doesn’t do us much good unless we can load it later.

First, let’s add some code to print the high score if we have it:

Drawing the high score

Next, let’s add a new method called castle.storage.get() which knows how to retrieve the high score from Castle’s servers:

function loadHighScore()
network.async(function()
local highScore = castle.storage.get(HIGH_SCORE_STORAGE_KEY)
if not (highScore == nil) then
State.highScore = highScore
end
end)
end

Add this method at the bottom of main.lua. Notice that this complements the earlier saveHighScore() method we wrote. It uses the same key that we used before to save the data, and it uses get() instead of set(). Also, it is wrapped inside the same network code we saw before.

Now we just need to actually use this new method. We will call it when the game first loads:

function love.load()
loadHighScore()
end

At this point, you should be able to reload your game and see your actual high score displayed in the game. Reload as many times as you want, and notice that the high score count persists between loads.

Displaying the player’s high score in the box clicking game

Check out the full diff for this step of the tutorial.

You’re done!

You now have a game where you can click a box to earn points, save the highest score, and reload it the next time somebody plays your game.

If you haven’t already, check out the completed code for the whole tutorial.

A word about scope

What happens if someone else plays your game and clicks the box even more? Do they knock out your high score, or do they see their own?

It turns out that castle.storage.set() and castle.storage.get() are sandboxed to the person that is currently playing the game. If somebody else plays, they will have a totally different high score, and they won’t be able to access your score, or that of any other player. Therefore, this API is good for player-specific world state in your game. Furthermore, if a totally different game uses the same “highscore” key, it won’t have any impact on your game. This behavior is built in to castle.storage, and you don’t have to do any special work to take advantage of it.

Castle also includes two methods castle.storage.setGlobal() and castle.storage.getGlobal() which affect everyone in your game. These are good for, e.g., a game-wide leaderboard where all the players should see the same information.

What is network.async?

You might notice that it takes a small interval before your high score displays after reloading the box game. Because castle.storage performs network requests to Castle’s servers, saving and loading data takes some time. Compared to rendering your game, it actually takes a lot of time.

For that reason, it’s not a good idea to perform this work synchronously on your game’s graphics thread. In fact, Castle won’t let you do that, and castle.storage will throw an error if you try to run it this way.

Instead, we wrap the castle.storage calls in a network.async callback so that they can be executed asynchronously without blocking the game.

Thanks for reading!

Let us know on twitter, Discord, or in Castle chat if you make something fun out of this.

--

--