With the highly competitive scene that is the indie game market today, anyone who has even the slightest edge might end up at the top percentile of successful games. When you release your game out in the wild, any kind of integration specific for the online stores it's sold in turns from “nice to have” to mandatory, and Steam is no exception.
Any feature your game has on Steam such as Achievements, Cloud Saves, Trading Cards, Controller Support, etc. will help your game list better on the store. After all, these all are filter options when it comes to searching for the next game to play/buy, so why not have your game appear there as well?
A Unity build already done and submitted on Steam.
Steam Cloud provides an easy and transparent remote file storage system for your game. The idea is that you can sync data (e.g. unlocked checkpoints, game state, settings, save/load files) between different PCs that the player has the game installed on.
The focus of this article is the Auto-Cloud setup. Files specified in the Auto-Cloud configuration or written to disk (created, modified, deleted, etc.) using the Cloud API will automatically be replicated to the Steam servers after the game exits.
Decide what type of information you want to store in the Cloud. In my example, I’ll create a serializable class with some generic game properties.
[Serializable]
public class SteamCloudPrefs
{
// Game Data //
public string hp = "";
public string ammo = "";
}
This is an oversimplification, but it works for our example’s needs.
Next is the save and load file mechanism.
using System;
using System.IO;
using System.Runtime.Serialization.Formatters.Binary;
using UnityEngine;
public static class SaveLoadFile
{
private const string FILENAME = "/SteamCloud_BattleForMurk.sav";
public static void Save(SteamCloudPrefs steamCloudPrefs)
{
BinaryFormatter bf = new BinaryFormatter();
FileStream stream = new FileStream(Application.persistentDataPath + FILENAME, FileMode.Create);
bf.Serialize(stream, steamCloudPrefs);
stream.Close();
}
public static SteamCloudPrefs Load()
{
if(File.Exists(Application.persistentDataPath + FILENAME))
{
BinaryFormatter bf = new BinaryFormatter();
FileStream stream = new FileStream(Application.persistentDataPath + FILENAME, FileMode.Open);
SteamCloudPrefs data = bf.Deserialize(stream) as SteamCloudPrefs;
stream.Close();
return data;
}
else
{
Debug.LogError("File not found.");
return null;
}
}
}
There are several key moments that I would like to outline.
BinaryFormatter
is essential for storing data in a non-readable format.
You do not want the players messing around with it and obtaining potentially sensitive data for your game. So, if opened, the file would look something
like this:
0001 0000 00ff ffff ff01 0000 0000 0000
000c 0200 0000 0f41 7373 656d 626c 792d
…
FileStream
is standard C# for input and output of data streams.
Unity provides you with Application.persistentDataPath
which contains the path to a persistent data directory.
You will be using it for storing your files locally on the disk. The location varies on different OS, that’s why it’s important to note down
in advance the exact path based on your needs. In my case, I store my files in %USERPROFILE%/AppData/LocalLow/CompanyName/GameName
(on Windows).
Based on your installation this is your user folder on your local drive, e.g. C:/Users/My-User/AppData. The folder may be hidden by default so remember
to view your hidden files. On OS X (my Mac) I have the location as ~/Library/Application Support/unity.CompanyName.GameName
Adjust your game code so it stores the necessary data upon an event (e.g. when a Scene gets destroyed).
Make sure that it obtains it from that file on startup and uses it to update its state.
public class Main : MonoBehaviour
{
public SteamCloudPrefs SteamStorage = new SteamCloudPrefs();
private void Start()
{
if (SaveLoadFile.Load() != null)
{
SteamStorage = SaveLoadFile.Load();
LocalStorage.hp = SteamStorage.hp;
LocalStorage.ammo = SteamStorage.ammo;
}
}
private void OnDestroy()
{
SteamStorage.hp = LocalStorage.hp;
SteamStorage.ammo = LocalStorage.preBatammotleState;
SaveLoadFile.Save(SteamStorage);
}
}
Sample Mono class that can serve as your hub for storing and loading your data.
Test and if all works, you are now half-way there. Next step is Steam!
When your Unity application stores and loads the config files successfully you have what I call - "a black box". Your application has the single responsibility to read and write the file from within that directory. Your next task is to sync this directory’s contents with Steam’s servers and make sure it is the same across devices.
I highly recommend reading the official Steam documentation end-to-end here: https://partner.steamgames.com/doc/features/cloud
Once you are familiar with it, you can proceed with configuring your app.
The idea is to let Steam do the syncing automatically. It will check the directory and upload its contents to its servers once you exit the game. Before you start the game, it will obtain any new files and download them to that directory to keep things on track. Even if you lack internet connection, upon the next game bootup Steam will warn the player if a newer file version than the one in the cloud exists locally and prompt them with a pop-up window to choose which one to use and overwrite the other.
Set the number of files you want to sync and the maximum file size it should allow.
Remember you wrote down your directory paths earlier? It’s time to put them into use. Scroll down to see the directory setup.
Setup the main directory first for the preferred OS (I imagine that would be Windows in most cases).
After that you can create Root Overrides for the additional OS you support. The overrides will replace the first part of your root path and the second part you’ll have to fill up yourself. It’s straightforward. Just pay attention to what it says in the preview under the input boxes.
Finally, test if all is fine and dandy. Follow the steps for Pre-release Testing from Valve’s docs https://partner.steamgames.com/doc/features/cloud
Good luck with your Steam releases and may your stamina never fail 😊 If you have questions, post them in the comments section bellow and I’ll try to answer them as best I can.
Comments