Где находятся сохранения unity

Где находятся сохранения unity

Ни для кого не секрет — в любой игре бывают проблемы. И когда мы с ними сталкиваемся — приходит разочарование. Ведь ответ Технической поддержки может занять время. Недавно я встретился с проблемой сохранений в игре. В частности, прогресс просто "полетел" и запустился с нуля. Решением этой проблемы и я хочу здесь с Вами поделиться.

Материал подготовил Серый волк.

Данные актуальны на 24.12.2014г.
Для работы использовался компьютер с конфигурацией:
Intel Core i7; 16 gb ram, GeForce GTX 780, SSD жёсткий диск.
Следите за обновлениями и нашими новинками!

Лимит — у всех у нас есть свои лимиты и ограничения. Наша цель — дать ту информацию, которая позволит сделать Вам наилучший выбор. Для Вас!
Преодолей с нами свои пределы!

2,245 уникальных посетителей
18 добавили в избранное

Сейвы игры хранятся в достаточно не стандартном месте. По умолчанию это:
C:Program Files (x86)UbisoftUbisoft Game Launchersavegames"Unique ID""3 цифры"
Далее смотрим в чём именно у Вас проблема.

Откройте каждый из файлов с помощью блокнота и проверьте, нет ли пустых файлов. Если один из файлов пуст, нажмите правой кнопкой мыши и выберите Свойства.
В новом окне, выберите вкладку "Предыдущие версии" .
Если вы включили опцию для восстановления системы, то вы должны найти список предыдущих версий файла. Выберите опцию для восстановления файла и повторите процедуру для следующих файлов. (Вариант доступен не во всех версиях Windows и требует включенной опции восстановления системы!)

Удалите из папки C:Program Files (x86)UbisoftUbisoft Game LauncherSavegames\"Unique ID""3 цифры" все файлы, кроме начинающихся на "1.save” (если Вы до этого пробовали вернуть сохранения вариантом 2, удалите все файлы и скопируйте в папку с сохранениями файлы из папки с резервной копией сохранений).
Перейдите в настройки Uplay PC, выключите Облачную синхронизацию (снимите галочку с Enable Cloud Save Synchronization for supported games) .
Сохраните изменения, а затем запустите игру и проверьте наличие сохранений.

Надеюсь это поможет решить возможные проблемы с сохранениями в игре. И, как я писал выше — это удобный способ вручную менять профили, что позволит познакомить с игрой своих близких, не переживая за свои "Сейвы".

Приятной игры, и пусть подобные проблемы минуют Вас!

Если вы пишете не казуалку под веб и не беспощадный суровый рогалик, без сохранения данных на диск не обойтись.
Как это делается в Unity? Вариантов тут достаточно — есть класс PlayerPrefs в библиотеке, можно сериализовать объекты в XML или бинарники, сохранить в *SQL*, можно, в конце-концов, разработать собственный парсер и формат сохранения.
Рассмотрим поподробнее с первые два варианта, и заодно попробуем сделать меню загрузки-сохранения со скриншотами.

Будем считать, что читающий дальше базовыми навыками обращения с этим движком владеет. Но при этом можно не подозревать о сущестовании в его библиотеке PlayerPrefs, GUI, и ещё в принципе не знать о сериализации. С этим всем и разберёмся.
А чтобы эта заметка не стала слишком уж увлекательной и полезной, ориентирована она самый неактуальный в мобильно/планшетно/онлайновый век вариант — сборку под винду (хотя, конечно, более общих моментов достаточно).

  • Кстати, пару недель назад на Хабре была статья, где автор упомянул, что Unity3D проходят в курсе компьютерной графики на кафедре информатики питерского матмеха. Занятный факт, немало говорящий о популярности движка.
    Хотя насколько это в целом хорошая идея — на мой взгляд, тема для дискуссии. Может быть, обсудить это было бы даже интереснее вопросов сериализации =)

Ещё небольшой дисклеймер — я не профи ни в одной из раскрываемых тем, так что если какие-то вещи можно сделать правильнее-проще-удобнее — исправления и дополнения очень приветствуются.

