Хочешь уверенно проходить IT-интервью?

Мы понимаем, как сложно подготовиться: стресс, алгоритмы, вопросы, от которых голова идёт кругом. Но с AI тренажёром всё гораздо проще.
💡 Почему Т1 тренажёр — это мастхэв?
- Получишь настоящую обратную связь: где затык, что подтянуть и как стать лучше
- Научишься не только решать задачи, но и объяснять своё решение так, чтобы интервьюер сказал: "Вау!".
- Освоишь все этапы собеседования, от вопросов по алгоритмам до диалога о твоих целях.
Зачем листать миллион туториалов? Просто зайди в Т1 тренажёр, потренируйся и уверенно удиви интервьюеров. Мы не обещаем лёгкой прогулки, но обещаем, что будешь готов!
Реклама. ООО «Смарт Гико», ИНН 7743264341. Erid 2VtzqwP8vqy
Одна из самых важных вещей в игре – сохранение пользовательских данных: настроек и игровых результатов. Когда-то игры были короткими, и сохранять там было особенно нечего. В лучшем случае игра записывала самый высокий балл для составления рейтинга. Но технологии стремительно развивались, и геймдев не оставался в стороне.
Сейчас нам нужно хранить тонны данных, включая прогресс и статистику игрока. Эта потребность актуальна как для игр с огромным открытым миром, так и для простых линейных приключений. Зачастую требуется несколько сессий, чтобы завершить игру, значит, нужен способ сохраниться и вернуться позже к тому же моменту. Даже для простых коротких игр может быть полезно записывать какую-то информацию.
Подготовка
Unity предлагает сразу два способа сохранять игровые данные – попроще и посложнее:
- Попроще – встроенная система PlayerPrefs. Устанавливаете значение для ключа, нажимаете Save – и все готово.
- Посложнее – сериализация данных и запись в файл для дальнейшего использования.
У обоих методов есть преимущества и недостатки, поэтому для конкретного случая важно выбрать правильный вариант. Для демонстрации нам потребуется некоторая минимальная конфигурация. Создадим новый проект в Unity, за основу для простоты возьмем 2D-шаблон.

Добавим два скрипта – SavePrefs
и SaveSerial
– для реализации двух методов.
Чтобы создать скрипт, кликните правой кнопкой мыши в окне Assets
и выберите пункты Create
-> C# Script
.

Начнем с более простого способа – SavePrefs
.
Кликните два раза по скрипту, чтобы открыть его в редакторе Visual Studio.
Простой способ: PlayerPrefs
Для начала можно закомментировать или удалить методы Start
и Update
, так как они не потребуются для демонстрации сохранения данных. Затем нам понадобятся несколько переменных.
int intToSave;
float floatToSave;
string stringToSave = "";
С помощью метода OnGui создадим пользовательский интерфейсдля визуального управления этими переменными.
- Две кнопки – для увеличения значений
intToSave
иfloatToSave
. - Текстовое поле – для переменной
stringToSave
. - Несколько лейблов для отображения текущих значений переменных.
- Три кнопки действий, чтобы сохранить, загрузить и сбросить данные.
void OnGUI()
{
if (GUI.Button(new Rect(0, 0, 125, 50), "Raise Integer"))
intToSave++;
if (GUI.Button(new Rect(0, 100, 125, 50), "Raise Float"))
floatToSave += 0.1f;
stringToSave = GUI.TextField(new Rect(0, 200, 125, 25), stringToSave, 15);
GUI.Label(new Rect(375, 0, 125, 50), "Integer value is "
+ intToSave);
GUI.Label(new Rect(375, 100, 125, 50), "Float value is "
+ floatToSave.ToString("F1"));
GUI.Label(new Rect(375, 200, 125, 50), "String value is "
+ stringToSave);
if (GUI.Button(new Rect(750, 0, 125, 50), "Save Your Game"))
SaveGame();
if (GUI.Button(new Rect(750, 100, 125, 50), "Load Your Game"))
LoadGame();
if (GUI.Button(new Rect(750, 200, 125, 50), "Reset Save Data"))
ResetData();
}
Сохранение
Создадим метод SaveGame
, который будет отвечать за сохранение данных:
void SaveGame()
{
PlayerPrefs.SetInt("SavedInteger", intToSave);
PlayerPrefs.SetFloat("SavedFloat", floatToSave);
PlayerPrefs.SetString("SavedString", stringToSave);
PlayerPrefs.Save();
Debug.Log("Game data saved!");
}
Как видим, для сохранения данных с PlayerPrefs
нужно лишь несколько строчек кода. Здесь мы устанавливаем ключи настройки ("SavedInteger"
или "SavedFloat"
) и их значения передаем в соответствующие методы объекта PlayerPrefs
. После того, как все нужные данные записаны, сохраняем их, вызвав метод PlayerPrefs.Save
. Выводим сообщение в отладочную консоль, о том, что операция успешно выполнена.
Должно быть, вам интересно, где сейчас физически находятся эти данные. Они записываются в файл в папке проекта. В Windows его можно найти по адресу HKEY_CURRENT_USER\Software\Unity\UnityEditor\[company name]\[project name]
. Именно отсюда запускается игра из редактора. В exe-файле их можно найти по адресу HKEY_CURRENT_USER\Software\[company name]\[project name]
. На Mac OS согласно документации файлы PlayerPrefs находятся в папке ~/Library/Preferences
, в файле с названием unity.[company name].[product name].plist
.

