Втрата прогресу — найгірше, що може статися з гравцем. Пройшов 30 рівнів, купив бустери, розблокував персонажів — і після перевстановлення або зміни пристрою нічого нема. Рейтинг у магазині падає, користувач йде. Нещодавно до нас звернувся клієнт: після оновлення всі збереження виявилися битими, 15% активної аудиторії пішло за тиждень. Після впровадження нашої системи відтік скоротився до 2%, а рейтинг у магазині виріс з 3.2 до 4.5 зірок. Ми знаємо, як цього уникнути.
Ми розробляємо систему збереження під ключ для ігор на Unity, Android, iOS та кроссплатформенних проєктах. За 5 років реалізували понад 40 ігор, гарантуючи збереженість даних та безшовний користувацький досвід. Оцінимо ваш проєкт — зв'яжіться з нами, щоб обговорити деталі.
Які дані потрібно зберігати і де?
Прогрес гри включає кілька типів даних з різними вимогами до надійності та швидкості доступу:
- Критичні дані: рівень, валюта, покупки. Повинні синхронізуватися з сервером — ніколи не втрачатися. Зберігаються локально + хмара.
- Ігровий прогрес: пройдені рівні, досягнення, розблоковані предмети. Локально + опціональна синхронізація.
- Користувацькі налаштування: гучність, керування, графіка. Тільки локально — втрата некритична.
- Сесійні дані: поточний рівень, позиція, тимчасові бафи. Тільки в пам'яті — не потребують персистентності.
| Тип даних | Місце зберігання | Частота збереження | Ризик втрати |
|---|---|---|---|
| Критичні | Локально + хмара | Після кожної зміни | Мінімальний |
| Ігровий прогрес | Локально + опціонально хмара | При проходженні рівнів | Низький |
| Налаштування | Локально | За запитом користувача | Приймається |
| Сесійні | Пам'ять | Не зберігаються | Високий (але допустимий) |
Локальне зберігання
Для простих ігор — JSON-файл або SharedPreferences/UserDefaults. Для складного прогресу з безліччю сутностей — SQLite через Room.
@Entity(tableName = "save_data")
data class SaveDataEntity(
@PrimaryKey val playerId: String,
val level: Int,
val experience: Long,
val coins: Long,
val gems: Int,
val unlockedLevels: String, // JSON array
val inventory: String, // JSON array
val achievements: String, // JSON array
val settings: String, // JSON object
val lastSavedAt: Long,
val version: Int = 1 // для міграцій формату
)Для Unity — PlayerPrefs для простих значень, Application.persistentDataPath + бінарний файл для складних структур. BinaryFormatter застарів у сучасних версіях Unity — використовуємо JsonUtility або Newtonsoft.Json + File.WriteAllBytes.
// Unity: збереження через JsonUtility
[Serializable]
public class SaveData
{
public int level;
public long coins;
public List<string> unlockedItems = new List<string>();
public int[] levelStars; // 3 зірки на кожен рівень
public long savedAt;
}
public class SaveSystem : MonoBehaviour
{
private const string SAVE_FILE = "save.json";
public static void Save(SaveData data)
{
data.savedAt = DateTimeOffset.UtcNow.ToUnixTimeMilliseconds();
string json = JsonUtility.ToJson(data);
string path = Path.Combine(Application.persistentDataPath, SAVE_FILE);
// Спочатку пишемо в temp, потім перейменовуємо — атомарна операція
string tempPath = path + ".tmp";
File.WriteAllText(tempPath, json);
File.Move(tempPath, path, overwrite: true);
}
}Запис через temp-файл з подальшим перейменуванням — захист від корумпованого файлу при креші під час запису.
Хмарна синхронізація
Для кроссплатформенних ігор — власний бекенд. Для iOS-only — Game Center + iCloud. Для Android — Google Play Games Services. Для Unity — обгортки над обома.
// Android: Google Play Games Services
GamesSignInClient.signIn().addOnCompleteListener { task ->
if (task.isSuccessful) {
PlayGames.getSnapshotsClient(activity)
.open(SNAPSHOT_NAME, true, SnapshotsClient.RESOLUTION_POLICY_MOST_RECENTLY_MODIFIED)
.addOnSuccessListener { dataOrConflict ->
if (dataOrConflict.isConflict) {
resolveConflict(dataOrConflict.conflict)
} else {
loadFromSnapshot(dataOrConflict.data)
}
}
}
}Google Play Games Snapshots API автоматично керує конфліктами при RESOLUTION_POLICY_MOST_RECENTLY_MODIFIED — перемагає більш свіже збереження. Для більшості ігор цього достатньо.
Порівняйте хмарні рішення:
| Платформа | Сервіс | Автоматичне вирішення конфліктів | Ліміт розміру збереження |
|---|---|---|---|
| iOS | CloudKit | Так (останній запис) | 1 MB на запис, 10 MB загальний |
| Android | Google Play Games Snapshots | Так (останній запис або ручне) | 3 MB |
| Кроссплатформа | Власний бекенд | Налаштовується | Необмежений |
Згідно з документацією Apple, CloudKit забезпечує автоматичне вирішення конфліктів на основі часових міток. В одному проєкті впровадження серверної валідації дозволило заощадити понад $10,000 на місяць на поверненнях від чітерів.
Чому важлива міграція формату?
Формат даних змінюється з оновленнями гри. Потрібна міграційна система:
class SaveMigrator {
fun migrate(data: SaveDataEntity): SaveDataEntity {
var current = data
while (current.version < CURRENT_VERSION) {
current = when (current.version) {
1 -> migrateV1toV2(current)
2 -> migrateV2toV3(current)
else -> throw IllegalStateException("Unknown version: ${current.version}")
}
}
return current
}
private fun migrateV1toV2(data: SaveDataEntity): SaveDataEntity {
// У версії 2 додали daily challenges progress
return data.copy(
dailyChallenges = "{}",
version = 2
)
}
}Перевіряємо версію при завантаженні — якщо стара, мігруємо до актуальної та зберігаємо заново.
Як забезпечити захист від чітерства?
Для ігор з монетизацією — хеш для виявлення модифікації файлу:
fun computeChecksum(data: SaveDataEntity): String {
val content = "${data.playerId}|${data.coins}|${data.gems}|${data.level}|${SECRET_SALT}"
return MessageDigest.getInstance("SHA-256")
.digest(content.toByteArray())
.fold("") { str, byte -> str + "%02x".format(byte) }
}Серверна валідація — надійніше. Критичні операції (покупка за gems) проходять через сервер, клієнт не може просто записати потрібне значення у файл. В одному проєкті ми знизили кількість чітерів на 95% всього за місяць після впровадження серверної перевірки кожної покупки.
Як вибрати між CloudKit та Google Play Games?
Вибір залежить від платформи та вимог. Якщо гра тільки на iOS — CloudKit та Game Center. Для Android — Google Play Games Snapshots. Для кроссплатформенного проєкту частіше обирають власний бекенд для єдності. Ми допомагаємо визначитися на етапі аудиту — замовте консультацію, щоб отримати оптимальне рішення.
Що входить у розробку системи збереження
Ми надаємо:
- Аналіз вимог до даних та вибір архітектури
- Реалізацію локального сховища (Room, DataStore, CoreData, PlayerPrefs)
- Підключення хмарної синхронізації (iCloud, Google Play Games, власний сервер)
- Механізм міграції формату при оновленнях
- Захист від злому (хеші, серверна валідація)
- Тестування на різних пристроях та версіях ОС
- Документацію та навчання команди
Терміни — від 2 до 4 тижнів залежно від складності. Ми проводимо навантажувальне тестування: симулюємо 1000 одночасних збережень, щоб переконатися у відмовостійкості. Середня окупність інвестицій — 3 місяці за рахунок лояльності користувачів. Зв'яжіться з нами, щоб отримати консультацію та оцінку вашого проєкту.