1. PlayerPrefs

Удобный встроенный класс. Работает с int, float и string. Довольно прозрачный, но мне всё равно встречались на форумах обороты в духе «не могу понять PlayerPrefs» или «надо бы как-нибудь разобраться с PlayerPrefs», так что посмотрим на него на простом примере.

1.1 Примитивное использование в рамках одной сцены: QuickSave & QuickLoad по хоткеям.

Быстрый пример использования. Допустим, у нас одна сцена и персонаж на ней. Скрипт SaveLoad.cs прикреплен к персонажу. Будем сохранять самое простейшее — его положение.

Конечно, тут применение PlayerPrefs довольно надумано — фактически против обычных переменных оно добавляет нам только возможность загрузки игру с места сохранения после выхода.

Зато весь основной интерфейс класса виден: для каждого из трех типов Get / Set по ключу, проверка вхождения по ключу, очистка. Нет смысла даже разбирать ScriptReference, всё очевидно по названиям функций: PlayerPrefs

Однако на одной всё же стоит остановиться подробнее, PlayerPrefs.Save. В описании говорится, что вообще дефолтно юнити пишет PlayerPrefs на диск только при закрытии приложения — в общем-то логично, учитывая, что класс ориентирован не на внутренний обмен данными, и на их сохранение между сеансами. Соответственно, Save() предполагается использовать только для периодических сохранений на случай крэша.

Возможно, в некоторых случаях это так и работает. Под Win PlayerPrefs пишутся в реестр, и, как можно легко убедиться, считываются и пишутся сразу.
Как-то так выглядит наш класс в реестре:

Ко всем ключам в конце добавлен их DJBX33X-хеш (Bernshtein hash with XOR).

UnityGraphicsQuality сохраняется всегда автоматически, и действительно при закрытии приложения. Это Quality level из Edit -> Project Settings Quality, оно же QualitySettings.SetQualityLevel .

Можно при запущенном приложении модифицировать сохранённое значение в реестре, потом затребовать его из программы — и мы увидим, что вернулся модифицированный вариант. Т.е. не стоит думать что во время работы программы PlayerPrefs — что-то вроде аналога глобальных переменных, а работа с диском не происходит.

2. Сериализация в XML

Говорим сериализация, подразумеваем бинарный код. Такое встречается, но на самом деле сериализовать можно в любой формат. По сути это перевод структуры данных или состояния объекта в хранимый/передаваемый формат. А десериализация, соответственно — восстановление объекта по сохраненным/полученным данным.

Вообще Mono умеет и бинарную сериализацию, и XML (System.Xml.Serialization), но есть один момент: большинство классов Unity не сериализуются напрямую. Невозможно просто взять и сериализовать GameObject, или класс, наследующий MonoBehavoir: придётся завести дополнительно внутренний сериализуемый класс, содержащий нужные данные, и работаеть, используя его. Но XmlSerializer хотя бы кушает автоматически Vector3, а BinarySerializer, afaik, даже этого не умеет.

2.1 Суть примера

Представьте, что вы пишете свой Portal, где герой проходит череду однотипных локаций — но на любую из них может впоследствии вернуться. Причём на каждую он мог оказать воздействие: какие-то ресурсы использовать, что-то сломать, что-то расшвырять. Хочется, эти изменения сохранять, но возвращение на локацию маловероятно и непрогнозируемо, и тащить за собой параметры всех комнат в оперативке нет особого смысла. Будем сериализовать локацию, покидая её — например, по триггеру на двери. А при загрузке локации генерировать либо дефолтную ситуацию, либо, если есть сохраненные данные, восстанавливать по ним.

2.2 Сериализуемые классы для данных

XmlSerializer умеет работать с классами, данные в которых состоят из других сериализуемых классов, простых типов, большинства элементов Collections[.Generic]. Обязательно наличие у класса пустого конструктора и public-доступ ко всем сериализуемым полям.
Некторые типы из библиотеки Юнити (вроде Vector3, содержащего всего три интовых поля) успешно проходят этот фейсконтроль, но большинство, особенно более сложных, его фейлят.