Загрузка
Загрузка сохраненных данных – это, по сути, сохранение наоборот. Необходимо взять значения, хранящиеся в PlayerPrefs
и записать их в переменные.
Хорошей практикой является предварительная проверка наличия нужных ключей. Для этого предназначен метод HasKey
. Достаточно проверить хотя бы один ключ из установленных в методе SaveGame
, чтобы понимать, есть ли у пользователя сохраненные данные.
Если данных нет, выведем в консоль сообщение об ошибке.
void LoadGame()
{
if (PlayerPrefs.HasKey("SavedInteger"))
{
intToSave = PlayerPrefs.GetInt("SavedInteger");
floatToSave = PlayerPrefs.GetFloat("SavedFloat");
stringToSave = PlayerPrefs.GetString("SavedString");
Debug.Log("Game data loaded!");
}
else
Debug.LogError("There is no save data!");
}
Сброс
Для удаления всех данных, хранящихся в PlayerPrefs
, нужно использовать метод PlayerPrefs.DeleteAll
.
void ResetData()
{
PlayerPrefs.DeleteAll();
intToSave = 0;
floatToSave = 0.0f;
stringToSave = "";
Debug.Log("Data reset complete");
}
В методе ResetData
мы очищаем хранилище, а также обнуляем все переменные.
Теперь проверим весь этот код в деле. Сохраните файл и вернитесь в редактор Unity. Прикрепите скрипт SavePrefs
к какому-нибудь объекту, например, к Main Camera
.

Теперь запустите игру и начните взаимодействовать с GUI-элементами. Изменяйте переменные, нажимая на кнопки и заполняя текстовое поле. Когда будете готовы, сохраните данные кнопкой Save Your Game
. После этого остановите и перезапустите игру и нажмите на кнопку Load Your Game
. Если вы всё сделали правильно, значения переменных немедленно изменятся на те, что вы сохранили в предыдущем запуске.
Чтобы очистить PlayerPrefs
, кликните Reset Save Data
.

Недостатки
Этот способ кажется простым и эффективным. Почему бы всегда не использовать PlayerPrefs
для сохранения пользовательских данных?
Из-за его невысокой безопасности. Вы точно не захотите сохранять таким способом данные, в которые не должен вмешиваться игрок: например, количество доступной игроку игровой валюты, или статистика.
Из названия понятно, что PlayerPrefs
предназначен для хранения пользовательских предпочтений и других неигровых данных. Например, этот метод идеально подходит для записи настроек интерфейса: цветовой темы, размера элементов и т. п.
Другая проблема – в недостаточной гибкости. В PlayerPrefs
вы можете сохранять только числа и строки, поэтому он не подходит для данных, имеющих сложную структуру.
К счастью, у нас есть еще один способ, более гибкий и безопасный.
Сложный способ: Сериализация
Для демонстрации сложного способа сохранения данных в Unity откроем скрипт SaveSerial
.
Снова определим переменные и создадим интерфейс для управления ими. Метод OnGUI
похож на тот, что мы только что писали:
int intToSave;
float floatToSave;
bool boolToSave;
void OnGUI()
{
if (GUI.Button(new Rect(0, 0, 125, 50), "Raise Integer"))
intToSave++;
if (GUI.Button(new Rect(0, 100, 125, 50), "Raise Float"))
floatToSave += 0.1f;
if (GUI.Button(new Rect(0, 200, 125, 50), "Change Bool"))
boolToSave = boolToSave ? boolToSave = false : boolToSave = true;
GUI.Label(new Rect(375, 0, 125, 50), "Integer value is "
+ intToSave);
GUI.Label(new Rect(375, 100, 125, 50), "Float value is "
+ floatToSave.ToString("F1"));
GUI.Label(new Rect(375, 200, 125, 50), "Bool value is "
+ boolToSave);
if (GUI.Button(new Rect(750, 0, 125, 50), "Save Your Game"))
SaveGame();
if (GUI.Button(new Rect(750, 100, 125, 50), "Load Your Game"))
LoadGame();
if (GUI.Button(new Rect(750, 200, 125, 50), "Reset Save Data"))
ResetData();
}
Для сериализации данных потребуется добавить несколько директив using
:
using System;
using System.Runtime.Serialization.Formatters.Binary;
using System.IO;
Сохранение
Создадим новый сериализуемый класс SaveData
, который будет содержать сохраняемые данные.
[Serializable]
class SaveData
{
public int savedInt;
public float savedFloat;
public bool savedBool;
}

