Начну с того, что в Unity есть своя система сохранений PlayerPrefs, но она имеет ряд минусов:
1) Не всем хочется засорять реестр.
2) Нет возможности сделать несколько слотов сохранения (костыли с припиской не считаются).
3) Нет возможности передать сохранение или перенести на другое устройство.
Поэтому я решил сделать свою систему сохранений.

В своих целях я писал на местной интерпретации Javascript, но, думаю, можно перенести и на C#.
Для начала получаем ссылку на текущий файл. Для примера возьмем локальную папку name_data/StreamingAssets/Saves. Можно выставить любой путь.
Название файла задаем в переменной saveName, плюс к нему будет приписываться порядковый номер сохранения.
Если слоты сохранения не нужны, можно убрать PlayerPrefs.GetInt(«CurrentSave»).
Назначать номер слота — PlayerPrefs.SetInt(«CurrentSave», num), где num — порядковый номер.
".db" — формат файла. На самом деле, это обычный текстовый документ, но лучше придумать свой формат, чтобы не искушать пользователя отредактировать.
Итак, мы имеем ссылку на файл в переменной playerDataPath. Далее проверяем нахождения файла по ссылке, и если его там нет — создаем.
Если он там есть — считываем в массив Saves.
И да, в массиве стоит сразу указать количество строк, которое нам потребуется, так как такой тип массивов не динамический.
Создадим функцию Save для сохранения. При вызове построчно записываем массив в наш текстовый файл.
Основная часть готова.
В чем плюс этого способа — не имеет значения, в каком формате мы сохраняем данные — в float, int или String — все равно все хранится в виде текста.
При получении просто преобразовываем в нужный формат. Для удобства сделаем названия функций такими же, как и в PlayerPrefs.
Например, нам нужен int. Вызываем функцию GetInt с номером нужной нам строчки.
Для сохранения вызываем функцию SetInt, куда отправляем номер строчки и число для сохранения.
Аналогично можно сделать и с другими типами переменных.
Можно сделать удаление конкретного сохранения.
Для этого вызываем DeleteSaveFile с номером нужного файла. Если файл только один — номер будет равен нулю.
Или, например, можно очистить конкретный файл.
Если нужно, чтобы сохранение происходило про выходе из игры или при загрузке другого уровня, пользуемся встроенной функцией OnDisable. Она вызывается автоматически.
Вот здесь я сделал демонстрацию с кубиками:
Можно скачать уже готовый ассет для юнити, вместе с демкой из видео.
Подробное FAQ по уже готовому скрипту.
Всем спасибо за внимание, буду очень рад критике.
1) Не всем хочется засорять реестр.
2) Нет возможности сделать несколько слотов сохранения (костыли с припиской не считаются).
3) Нет возможности передать сохранение или перенести на другое устройство.
Поэтому я решил сделать свою систему сохранений.
В своих целях я писал на местной интерпретации Javascript, но, думаю, можно перенести и на C#.
Создание и импорт файла
Для начала получаем ссылку на текущий файл. Для примера возьмем локальную папку name_data/StreamingAssets/Saves. Можно выставить любой путь.
Название файла задаем в переменной saveName, плюс к нему будет приписываться порядковый номер сохранения.
playerDataPath = Application.streamingAssetsPath +'/Saves/'+ saveName + PlayerPrefs.GetInt(«CurrentSave») + ".db";
Если слоты сохранения не нужны, можно убрать PlayerPrefs.GetInt(«CurrentSave»).
Назначать номер слота — PlayerPrefs.SetInt(«CurrentSave», num), где num — порядковый номер.
".db" — формат файла. На самом деле, это обычный текстовый документ, но лучше придумать свой формат, чтобы не искушать пользователя отредактировать.
Итак, мы имеем ссылку на файл в переменной playerDataPath. Далее проверяем нахождения файла по ссылке, и если его там нет — создаем.
if (!File.Exists(playerDataPath)){
var dataWriter: StreamWriter = new StreamWriter(playerDataPath);
}
Если он там есть — считываем в массив Saves.
else {
var dataReader = new File.OpenText(playerDataPath);
for(var i: int = 1; i < Saves.length; i++)
{
Saves[i] = dataReader.ReadLine();
}
}
И да, в массиве стоит сразу указать количество строк, которое нам потребуется, так как такой тип массивов не динамический.
Сохранение.
Создадим функцию Save для сохранения. При вызове построчно записываем массив в наш текстовый файл.
function Save(){
var dataWriter: StreamWriter = new StreamWriter(playerDataPath);
for(var i: int = 1; i < Saves.length; i++)
{
dataWriter.WriteLine(Saves[i]);
}
saved = true;
dataWriter.Flush();
dataWriter.Close();
}
Основная часть готова.
Получение и запись данных
В чем плюс этого способа — не имеет значения, в каком формате мы сохраняем данные — в float, int или String — все равно все хранится в виде текста.
При получении просто преобразовываем в нужный формат. Для удобства сделаем названия функций такими же, как и в PlayerPrefs.
Например, нам нужен int. Вызываем функцию GetInt с номером нужной нам строчки.
function GetInt (i: int): int {
if (Saves[i] != ""){
return int.Parse(Saves[i]);
}
else {
return 0;
}
}
Для сохранения вызываем функцию SetInt, куда отправляем номер строчки и число для сохранения.
function SetInt (i: int, v: int){
Saves[i] = v.ToString();
saved = false;
}
Аналогично можно сделать и с другими типами переменных.
function SetFloat (i: int, v: float){
Saves[i] = v.ToString();
saved = false;
}
function GetFloat(i: int): float {
if (Saves[i] != ""){
return float.Parse(Saves[i]);
}
else {
return 0f;
}
}
Другие функции
Можно сделать удаление конкретного сохранения.
Для этого вызываем DeleteSaveFile с номером нужного файла. Если файл только один — номер будет равен нулю.
function DeleteSaveFile (i: int){
var tempPlayerDataPath = Application.streamingAssetsPath +'/Saves/'+ saveName + i + ".db";
if (File.Exists(tempPlayerDataPath)){
File.Delete(tempPlayerDataPath);
Debug.Log(«Delete File: » +tempPlayerDataPath);
}
else {
Debug.Log(«File not found»);
}
}
Или, например, можно очистить конкретный файл.
function ClearSaveFile (){
for(var i: int = 1; i < Saves.length; i++)
{
Saves[i] = "";
}
Debug.Log(«Clean Complite»);
Save();
}
Если нужно, чтобы сохранение происходило про выходе из игры или при загрузке другого уровня, пользуемся встроенной функцией OnDisable. Она вызывается автоматически.
function OnDisable(){
Save();
}
Вот здесь я сделал демонстрацию с кубиками:
Можно скачать уже готовый ассет для юнити, вместе с демкой из видео.
Подробное FAQ по уже готовому скрипту.
Всем спасибо за внимание, буду очень рад критике.