Читайте также:  Ошибка в обработчике события послезагрузкиобъекта

Допустим, в каждой комнате нам надо сохранять состояния некоторого произвольного набора GameObject’ов. Напрямую сделать этого мы не можем. Значит, нам потребуются дублирующие типы.

Создадим новый скрипт в Standard Assets:

В квадратных скобках идут атрибуты для управления XML-сериализацией. Тут они фактически влияют только на имена тегов в генерируемом *.xml, и строго говоря, необходимости в них нет. Но пусть будут, для наглядности 🙂 Если вам почему-то вдруг важно, как будет выглядеть xml-код, то возможности атрибутов, конечно шире.

Дальше там же добавим базовый класс для предметов из списка и сколько угодно наcледуемых от него. Хотя… для примера хватит и одного:

Итак, сериализуемые классы готовы. Сделаем теперь ещё класс для дополнительного упрощения сериализации созданного типа RoomState.

2.3 Непосредственно сериализация

Тоже в Standard Assets сделаем класс с парой статических методов, которыми будем в дальнейшем пользоваться:

Здесь XmlSerializer мы создаём через конструктор Constructor (Type, Type[])
FileStream открываем по адресу сохранения, передаваемого конкретной локацией.

Использование

Итак, все вспомогательные инструменты готовы, можно приступать к самой комнате. На объект комнаты вешаем:

Напоследок, сделаем вызов RoomGen.Dump(). Пусть, например, по триггерам на дверях, которые являются дочерними объектами относительно комнаты (объекта с компонентом RoomGen):

Вот и всё. Здесь опущено собственно взаимодействие с предметами и процесс изменения их состояния, но это несложно добавить. Для первоначального теста можно просто добавить в скрипт пару устанавливащих состояния функций по хоткеям, или ставить на паузу и двигать руками.

При первом запуску генерируется дефолтный вариант, при выходе изменения дампятся в файл, при возвращении последние состояние восстанавливается из файла, в том числе если приложение закрывалось. Works like a charm.

Один из существенных недостатков XML — игрок может легко изменить данные. И если в данном случае мало кого заинтересует расположение раскиданных стульев, то при сохранении более существенных для игрока данных сериализации в XML лучше избегать. Да и в реестре поменять значения не сложно. В таких случаях уже лучше использовать бинарную сериализацию или свой формат.

3. Save/Load через меню

Наверное, актуальнее было бы реализовать вариант с выбором/созданием пользователя и внутренними автоматическими сохранениями. Если вашей игре требуется серьёзное меню Save/Load, то вряд ли вы сейчас читаете эту статейку для профанов.

Но я жду не дождусь новогодних праздников, когда можно будет наконец увидеться с сестрой и за пару вечеров добить классическую American McGee’s Alice, так что сделаем Save/Load почти как там. Со скриншотами. Заодно будет повод покопаться в GUI, текстурах и других увлекательных вещах.

3.1 Главное меню

Чтобы сделать загрузку и сохранение через меню, нам, как ни странно, понадобится меню. Конечно, можно сделать его самостоятельно через объекты на сцене, можно заюзать готовые решения ироде NGUI , но мы пока воспользуемся GUI из штатной библиотеки.

    Scripting Reference
    Для начала пригодятся:
    OnGUI() — функция MonoBehaviour для отрисовки GUI и обработки связанных с ним событий. Нечто вроде Update(), но специально для GUI и вызываться может чаще, чем каждый фрейм.

функция кнопки. Рисует её в рамках заданного прямоугольника, реагирует на нажатие, возвращая true. Конструкторов больше, но нам хватит этих.