Обратите внимание, три переменные в классе SaveData
соответствуют переменным из класса SaveSerial
. Для сохранения мы будем передавать значения из SaveSerial
в SaveData
, а затем сериализовать последний.
Добавим в класс SaveSerial
метод SaveGame
:
void SaveGame()
{
BinaryFormatter bf = new BinaryFormatter();
FileStream file = File.Create(Application.persistentDataPath
+ "/MySaveData.dat");
SaveData data = new SaveData();
data.savedInt = intToSave;
data.savedFloat = floatToSave;
data.savedBool = boolToSave;
bf.Serialize(file, data);
file.Close();
Debug.Log("Game data saved!");
}
Объект BinaryFormatter
предназначен для сериализации и десериализации. При сериализации он отвечает за преобразование информации в поток бинарных данных (нулей и единиц).
FileStream
и File
нужны для создания файла с расширением .dat
. Константа Application.persistentDataPath
содержит путь к файлам проекта: C:\Users\[user]\AppData\LocalLow\[company name]
.
В методе SaveGame
создается новый экземпляр класса SaveData
. В него записываются текущие данные из SaveSerial
, которые нужно сохранить. BinaryFormatter
сериализует эти данные и записывает их в файл, созданный FileStream
. Затем файл закрывается, в консоль выводится сообщение об успешном сохранении.
Загрузка
Метод LoadGame
– это, как и раньше, SaveGame
наоборот:
void LoadGame()
{
if (File.Exists(Application.persistentDataPath
+ "/MySaveData.dat"))
{
BinaryFormatter bf = new BinaryFormatter();
FileStream file =
File.Open(Application.persistentDataPath
+ "/MySaveData.dat", FileMode.Open);
SaveData data = (SaveData)bf.Deserialize(file);
file.Close();
intToSave = data.savedInt;
floatToSave = data.savedFloat;
boolToSave = data.savedBool;
Debug.Log("Game data loaded!");
}
else
Debug.LogError("There is no save data!");
}
- Сначала ищем файл с сохраненными данными, который мы создали в методе SaveGame.
- Если он существует, открываем его и десериализуем с помощью
BinaryFormatter
. - Передаем записанные в нем значения в переменные класса
SaveSerial
. - Выводим в отладочную консоль сообщение об успешной загрузке.
Если файла с данными не окажется в папке проекта, выведем в консоль сообщение об ошибке.
Сброс
Наконец, реализуем метод для сброса сохранения. Он похож на тот ResetData
, который мы написали для очистки PlayerPrefs
, но включает в себя пару дополнительных шагов.
Сначала нужно убедиться, что файл, который мы хотим удалить, существует. После его удаления обнуляем значения переменных класса SaveSerial до дефолтных и выводим сообщение в консоль.
Если файла нет, выводим сообщение об ошибке.
void ResetData()
{
if (File.Exists(Application.persistentDataPath
+ "/MySaveData.dat"))
{
File.Delete(Application.persistentDataPath
+ "/MySaveData.dat");
intToSave = 0;
floatToSave = 0.0f;
boolToSave = false;
Debug.Log("Data reset complete!");
}
else
Debug.LogError("No save data to delete.");
}
Скрипт метода сериализации готов, теперь его можно проверить в деле. Сохраните код, вернитесь в Unity и запустите игру. Привяжите скрипт SaveSerial
к объекту Main Camera
(не забудьте деактивировать предыдущий).

После запуска вы увидите тот же самый интерфейс, что и раньше. Попробуйте изменить переменные и сохранить игру.
В этот раз файл будет сохранен по "постоянному пути данных" игры. В Windows это C:\Users\username\AppData\LocalLow\project name
, в Mac – ~/Library/Application Support/companyname/productname
согласно документации.
Перезапустите игру и загрузите данные, нажав на кнопку Load Your Game
. Значения переменных должны измениться на те, что вы сохранили ранее.
Также вы можете удалить все сохраненные данные кнопкой Reset Save Data
.

Заключение
Сохранение данных – важная часть большинства игр, за исключением лишь некоторых специфических исключений (например, игры с очень короткими игровыми сессиями или механикой перманентной смерти, в которой после каждого проигрыша вся игра сбрасывается к началу).
Помните о том, что PlayerPrefs
– это простой способ сохранить настройки и предпочтения пользователя, но его не следует использовать для записи важных игровых данных.
Сериализация и запись в файл – более сложный путь, но он дает вам бóльшую гибкость и безопасность.
Какие именно данные сохранять и каким способом – зависит от особенностей проекта.
Комментарии