dhmstark
Developing Incrementals

So you've gotten started with your incremental game?

Great job! Hopefully you've got the hang of what we covered in the last article. Let's cover three slightly more advanced topics.

1. Saving

This is a big one. Get this in your game early, because very few people will want to come back to your game if they lose their progress between sessions. You may have heard about saving using browser cookies, but for various reasons that would take too long to cover here, we're not going to bother with them. Instead we'll be using HTML5 localStorage which is much simpler.

Saving the game

The first thing we need to do is get our game data into a state where we can save it. First create yourself a save() function, as this is where we'll do the save logic. You can have the user manually save by calling this from a button, and you can do autosave by calling this from your game loop.

Inside the save() function, we're going to first construct an object containing the data we want, and then we'll store it. The first part is much easier if you're already storing all of your game data in an object - if not, create an object and set the values you want to save as properties of that object. For example, something like this:

var save = {
    cookies: cookies,
    cursors: cursors,
    prestige: prestige
}

When we get around to loading, we'll be accessing the properties of this object and use them to overwrite our existing values, so make sure the properties have sensible names. Obviously this save object can contain nested objects with their own properties if you find that useful for organisation.

Now we've got our object (whether it already exists or we've created it within the save() function), we're going to save it to localStorage. Note that localStorage only stores strings, so we're going to have to use JSON methods (specifically, JSON.stringify() converts an object to a string) to convert our save object.

Very simply, the code to save is as follows:

localStorage.setItem("save",JSON.stringify(save));

assuming that your save variable is named save. Strictly speaking we should put this line in a try/catch statement, but for the moment we're assuming the player is using a modern browser. Feel free to add it yourself if you want to improve compatibility.

You've now saved the game! Of course, a saved game isn't useful without the ability to load it...

Loading the game

Assuming you used the method above, loading is extremely simple. Create a load() function, and make sure you include it in the appropriate places (for example, on page load). This function is going to handle the conversion from your save file back into usable data.

First, we want to load the save from localStorage. This is as simple as:

var savegame = JSON.parse(localStorage.getItem("save"));

We're using the JSON.parse() function to take the string stored in localStorage and turn it back into a usable object. You should now be able to access the properties on the savegame object (eg. savegame.cookies).

Lastly we have to take this new object and get the values from it. This is slightly more complicated than you might expect, since we want to future-proof people's save files for when you add more features.

What we're going to do is individually go through the properties we expect to be on the object and check whether they're defined. This way, if you add a new property to the save file (eg. grandmas) then you won't accidentally set your grandmas variable to undefined should someone load up an old save file. For each property, do the following:

if (typeof savegame.cookies !== "undefined") cookies = savegame.cookies;

(The above assumes that you're storing a global cookies variable.) Once you've gone through all of the properties and used them to set the appropriate variables, your load() function should be finished and ready to use!

For reference, and for testing purposes if nothing else you should probably add this, you can allow players to delete their saves by calling localStorage.removeItem("save") from a button.

2. Dealing with rogue decimals

By now you may well have come across the bane of an incremental game developer's life: rogue decimals. These are numbers where you end up with something like 123.000000000001 or 122.99999999999 instead of a nice, neat, round number like 123.

Why does this happen? It's to do with the way that Javascript handles floating point arithmetic. There's no clean and simple solution, so I'm just going to cover one of the simpler ways of dealing with these numbers.

You'll need another function to handle this. You'll use it by passing in your number as an argument, cleaning it up, and outputting it using a return statement at the bottom.

The simplest way to handle the cleanup, if you know you're never going to pass in a fractional number, is to take the input and use Math.round() on it. This will round the number to the nearest integer. Alternatives are Math.floor() or Math.ceil(), which round down and up respectively.

But what if you want to keep some of those pesky decimal places, just not all of them? Here we'll have to multiply the number by some large amount, say 1,000,000, use Math.round(), and then divide by 1,000,000 again. This way you'll keep some of the decimal points, but catch most if not all floating point errors.

You should end up with something that looks like this:

function prettify(input){
    var output = Math.round(input * 1000000)/1000000;
	return output;
}

And you use it by calling it whenever you need to present a number to the player - for example:

document.getElementById('cookies').innerHTML = prettify(cookies);

3. Google Analytics

One of the most useful tools you can use to monitor how your game is doing is Google Analytics. If you're not familiar with it, it allows website owners to monitor traffic that comes to their site and provides a great deal of extremely useful (though anonymised) information. In particular, you'll want to use it to monitor where people find your game from, and also to pay attention to what players are doing in your game.

Adding Google Analytics to your game

If you can follow instructions, this part is fairly easy. You'll need a Google account and then you'll need to visit http://analytics.google.com. You can sign up to analytics there. Follow the instructions, and eventually it will give you a little snippet of code to include in your website. Make sure you follow the instructions carefully. Look at the page source of other games (or this page) to see how it should look when you're done.

Good work! You should now be able to track when people show up on your site. Let's look at how we can use this information.

Referrals

This is the best part. Go to the Acquisition option in the sidebar, and click All Referrals. This should give you a list of all the sites that send traffic your way. Of course, this doesn't tell you exactly which page - for this you'll want to go to Customization and add a custom report.

In your new custom report, add a metric group that contains Users and Pageviews. Then in Dimension Drilldowns, select Source and then Referral Path. This should now give you much more detail on where your users are coming from specifically - particularly useful if many of them come from sites like Reddit.

Event Tracking

There are lots of uses for event tracking - far more than the scope of this tutorial. Let's just add one sample event that you can use to track actual playtimes rather than relying on session length (which doesn't work well for single page games). You can experiment with more, depending on your game.

An event is a bit of javascript which you can fire at a specific point in your game's code to let Google Analytics know that something has happened. In particular, I'm going to assume that you have an autosave system in your game (that is, that you call your save() function periodically on a timer) and we're going to create an event associated with it.

Within your save() function, add the following line:

_gaq.push(['_trackEvent', 'My Game', 'Save']);

You can also pass in a fourth string in that array if you want more detail. For example, in CivClicker I used a string to represent the save type (i.e. whether the save was called manually by the user clicking a button, or whether it was an autosave). You can see this in action in the CivClicker code.

Once added, the events will start showing up in Events, under the Behavior section. If you add multiple types of events then they'll all be listed here. The major benefit to the above event being fired on autosave, incidentally, is that firing an event keeps the session alive, and makes Google Analytics session data much more reliable and useful.