Группировка элементов гуи, полезна в основном переопределением границ относительно которых вычисляется положение вложенных элементов (дефолтно это границы экрана, в данном случает — прямоугольник position).

  • Суть
    Сделаем отдельную стартовую сцену, а на ней пустой объект, к которому и прикрепим скрипт для нашего меню. Т.о. меню будет путешествовать сквозь сцены (при загрузке очередной сцены не пересоздаётся, а просто переносится в неё), хэндлить нужные события (вроде кнопки вызова меню), при вызове рисоваться поверх экрана игры.
  • по коду с комментариями всё должно быть ясно:
  • Главное меню до и после начала игры

    3.2 Рисуем меню загрузки / сохранения

    Функция drawSaveLoadMenu() у нас уже вызывается при menutype>0, но пока не написана. Исправим это упущение. Пока просто научимся рисовать наши меню и вызывать собственно функции загрузки/сохранения.

      Scripting Reference
      GUI.SelectionGrid — рисует сетку кнопок, но по сути это одновариантый селект. Всегда выбран один вариант, возвращает номер выбранного.

    Количество — исходя из размеров передаваемого массива. Вообще предназначен для использования как-то так:

  • Суть
    Кажется, это не совсем то, что нам требуется — нам-то нужно выбрать один раз и сразу отреагировать. Но SelectionGrid спокойно ест грязный хак — индекс вне пределов реального массива. Т.е. мы всегда будем передавать, допустим, -1 и тогда сможем отслеживать собственно событие клика.
  • Меню Load на SelectionGrid — внешне ничем не отличается от соответствующего Save

    Основное, что мне в этом решении не нравится, это что в меню загрузки не содержащие сохранений слоты остаются относительно активными — внешне отличаются только отсутствием текстуры, реагируют на наведение. Поэтому бонусом — сетка ручками, вместо неактивных слотов рисуем Box, для активных Button.
    Заодно добавим резиновости: количество слотов в строке задаётся, размер слотов подстраивается под экран. Правда, тут они уже квадратные, но встроить произвольное соотношение сторон будет несложно 🙂 Ну и заодно min/max width/height из GUILayout и прочая обработка напильником.

    Меню Load на Button и Box — теперь пустые слоты неактивны

    3.3 Текстуры, скриншоты

    Итак, с момента создания нашего объекта меню мы будем держать массив текстур. Памяти он занимает немного и нам гарантирован в ним мгновенный доступ. На самом деле, тут и альтернативы особой нет — не пихать же работу с диском в onGUI().

    Как мы уже видели, при создании нашего меню создаём и массив:

    Сохранять мы будем не только информацию сейвов, но и информацию о них, а точнее — какие именно слоты содержат сохранения. Как хранить — выбор каждого, можно по параметру 0/1 на каждый слот, можно строку из 0/1, но мы сделаем некрасиво 🙂 и возьмём битовый вектор в int. В какой момент и как он сохраняется, увидим позже, пока просто читаем.
    Добавим в Start():

    Ну и собственно главное в данном вопросе — как скрины сохранять? Напрашивается вариант Application.CaptureScreenshot , но тут сразу два подвоха. Во-первых, они сохраняются в полном размере, а поскольку в кончном итоге понадобятся нам только thumbnails, логичнее сразу сделать ресайз. Во-вторых, мы же держим массив текстур, придётся в него снова считывать с диска? Не очень-то здорово.

    Функцию взятия и записи скриншота вызывать будем позже, а пока заранее выделим в Coroutine:

    Неприятный нерешенный момент — текстура с текущей сессии и текстура, загруженная с диска, сильно различаются по качеству.
    Ниже слева две текущих, справа — две с диска, от предыдущих сессий:

    3.4 Собственно реализация сохранения загрузки

    Итак, вроде бы с шелухой разобрались. Научились минимально работе с GUI, сделали простое главное меню, меню Save/Load, научились работать со скриншотами.

    Читайте также:  Как изменить текст в штампе в автокаде

    Как реализовать взаимодействие между объектами сцены, параметры которых мы будем сохранять и нашим меню?

    1. Если мы будем записывать только состояние такого же создаваемого с первой сцены и неразрушаемого далее объекта (например, игрок, его параметры и инвентарь) — можно сразу держать прямую ссылку.

    2. GameObject.Find и GameObject.FindWithTag тут использовать практически не стыдно — загрузка/сохранение — разовое событие. Можно искать напрямую, а поскольку сцены могут содержать разную информацию — то, как вариант, добавлять на каждую специальный объект с определенным тегом, к которому и будет прикручен скрипт сохранения/загрузки собственно данной сцены, тут уже можно держать прямые ссылки на требуемые объекты.

    А пока рассмотрим такой простой вариант. Сохранять будем только сцену и положение игрока. Игрок в каждой сцене пересоздаётся, но всегда вид от первого лица, и соответственно к игроку прикреплена камера.
    Через неё и будем получать доступ. В ниже представленной функции вся эта специфика — в двух строках помеченных //!, и её не сложно локально заменить, остальное привязано к уже написанному нами выше коду.

    Если делать скриншот заранее, то он во-первых, может не пригодится, а во-вторых, нужно ещё успеть. А так, с учётом заблокированности камеры в режиме меню, результат примерно тот же.

    С загрузкой ещё проще. Всё специфику мы снова полностью делегируем, причём даже не будем её напрямую вызывать. Просто загрузим нужный уровень, а дальше они как-нибудь сами 🙂

    Сделаем теперь поведение, который будем вешать на камеры:

    Надо заметить „дальше как-нибудь сами“ было определенной степенью лукавства: loadgame() меню и load() объекта определенно обменялись информацией, только вот через известное место — реестр. Сохранять туда откровенно временную переменную — ход не слишком красивый. Можно изменить на прямой вызов load(), а без изменения текущей общей структуры — держать переменную в меню, и в Start() загружаемого объекта добавить поиск объекта меню и получение нужной информации.

    Дальше. От созданного базового поведения мы можем унаследовать разные варианты для разных сцен и объектов. Например, вариант с сохранением поворота:

    Конечно, здесь данным уже пригодилась бы защита. Поскольку поскольку вся фактическая работа с PlayerPrefs тут выделена в отдельные функции save() / load(), заменить их содержательную часть будет не сложно. На что? Можно аналогично примеру из части 2 держать класс-рефлектор, и сериализовать его через BinarySerializer.
    Другой неплохой вариант — прикрутить, например, SQLite. Правда, по слухам, на js с ней работать удобнее, чем на шарпе, но и на последнем всё в конечном итоге заводится. Кто хочет попробовать, начать можно отсюда.

    Этот текст никогда бы не получился без:

    и хабра. Спасибо им.
    Надеюсь, всё это принесёт кому-нибудь пользу, и никому — вреда 🙂

    Большинство проектов созданных в Unity часто имеют систему хранения игровых данных. Эта система включает в себя инструменты для сохранения и загрузки данных. Как и где хранить эти данные часто зависит от того что это за игра, кто в нее играет и какое кол-во данных необходимо сохранить. Обычно различают два вида хранения данных: локальную, облачную (удаленную) и комбинированную.

    Локальную систему хранения данных часто используют в одиночных играх, где необходимо хранить несущественные данные, вроде прогресса прохождения или характеристик персонажей и тд.

    Облачную систему чаще используют для многопользовательских проектов. В таких проектах игре необходимо иметь доступ к данным всех игроков, поэтому они используют сервера, где хранятся эти данные.

    Комбинированную систему обычно используют в проектах, нацеленных как на одиночную игру, так и на многопользовательскую. В таких проектах необходимо хранить данные локально и удаленно.

    В этой статье рассмотрим локальный тип хранения данных, и для этого в Unity есть очень простой инструмент PlayerPrefs.

    PlayerPrefs – это небольшой набор методов для сохранения и загрузки данных из реестра системы. Сам реестр используется для иерархического хранения данных и настроек системы. В отличие от файловой системы, где хранятся файлы с любыми данными и которые доступны всем пользователям компьютера, в реестре хранятся только настройки программы с самыми необходимыми данными которые доступны только определенным пользователям, а PlayerPrefs, в свою очередь, позволяет записывать и считывать эти самые данные из реестра.

    Для начала рассмотрим способы записи данных в реестр с помощью PlayerPrefs.

    Система имеет несколько методов и все они работают по одному и тому же принципу: сначала указываем ключ под которым хотим записать данные, после чего указываем сами данные которые необходимо записать.

    • SetInt. Метод используется для записи целого числа(integer) в реестр.
    • SetFloat. Метод для записи числа с “плавающей” запятой или дробного числа(float).
    • SetString. Метод для записи текстовых данных.

    Для загрузки есть аналогичные методы, которые возвращают сохраненные ранее данные под определенным ключом.

    • GetInt. Метод используется для считывания целого числа(integer) из реестра.
    • GetFloat. Метод для считывания дробного числа(float).
    • GetString. Метод для считывания текстовых данных.

    И так, мы разобрали основные методы для работы с PlayerPrefs, теперь попробуем сохранить с помощью этой системы некоторые данные в игре.

    Игра представляет собой небольшую аркаду в которой необходимо отстреливать инопланетные корабли до того как они захватят главную базу.

    Начнем с простого сохранения кол-ва уничтоженных кораблей.

    Создадим небольшой скрипт Control унаследованный от MonoBehaviour.

    В числовой переменной kills будем хранить кол-во уничтоженных кораблей.

    Теперь добавим метод сохранения Save.

    В игре этот метод вызывается через UI кнопку.

    После нажатия этой кнопки переменная kills запишется в реестр под указанным ключом.

    1. publicclass Control : MonoBehaviour <
    2. publicint kills = 0 ;
    3. publicvoid Save () <
    4. string key = “ MyGame” ;
    5. PlayerPrefs . SetInt ( key, this . kills );
    6. PlayerPrefs . Save ();
    7. >
    8. >

    И так первым действие указываем в переменной key ключ под которым необходимо будет записать данные, пусть, к примеру название ключа будет MyGame, далее вызываем метод SetInt в который передаем ключ и переменную kills, в конце завершаем запись данных в реестре с помощью метода Save.

    Проверить записи данных можно в реестре. Для быстрого входа в реестр необходимо нажать комбинацию кнопок Win + R, после чего в окошке “Выполнить” ввести regedit и нажать “Ok”.

    Далее необходимо найти раздел с игрой. Все данные unity проектов хранятся в разделе HKEY_CURRENT_USER/Software/Unity/UnityEditor/DefaultCompany в этом разделе находим проектом по названию, там и будут храниться все записи программы.

    В разделе “Параметр” можно увидеть название ключа под которым записаны данные, а в разделе “Значение” число равное кол-ву уничтоженных кораблей в игре.

    Именно в этом разделе мы будем хранить все остальные данные из игры.

    Теперь необходимо произвести чтение данных из реестра.

    Загрузку будет проводить при старте игры, для этого заведем новый метод Start в скрипте Control.

    В методе Load, в переменную key укажем ключ под которым записаны наши данные.

    Теперь с помощью условия проверим: существуют ли наш ключ в реестре, для этого используем метод HasKey.

    Читайте также:  R drive image не удалось заблокировать том

    Если ключ существует значит можно загрузить данные из реестра.

    1. publicclass Control : MonoBehaviour <
    2. publicint kills = 0 ;
    3. privatevoid Start () <
    4. Load ();
    5. >
    6. privatevoid Load () <
    7. string key = “ MyGame” ;
    8. if ( PlayerPrefs . HasKey ( key )) <
    9. this . kills = PlayerPrefs . GetInt ( key );
    10. >
    11. >
    12. /*…метод Save…*/
    13. >

    Отлично, данные загрузились.

    И так, теперь мы научились сохранять и загружать самые элементарные данные из реестра. Теперь попробуем проделать все тоже самое с кол-во очков в игре, для этого объявим новую дробную переменную scores.

    Теперь немного расширим метод Save, чтобы сохранить эту новую переменную в реестр.

    1. publicclass Control : MonoBehaviour <
    2. publicint kills = 0 ;
    3. publicfloat scores = 0f ;
    4. publicvoid Save () <
    5. string key = “ MyGame” ;
    6. PlayerPrefs . SetInt ( key, this . kills );
    7. PlayerPrefs . SetFloat ( key, scores );
    8. PlayerPrefs . Save ();
    9. >
    10. >

    В методе Load проведем аналогичные действия только по загрузке переменной scores.

    1. publicclass Control : MonoBehaviour <
    2. publicint kills = 0 ;
    3. publicfloat scores = 0f ;
    4. privatevoid Start () <
    5. Load ();
    6. >
    7. privatevoid Load () <
    8. string key = “ MyGame” ;
    9. if ( PlayerPrefs . HasKey ( key )) <
    10. this . kills = PlayerPrefs . GetInt ( key );
    11. this . scores = PlayerPrefs . GetFloat ( key );
    12. >
    13. >
    14. /*…метод Save…*/
    15. >

    Запускаем игру, чтобы проверить работоспособность системы.

    У методов записи и загрузки данных есть один недостаток, заключается он в том, что под одним ключом может храниться только одна переменная определенного типа. Мы уже использовали ячейки для записи целого числа int и дробного float, больше данный ключ вместить данных не может, но в игре еще остались данные которые необходимо записать в реестр – это кол-во жизней главной базы.

    Кол-во жизней базы это тоже дробное число float, а так как ячейка дробного числа уже занята переменной scores, то получается, что мы не сможем поместить еще одну. В этом случае на помощь приходят текстовые данные. Мы просто преобразуем все данные для сохранения в текст и запишем его в реестр в текстовую ячейку, которая все еще пустая. Для удобного преобразования множества данных в текст и обратно используем JSONUtility.

    JSON – это удобный текстовый формат хранения данных. Он преобразует любой объект в читаемый текст и обратно. С помощью него можно хранить практически любое кол-во данных в виде текста.

    И так объявим новую переменную health в скрипте Control где будем хранить кол-во жизней базы.

    1. publicclass Control : MonoBehaviour <
    2. publicint kills = 0 ;
    3. publicfloat scores = 0f ;
    4. publicfloat health = 100 ;
    5. >

    Теперь нам нужен объект который будет хранить все эти три переменные. Для этого подойдет простой класс SaveData. Создадим новый скрипт SaveData и уберем у него наследование от MonoBehaviour.

    Переходим в метод Save, откуда сотрем последние два действия SetInt и SetFloat.

    1. publicclass Control : MonoBehaviour <
    2. publicint kills = 0 ;
    3. publicfloat scores = 0f ;
    4. publicfloat health = 100 ;
    5. publicvoid Save () <
    6. string key = “ MyGame” ;
    7. SaveData data = new SaveData ();
    8. PlayerPrefs . Save ();
    9. >
    10. >

    Сначала создаем новый экземпляр класса SaveData, после чего наполняем его данными.

    1. publicclass Control : MonoBehaviour <
    2. publicint kills = 0 ;
    3. publicfloat scores = 0f ;
    4. publicfloat health = 100 ;
    5. publicvoid Save () <
    6. string key = “ MyGame” ;
    7. SaveData data = new SaveData ();
    8. data . kills = this . kills ;
    9. data . scores = this . scores ;
    10. data . health = this . health ;
    11. PlayerPrefs . Save ();
    12. >
    13. >

    Теперь необходимо преобразовать объект data в текст, для чего воспользуемся методом ToJson класса JsonUtility.

    1. publicclass Control : MonoBehaviour <
    2. publicint kills = 0 ;
    3. publicfloat scores = 0f ;
    4. publicfloat health = 100 ;
    5. publicvoid Save () <
    6. string key = “ MyGame” ;
    7. SaveData data = new SaveData ();
    8. data . kills = this . kills ;
    9. data . scores = this . scores ;
    10. data . health = this . health ;
    11. stringvalue = JsonUtility . ToJson ( data );
    12. PlayerPrefs . Save ();
    13. >
    14. >

    После чего сохраняем полученный текст в реестр с помощью метода SetString.

    1. publicclass Control : MonoBehaviour <
    2. publicint kills = 0 ;
    3. publicfloat scores = 0f ;
    4. publicfloat health = 100 ;
    5. publicvoid Save () <
    6. string key = “ MyGame” ;
    7. SaveData data = new SaveData ();
    8. data . kills = this . kills ;
    9. data . scores = this . scores ;
    10. data . health = this . health ;
    11. stringvalue = JsonUtility . ToJson ( data );
    12. PlayerPrefs . SetString ( key, value );
    13. PlayerPrefs . Save ();
    14. >
    15. >

    Теперь необходимо проделать действия по загрузке данных в методе Load и перевести текст обратно в объект SaveData с помощью того же JSONUtility.

    1. publicclass Control : MonoBehaviour <
    2. publicint kills = 0 ;
    3. publicfloat scores = 0f ;
    4. publicfloat health = 100 ;
    5. privatevoid Start () <
    6. Load ();
    7. >
    8. privatevoid Load () <
    9. string key = “ MyGame” ;
    10. if ( PlayerPrefs . HasKey ( key )) <
    11. stringvalue = PlayerPrefs . GetString ( key );
    12. >
    13. >
    14. /*…метод Save…*/
    15. >

    Как и раньше проверяем существование ключа, после чего загружаем текст из реестра. Далее преобразуем полученный текст в объект SaveData с помощью метода FromJson класса JsonUtility.

    1. publicclass Control : MonoBehaviour <
    2. publicint kills = 0 ;
    3. publicfloat scores = 0f ;
    4. publicfloat health = 100 ;
    5. privatevoid Start () <
    6. Load ();
    7. >
    8. privatevoid Load () <
    9. string key = “ MyGame” ;
    10. if ( PlayerPrefs . HasKey ( key )) <
    11. stringvalue = PlayerPrefs . GetString ( key );
    12. SaveData data = JsonUtility . FromJson SaveData >( value );
    13. >
    14. >
    15. /*…метод Save…*/
    16. >

    В методе FromJson, в фигурных скобках указываем тип объекта который мы хотим получить из текста, а в сам метод передаем текстовую переменную value в которой находится загруженный текст из реестра. Получив целый объект из текста применяем сохраненные значения переменных обратно.

    1. publicclass Control : MonoBehaviour <
    2. publicint kills = 0 ;
    3. publicfloat scores = 0f ;
    4. publicfloat health = 100 ;
    5. privatevoid Start () <
    6. Load ();
    7. >
    8. privatevoid Load () <
    9. string key = “ MyGame” ;
    10. if ( PlayerPrefs . HasKey ( key )) <
    11. stringvalue = PlayerPrefs . GetString ( key );
    12. SaveData data = JsonUtility . FromJson SaveData >( value );
    13. this . kills = data . kills ;
    14. this . scores = data . scores ;
    15. this . health = data . health ;
    16. >
    17. >
    18. /*…метод Save…*/
    19. >

    Запускаем для проверки.

    Сохранение и загрузка работают исправно. Переходим в реестр и проверяем данные.

    Теперь в разделе “Значение” мы видим текст со всеми переменными и их значениями.

    Сохранение и загрузка данных через PlayerPrefs имеет свои преимущества перед другими видами локального хранения данных: во первых простотой работы, вам не нужно работать с файлами и лезть в файловую систему вообще, во вторых при работе с файловой системе, к примеру, на некоторых платформах вам нужно иметь разрешение на чтение и запись данных, для PlayerPrefs в этом нет необходимости он работает на всех устройствах одинаково. Поэтому PlayerPrefs отлично подходит для хранение небольшого кол-ва несложных данных на устройстве.

    Ознакомится с проектом из статьи можно по ссылке .

    Ссылка на основную публикацию
    Где находится спикер на материнской плате
    Купил корпус и в комплекте к нему дали какую-то детальку: на одном конце написано speaker, от него идёт два провода...
    Восклицательный знак при установке steam
    Не устанавливается стим. Выдаёт ошибку без слов. Просто восклицательный знак в жёлтом треугольнике Дополнено (1). Что делать? Поменять путь установки....
    Выбранная папка принадлежит другому пользователю яндекс диск
    Работая в команде, необходимо иметь надёжные коммуникации. Как только мы начинали работу над falbar, данный вопрос встал очень жестко. Ведь...
    Где находится клавиша лкм
    Доброго времени суток. Вы никогда не задумывались, почему на одни и те же операции в Windows разные пользователи затрачивают разное...
    Adblock detector