← Cheatsheets
Tags: unity, save, load, json, serialization, playerprefs, binary, cloud-saves, game-dev
Last updated: 2026-06-27
Save & Load Systems Cheatsheet
Quick Reference
| Method | Best For | Limitations |
| PlayerPrefs | Tiny data, settings | No nesting, strings only, not secure |
| JSON + File | Most games, human-readable | Not binary-efficient |
| BinaryFormatter | Compact saves | Deprecated (security), .NET only |
| Odin Serializer | Complex types | Third-party asset |
| Cloud Save | Cross-device, backup | Requires platform SDK |
PlayerPrefs
# Save
PlayerPrefs.SetInt("Level", 5);
PlayerPrefs.SetFloat("Volume", 0.8f);
PlayerPrefs.SetString("Name", "Player1");
PlayerPrefs.Save();
# Load
int level = PlayerPrefs.GetInt("Level", 1);
float vol = PlayerPrefs.GetFloat("Volume", 1f);
string name = PlayerPrefs.GetString("Name", "");
if (PlayerPrefs.HasKey("Level")) { }
PlayerPrefs.DeleteKey("Level");
PlayerPrefs.DeleteAll();
PlayerPrefs Limitations
- Only int, float, string. No arrays, no objects.
- Stored in registry (Windows) or plist (Mac).
- Easily editable by players — not secure.
- Use only for settings/preferences, never game state.
JSON Serialization
Simple SaveData Class
[System.Serializable]
public class SaveData {
public int level;
public float health;
public Vector3 position;
public List<string> inventory;
}
public void Save() {
SaveData data = new SaveData {
level = 5, health = 80f,
position = player.position,
inventory = new List<string> { "Sword", "Potion" }
};
string json = JsonUtility.ToJson(data, prettyPrint: true);
File.WriteAllText(Application.persistentDataPath + "/save.json", json);
}
public SaveData Load() {
string path = Application.persistentDataPath + "/save.json";
if (File.Exists(path)) {
string json = File.ReadAllText(path);
return JsonUtility.FromJson<SaveData>(json);
}
return new SaveData();
}
JsonUtility Limitations
- No Dictionary support. No polymorphism.
- No private field serialisation on non-Unity types.
- Workaround:
List<KeyValuePair> or Newtonsoft.Json.
Newtonsoft.Json (for complex types)
using Newtonsoft.Json;
string json = JsonConvert.SerializeObject(data, Formatting.Indented,
new JsonSerializerSettings {
TypeNameHandling = TypeNameHandling.Auto,
ReferenceLoopHandling = ReferenceLoopHandling.Ignore
});
SaveData data = JsonConvert.DeserializeObject<SaveData>(json, settings);
Binary Formatting (Legacy)
# ⚠ Deprecated due to security vulnerabilities.
using System.Runtime.Serialization.Formatters.Binary;
[System.Serializable]
public class SaveData { /* ... */ }
public void SaveBinary(SaveData data) {
BinaryFormatter bf = new BinaryFormatter();
using FileStream fs = File.Create(path);
bf.Serialize(fs, data);
}
public SaveData LoadBinary() {
BinaryFormatter bf = new BinaryFormatter();
using FileStream fs = File.OpenRead(path);
return (SaveData)bf.Deserialize(fs);
}
AES Encryption (Simple)
using System.Security.Cryptography;
using System.Text;
public static string Encrypt(string json, string password) {
byte[] key = SHA256.Create().ComputeHash(Encoding.UTF8.GetBytes(password));
using Aes aes = Aes.Create();
aes.Key = key;
aes.GenerateIV();
using ICryptoTransform encryptor = aes.CreateEncryptor();
byte[] plainBytes = Encoding.UTF8.GetBytes(json);
byte[] cipherBytes = encryptor.TransformFinalBlock(
plainBytes, 0, plainBytes.Length);
byte[] result = new byte[aes.IV.Length + cipherBytes.Length];
Buffer.BlockCopy(aes.IV, 0, result, 0, aes.IV.Length);
Buffer.BlockCopy(cipherBytes, 0, result, aes.IV.Length, cipherBytes.Length);
return Convert.ToBase64String(result);
}
Save Slots & Multiple Saves
public void SaveToSlot(int slot) {
SaveData data = GatherSaveData();
string json = JsonUtility.ToJson(data);
string path = $"{Application.persistentDataPath}/slot{slot}.json";
File.WriteAllText(path, json);
}
public SaveData LoadFromSlot(int slot) {
string path = $"{Application.persistentDataPath}/slot{slot}.json";
return File.Exists(path)
? JsonUtility.FromJson<SaveData>(File.ReadAllText(path))
: null;
}
Save Metadata (Timestamp, Thumbnail)
public class SaveSlotInfo {
public int slot;
public string timestamp;
public string sceneName;
public float playTime;
}
Auto-Save & Checkpoints
public float autoSaveInterval = 60f;
float timer;
void Update() {
timer += Time.deltaTime;
if (timer >= autoSaveInterval) {
SaveToSlot(0); # Auto-save slot
timer = 0f;
}
}
void OnTriggerEnter(Collider other) {
if (other.CompareTag("Checkpoint"))
SaveToSlot(1); # Checkpoint slot
}
Cloud Saves
Unity Cloud Save
using Unity.Services.CloudSave;
await CloudSaveService.Instance.Data.Player.SaveAsync(
new Dictionary<string, object> { { "SaveSlot1", json } });
var data = await CloudSaveService.Instance.Data.Player.LoadAsync(
new HashSet<string> { "SaveSlot1" });
Save File Locations
| Platform | Path |
| Windows / Mac / Linux | Application.persistentDataPath |
| iOS / Android | Application.persistentDataPath |
| WebGL | Application.persistentDataPath (IndexedDB) |
| Console | Platform-specific, requires SDK |
Best Practices
| Practice | Why |
| Version your save data | Migrate old saves when game updates |
| Validate on load | Check for corrupted or tampered saves |
| Hash/checksum integrity | Detect tampering for leaderboards |
| Async save/load | Avoid frame hitches during I/O |
| Backup before overwrite | Copy old save to .bak before saving |
| Compress large saves | GZip reduce JSON bloat ~10× |
| Encrypt sensitive data | Player currency, unlockables |
| Test save on all platforms | Paths and permissions differ |