Как получить файл с сервера unity

Обновлено: 06.07.2024

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

Самыми часто используемыми типами внешних данных являются:

  • Текстовый файл. В этом файле могут находится настройки, либо описания других объектов. Самый простой тип данных, который хранится в виде строк.
  • Изображение. Это может быть текстура или просто картинка.
  • Аудио файл. Музыкальный трек, либо звук спецэффекта.
  • Видео файл.
  • Байт массив. Самый универсальный тип данных, который можно преобразовать в любой другой тип данных(включая выше перечисленные). Изначально, любые загружаемые данные представляют из себя поток массива байтов.

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

Возможности unity

Для работы с внешними ресурсами в Unity существует целый специальный раздел Networking , который хранит все необходимые инструменты для работы в сети.

В этой части статьи мы начнем с самого важного элемента всей системы, который называется UnityWebRequest .

Этот элемент является основой для любых коммуникаций с сетевыми сервисами.

Объект UnityWebRequest можно представить в виде посредника, который связывается с сетевым сервисом по указанному адресу и проводит операции по передаче данных между приложением и указанным сервисом. Общение между приложением и сервисом происходит в виде Запроса ( Request ) и Ответа ( Response ), которые посылает и принимает UnityWebRequest . Также этот объект отвечает за загрузку и отправку любых типов данных.

Обычно все действия по загрузке или отправке данных в UnityWebRequest , выполняются асинхронно, и чтобы отследить результат работы через время, пользуются корутинами, либо разного рода Update’ами , где проверяют процесс выполнения работы.

Загрузка изображения

Теперь попробуем разобрать работу UnityWebRequest’а по порядку. Для примера, загрузим простое изображение и выведем его на экран.

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

  1. publicclass SimpleRequest : MonoBehaviour
  2. >

Далее добавляем новую строковую переменную url .

  1. publicclass SimpleRequest : MonoBehaviour
  2. publicstring url ;
  3. >

В этой переменной url будет храниться ссылка на изображение.

Теперь объявим еще одну переменную tex типа Texture2D , куда мы поместим загруженное изображение.

  1. publicclass SimpleRequest : MonoBehaviour
  2. publicstring url ;
  3. private Texture2D tex = null ;
  4. >

Чтобы вывести изображение на экран воспользуемся старой системой GUI , для этого добавим в класс SimpleRequest метод OnGUI .

  1. publicclass SimpleRequest : MonoBehaviour
  2. publicstring url ;
  3. private Texture2D tex = null ;
  4. privatevoid OnGUI ()
  5. >
  6. >
  1. publicclass SimpleRequest : MonoBehaviour
  2. publicstring url ;
  3. private Texture2D tex = null ;
  4. privatevoid OnGUI ()
  5. if ( tex )
  6. Rect r = new Rect ( 0 , 0 , tex . width , tex . height );
  7. GUI . DrawTexture ( r, tex );
  8. >
  9. >
  10. >

Для отрисовки изображения определяем область Rect с размерами текстуры, и с помощью метода GUI.DrawTexture выводим ее на экран.

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

  1. publicclass SimpleRequest : MonoBehaviour
  2. publicstring url ;
  3. private Texture2D tex = null ;
  4. private IEnumerator Start ()
  5. >
  6. privatevoid OnGUI ()
  7. if ( tex )
  8. Rect r = new Rect ( 0 , 0 , tex . width , tex . height );
  9. GUI . DrawTexture ( r, tex );
  10. >
  11. >
  12. >

Чтобы создать новый экземпляр класса UnityWebRequest для загрузки изображения, воспользуемся специально-предназначенным для этого классом UnityWebRequestTexture , который автоматически настроит новый экземпляр UnityWebRequest для загрузки изображения.

  1. publicclass SimpleRequest : MonoBehaviour
  2. publicstring url ;
  3. private Texture2D tex = null ;
  4. private IEnumerator Start ()
  5. UnityWebRequest req = UnityWebRequestTexture . GetTexture ( url );
  6. >
  7. privatevoid OnGUI ()
  8. if ( tex )
  9. Rect r = new Rect ( 0 , 0 , tex . width , tex . height );
  10. GUI . DrawTexture ( r, tex );
  11. >
  12. >
  13. >

Передаем в статический метод GetTexture класса UnityWebRequestTexture ссылку на изображение, которое храниться в переменной url и получаем на выходе готовый для работы UnityWebRequest .

Далее, чтобы запустить процесс загрузки изображения воспользуемся методом SendWebRequest .

  1. publicclass SimpleRequest : MonoBehaviour
  2. publicstring url ;
  3. private Texture2D tex = null ;
  4. private IEnumerator Start ()
  5. UnityWebRequest req = UnityWebRequestTexture . GetTexture ( url );
  6. req . SendWebRequest ();
  7. >
  8. privatevoid OnGUI ()
  9. if ( tex )
  10. Rect r = new Rect ( 0 , 0 , tex . width , tex . height );
  11. GUI . DrawTexture ( r, tex );
  12. >
  13. >
  14. >

Для проверки выполнения работы, у класса UnityWebRequest существует специальное свойство isDone , которое возвращает boolean значение.

  1. publicclass SimpleRequest : MonoBehaviour
  2. publicstring url ;
  3. private Texture2D tex = null ;
  4. private IEnumerator Start ()
  5. UnityWebRequest req = UnityWebRequestTexture . GetTexture ( url );
  6. req . SendWebRequest ();
  7. while ( req . isDone == false )
  8. yieldreturn new WaitForEndOfFrame ();
  9. >
  10. >
  11. privatevoid OnGUI ()
  12. if ( tex )
  13. Rect r = new Rect ( 0 , 0 , tex . width , tex . height );
  14. GUI . DrawTexture ( r, tex );
  15. >
  16. >
  17. >

В нашем случае, пока свойство isDone не вернет значение true , мы будем ожидать выполнения процесса загрузки.

После завершения загрузки воспользуемся специальным классом DownloadHandlerTexture , который преобразует загруженные ранее данные в готовое изображение.

  1. publicclass SimpleRequest : MonoBehaviour
  2. publicstring url ;
  3. private Texture2D tex = null ;
  4. private IEnumerator Start ()
  5. UnityWebRequest req = UnityWebRequestTexture . GetTexture ( url );
  6. req . SendWebRequest ();
  7. while ( req . isDone == false )
  8. yieldreturn new WaitForEndOfFrame ();
  9. >
  10. tex = DownloadHandlerTexture . GetContent ( req );
  11. >
  12. privatevoid OnGUI ()
  13. if ( tex )
  14. Rect r = new Rect ( 0 , 0 , tex . width , tex . height );
  15. GUI . DrawTexture ( r, tex );
  16. >
  17. >
  18. >

Для получение изображения достаточно передать в статический метод GetContent экземпляр класса UnityWebRequest’а , который храниться в переменной req .

Теперь можно поместить скрипт SimpleRequest на камеру, что на сцене, задать url адрес изображения в инспекторе и ожидать выполнения загрузки.

Важно! Чтобы UnityWebRequest мог правильно выполнить подключение к любому сетевому сервису, при этом неважно, выполняем мы при этом загрузку или отправку данных, ссылка должна быть “ статической ”. Часто сетевые сервисы, делают ссылки на свои ресурсы “ динамическими ”, то есть ссылки на эти самые ресурсы время от времени меняются, либо к ним не подключиться без прохождения дополнительных проверок, это делается для безопасности самого сервиса.


Для примера, можно воспользоваться любым бесплатным хостинг-сервисом для изображений, который выдает статические ссылки. В браузере Google Chrome , достаточно просто открыть изображение, кликнув по нему правой кнопкой мыши, и выбрать Копировать URL картинки .


Заключение

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

В область работ по обработке данных входят:

  • Доступность. В первую очередь, проверка возможностей подключения к указанному сервису в сети.
  • Сохранение. Кэширование данных является первостепенной задачей после их загрузки.
  • Актуальность. Наличие более новых версий данных.

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

По традиции, для начала определимся, что это и зачем нам это надо. Итак, что же такое эти внешние ресурсы. В рамках разработки игр, такими ресурсами может быть все, что требуется для функционирования приложения и не должно храниться в конечном билде проекта. Внешние ресурсы могут находится как на жестком диска компьютера пользователя, так и на внешнем веб-сервере. В общем случае такие ресурсы — это любой файл или набор данных, который мы загружаем в наше, уже запущенное приложение. Если говорить в рамках Unity 3d, то ими могут быть:

  • Текстовый файл
  • Файл текстуры
  • Аудио файл
  • Байт-массив
  • AssetBundle (архив с ассетами проекта Unity 3d)

Возможности Unity 3d


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

Работа с UWR в целом схожа с WWW в своей основе, однако есть и отличия, речь о которых пойдет дальше. Ниже приведен аналогичный пример загрузки текста.


Основные изменения, которые привнесла новая система UWR (помимо изменений принципа работы внутри) — это возможность назначать самому обработчиков для загрузки и скачивания данных с сервера, подробнее можно почитать здесь. По умолчанию это классы UploadHandler и DownloadHandler. Сам Unity предоставляет набор расширений этих классов для работы с различными данными, такими как аудио, текстуры, ассеты и т.п. Рассмотрим подробнее работу с ними.

Работа с ресурсами

Текст


Как видно из кода, здесь используется DownloadHandler по умолчанию. Свойство text это геттер, который преобразует byte массив в текст в кодировке UTF8. Основное применение загрузки текста с сервера — это получение json-файла (сериализованное представление данных в текстовом виде). Получить такие данные можно с использованием класса Unity JsonUtility.

Аудио

Для работы с аудио необходимо использовать специальный метод создания запроса UnityWebRequestMultimedia.GetAudioClip, а также для получения представления данных в нужном для работы в Unity виде, необходимо использовать DownloadHandlerAudioClip. Помимо этого, при создании запроса необходимо указать тип аудиоданных, представленный перечислением AudioType, который задает формат (wav, aiff, oggvorbis и т.д.).

Текстура

Загрузка текстур схожа с таковой для аудио файлов. Запрос создается с помощью UnityWebRequestTexture.GetTexture. Для получения данных в нужном для Unity виде используется DownloadHandlerTexture.

AssetBundle

Основные проблемы и решения при работе с веб-сервером и внешними данными

Выше были описаны простые способы взаимодействия приложения с сервером по части загрузки различных ресурсов. Однако на практике все обстоит гораздо сложнее. Рассмотрим основные проблемы, которые сопровождают разработчиков и остановимся на путях их решения.

Не хватает свободного места

Одной из первых проблем при загрузке данных с сервера является возможная нехватка свободного места на устройстве. Часто бывает, что пользователь использует для игр (особенно на Android) старые устройства, а также и сам размер скачиваемых файлов может быть достаточно большим (привет PC). В любом случае, эту ситуацию необходимо корректно обработать и заранее сообщить игроку, что места не хватает и сколько. Как это сделать? Первым дело необходимо узнать размер скачиваемого файла, это делается по средствам запроса UnityWebRequest.Head(). Ниже представлен код для получения размера.


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

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

Примечание: можно воcпользоваться классом Cache в Unity3d, он может показывать свободное и занятое место в кэше. Однако здесь стоит учесть момент, что эти данные являются относительными. Они рассчитываются исходя из размера самого кэша, по умолчанию он равен 4GB. Если у пользователя свободного места больше, чем размер кэша, то проблем никаких не будет, однако если это не так, то значения могут принимать неверные относительно реального положения дел значения.

Проверка доступа в интернет

Кэширование

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

  1. Экономия траффика (не скачивать уже скаченные данные)
  2. Обеспечение работы в отсутствии интернета (можно показать данные из кэша).


Аналогично, получение данных из кэша.


Примечание: почему для загрузки текстур не используется тот же самый UWR с url вида file://. На данный момент наблюдается проблемы с этим, файл просто напросто не загружается, поэтому пришлось найти обходной путь.

Примечание: я не использую прямую загрузку AudioClip в проектах, все такие данные я храню в AssetBundle. Однако если необходимо, то это легко сделать используя функции класса AudioClip GetData и SetData.

В отличие от простых ресурсов для AssetBundle в Unity присутствует встроенный механизм кэширования. Рассмотрим его подробнее.

В своей основе этот механизм может использовать два подхода:

  1. Использование CRC и номера версии
  2. Использование Hash значения

Итак, каким образом осуществляется кэширование:

  1. Запрашиваем с сервера manifest файл бандла (данный файл создается автоматически при его создании и содержит описание ассетов, которые в нем содержаться, а также значения hash, crc, размера и т.п.). Файл имеет тоже самое имя, что и бандл плюс расширение .manifest.
  2. Получаем из manifest’a значение hash128
  3. Создаем запрос к серверу для получения AssetBundle, где помимо url, указываем полученное значение hash128

В приведенном примере, Unity при запросе на сервер, сначала смотрит, есть ли в кэше файл с указанным hash128 значением, если есть, то будет возвращен он, если нет, то будет загружен обновленный файл. Для управления всеми файлами кэша в Unity присутствует класс Caching, с помощью которого мы можем узнать, есть ли файл в кэше, получить все кэшированные версии, а также удалить ненужные, либо полностью его очистить.

Примечание: почему такой странный способ получения hash значения? Это связано с тем, что получение hash128 способом, описанным в документации, требует загрузки всего бандла целиком, а затем получения из него AssetBundleManifest ассета и оттуда уже hash значения. Минус такого подхода в том, что качается весь AssetBundle, а нам как раз нужно, чтобы этого не было. Поэтому мы сначала скачиваем с сервера только файл манифеста, забираем из него hash128 и только потом, если надо скачаем файл бандла, при этом выдергивать значение hash128 придется через интерпретацию строк.

Работа с ресурсами в режиме редактора

Последней проблемой, а точнее вопросом удобства отладки и разработки является работа с загружаемыми ресурсами в режиме редактора, если с обычными файлами проблем нет, то с бандлами не все так просто. Можно, конечно, каждый раз делать их билд, заливать на сервер и запускать приложение в редакторе Unity и смотреть как всё работает, но это даже по описанию звучит как “костыль”. С этим надо что-то делать и для этого нам поможет класс AssetDatabase.

Для того, чтобы унифицировать работу с бандлами я сделал специальную обертку:


Теперь нам необходимо добавить два режима работы с ассетами в зависимости от того в редакторе мы или же в билде. Для билда мы используем обертки над функциями класса AssetBundle, а для редактора используем упомянутый выше класс AssetDatabase.

Примечание: в коде используется класс TaskManager, о нем пойдет речь ниже, если кратко, то это обертка для работы с Coroutine.

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

Пишем менеджер сетевых запросов или работа с веб-сервером

Выше мы рассмотрели основные аспекты работы с внешними ресурсами в Unity, теперь бы мне хотелось остановиться на реализации API, которая обобщает и унифицирует все выше сказанное. И для начала остановимся на менеджере сетевых запросов.

Примечание: здесь и далее используется обертка над Coroutine в виде класса TaskManager. Об этой обертке я писал в другой статье.

Заведем соответствующий класс:


Статическое поле NetworkType требуется для того, чтобы приложение могло получать сведения о типе интернет-соединения. В принципе это значение можно хранить, где угодно, я решил, что в классе Network ей самое место.

Добавим базовую функцию посылки запроса на сервер:

Как видно из кода, способ обработки завершения запроса изменен, по сравнению с кодом в предыдущих разделах. Это сделано с целью отображения прогресса загрузки данных. Также, все посылаемые запросы сохраняются в списке, с тем чтобы, если это необходимо, их можно было отменить.

Добавляем функцию создания запроса на основе ссылки для AssetBundle:

Аналогичным образом создаются функции для текстуры, аудио, текста, байт-массива.

Теперь необходимо обеспечить отправку данных сервер через команду Post. Часто нужно, что-то передать серверу, и в зависимости от того, что именно, получить ответ. Добавим соответствующие функции.

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

Аналогично добавляются методы для текстуры, аудио-файла, текста и т.д.

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

На этом наш менеджер для работы с сетевыми запроса завершен. По необходимости, каждая подсистема игры, которая требует работы с сервером может создавать свои экземпляры класса.

Пишем менеджер загрузки внешних ресурсов

Помимо описанного выше класса, для полноценной работы с внешними данными, нам нужен отдельный менеджер, который будет не просто скачивать данные, но и уведомлять приложение о начале загрузке, завершении, прогрессе, отсутствии свободного места, а также заниматься вопросами кэширования.

Заводим соответствующий класс, который в моем случае является синглетоном

Как видно, в конструкторе задается папка для кэширования в зависимости от того в редакторе мы находимся или нет. Также, мы завели приватное поле для экземпляра класса Network, который мы описали ранее.

Теперь добавим вспомогательные функции для работы с кэшем, а также определения размера скачиваемого файла и проверки свободного места для него. Далее и ниже код приводится на примере работы с AssetBundle, для остальных ресурсов все делается по аналогии.

Добавим теперь функции загрузки данных на примере AssetBundle

Итак, что происходит в данной функции:

Аналогично описанному выше методу в менеджере можно/нужно завести и другие функции работы с данными: GetJson, GetTexture, GetText, GetAudio и т.д.

И напоследок необходимо завести метод, который позволит скачивает наборы ресурсов. Данный метод будет полезен, если нам надо на старте приложения, что-то скачать или обновить.

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

Примечание: для тех, кто не любит Coroutine, все можно достаточно легко перевести на async/await, но в данном случае, в статье я решил использовать более понятный для новичков вариант (как мне кажется).

Заключение

В данной статье я постарался как можно более компактно описать работу с внешними ресурсами игровых приложений. Этот подход и код используется в проектах, которые были выпущены и разрабатываются при моем участии. Он достаточно прост и применим в несложных играх, где нет постоянного общения с сервером (ММО и другие сложные f2p игры), однако он сильно облегчает работу, в случае если нам надо скачать дополнительные материалы, языки, осуществить серверную валидацию покупок и другие данные, которые единовременно или не слишком часто используются в приложении.

Главная » Как сохранять и загружать данные игры в Unity

Как сохранять и загружать данные игры в Unity

Узнайте, как сохранять и загружать игру в Unity с помощью PlayerPrefs, Serialization и JSON. Дополните свой пользовательский опыт знаниями о том, как сохранять и загружать данные.

Но как создать файл сохранения и что в нем должно быть? Вам также нужно использовать файл сохранения, чтобы отслеживать настройки игрока? Как насчет отправки сохранений в интернет, чтобы их можно было позже загрузить на другое устройство?

В этом уроке вы узнаете:

  • Что такое сериализация и десериализация.
  • Что такое PlayerPrefs и как его использовать для сохранения настроек игрока.
  • Как создать файл сохранения игры и сохранить его на диск.
  • Как загрузить файл сохранения игры.
  • Что такое JSON и как его использовать.

Приступая к работе

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

Важные концепции сохранения

Есть четыре ключевых концепций сохранения в Unity:

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

Схема использования сериализации в Unity

Десериализация: это именно то, на что похоже. Это противоположность сериализации, а именно преобразование потока байтов в объект.

Player Prefs

Откройте проект, затем откройте сцену с именем Game и нажмите на кнопку воспроизведения.

Чтобы начать игру, нажмите на кнопку New Game. Чтобы воспроизвести игру, вы просто перемещаете мышь, и пистолет будет следовать за вашим движением. Нажмите левой кнопкой мыши, чтобы выстрелить и поразить цели (которые переворачиваются вверх и вниз через различные промежутки времени), чтобы получить игровой счет. Попробуйте и посмотрите, какой счет вы сможете набрать за тридцать секунд. Чтобы вызвать меню в любое время, нажмите кнопку esc на клавиатуре.

Какой бы забавной ни была эта игра, без музыки она могла бы быть немного суховата. Вы могли заметить, что есть переключатель музыки, но он был выключен. Нажмите на play, чтобы начать новую игру, но на этот раз установите переключатель Music на включенное положение, и при запуске игры вы услышите музыку. Убедитесь, что колонки компьютера включены!

Изменить настройки музыки было просто, но нажмите на кнопку воспроизведения еще раз, и вы заметите проблему: музыка больше не проверяется. Несмотря на то, что вы меняли настройки музыки ранее, это изменение не отслеживалось. Это то, в чем PlayerPrefs преуспевает.

Создайте новый скрипт с именем PlayerSettings в папке Scripts. Поскольку вы будете использовать некоторые элементы пользовательского интерфейса, добавьте следующую строку вверху файла с другими пространствами имен:

Затем добавьте следующие переменные:

Они будут отслеживать объекты Toggle и AudioSource.

Затем добавьте следующую функцию:

После настройки скрипт будет:

  1. Проверять, есть ли в PlayerPrefs кэшированная настройка для ключа «music». Если там нет значения, он создает пару ключ-значение для музыкального ключа со значением 1. Он также включает переключатель и включает AudioSource. Это будет запущено при первом запуске игры. Значение 1 используется, потому что вы не можете сохранить логическое значение (но вы можете использовать 0 как false и 1 как true).
  2. Проверять ключ «music», сохраненный в PlayerPrefs. Если значение установлено на 1, в проигрывателе была музыка, поэтому он включает музыку и устанавливает переключатель в положение «включено». В противном случае он отключает музыку и переключатель.

Теперь сохраните изменения в скрипте и вернитесь в Unity.

Добавьте скрипт PlayerSettings в игровой объект Game. Затем разверните игровой объект UI, а затем игровой объект Menu, чтобы открыть его дочерние элементы. Затем перетащите игровой объект Music в поле Toggle скрипта PlayerSettings. Затем выберите игровой объект Game и перетащите AudioSource в поле MyAudio.

Настройка параметров скрипта PlayerSettings в окне Inspector редактора Unity

Музыка настроена на работу при запуске игры (поскольку в функции Awake есть код), но вам все равно нужно добавить код, если игрок изменяет настройки во время игры. Откройте скрипт PlayerSettings и добавьте следующую функцию:

Это почти то же самое, что и код, который вы написали ранее, за исключением одного важного отличия. Он проверяет состояние переключателя музыки, а затем соответствующим образом обновляет сохраненную настройку. Чтобы этот метод был вызван и, следовательно, чтобы он мог выполнять свою работу, вам необходимо установить метод обратного вызова для игрового объекта Toggle. Выберите игровой объект Music и перетащите игровой объект Game на поле объекта в разделе OnValueChanged:

Установка функции-обработчика для кнопки переключения в окне Inspector редактора Unity

Выберите раскрывающийся список, в котором сейчас указано No Function, и выберите PlayerSettings -> ToggleMusic (). Когда кнопка переключения в меню нажата, она вызывает функцию ToggleMusic.

Выбор метода для обработки события нажатия кнопки в окне Inspector редактора Unity

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

Теперь настройки музыки сохранены правильно! Отличная работа, но возможности сериализации только начинаются.

Сохранение игры

Использовать PlayerPrefs было довольно просто, не так ли? С его помощью вы сможете легко сохранить в нем другие настройки, такие как графические настройки плеера или информацию для входа (например, токены Facebook или Twitter), а также любые другие настройки конфигурации, которые имеет смысл отслеживать для игрока. Однако PlayerPrefs не предназначен для отслеживания сохраненных игр. Для этого вы захотите использовать сериализацию.

Первым шагом к созданию файла сохранения игры является создание класса файла сохранения. Создайте скрипт с именем Save и удалите наследование MonoBehaviour . Удалите также стандартные методы Start () и Update () .

Затем добавьте следующие переменные:

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

Вам нужно добавить еще один очень важный фрагмент кода. Над объявлением класса добавьте следующую строку:

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

Примечание. Атрибуты имеют широкий спектр применения и позволяют прикреплять данные к классу, методу или переменной (эти данные известны как метаданные). Вы даже можете определить свои собственные атрибуты для использования в коде. Сериализация использует атрибуты [SerializeField] и [System.Serializable] , чтобы знать, что писать при сериализации объекта. Другие варианты использования атрибутов включают настройки для модульных тестов и внедрения зависимостей, которые выходят за рамки этого урока, но их стоит изучить.

Весь скрипт сохранения save должен выглядеть так:

Затем откройте скрипт Game и добавьте следующий метод:

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

Кнопка Save подключена к методу SaveGame в скрипте Game, но кода в SaveGame пока нет. Замените функцию SaveGame следующим кодом:

Рассматриваем комментарий за комментарием:

  1. Создать экземпляр Save со всеми данными текущего сеанса, сохраненными в нем.
  2. Создать BinaryFormatter и FileStream, передав путь для сохраняемого экземпляра Save. Он сериализует данные (в байты), записывает их на диск и закрывает FileStream. Теперь на вашем компьютере будет файл с именем gamesave.save. Файл .save использовался только в качестве примера, и вы могли использовать любое расширение для имени сохранения файла.
  3. Это просто сбрасывает игру, так что после сохранения игроком все находится в состоянии по умолчанию.

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

LoadGame в скрипте Game связана с кнопкой Load. Откройте скрипт игры и найдите функцию LoadGame . Замените его следующим:

Рассмотрим код детально:

Сохранение данных с помощью JSON

Внешние скобки представляют родительский объект, который является JSON. Если вы знакомы со структурой данных Dictionary , то JSON похож. Файл JSON представляет собой сопоставление пар ключей и значений. Итак, в приведенном выше примере есть 3 пары ключ-значение. В JSON ключи всегда являются строками, но значения могут быть объектами (т.е. дочерними объектами JSON), массивами, числами или строками. Значение, установленное для ключа «message» - «hi», значение ключа «age» - число 22, а значение ключа «items» представляет собой массив с двумя строками в нем.

Сам объект JSON представлен типом String. Передав эти данные в виде строки, любой язык может легко воссоздать объект JSON из строки в качестве аргумента конструктора. Очень удобно и очень просто.

У каждого языка есть свой способ создания объекта из этого формата. Начиная с Unity 5.3 существует собственный метод для создания объекта JSON из строки JSON. Вы создадите JSON-представление рекорда игрока, а затем распечатаете его на консоли. Но вы расширяете эту логику, отправляя JSON на сервер.

В скрипте Game есть метод с именем SaveAsJSON, который подключен к кнопке SaveAsJSON. Замените SaveAsJSON следующим кодом:

Это создает экземпляр Save, как и раньше. Затем он создает строку JSON, используя метод ToJSON в классе JsonUtility. Затем он выводит результат на консоль.

Вывод данных сохранения в консоль редактора Unity

Если вы хотите преобразовать этот JSON в экземпляр Save , вы должны просто использовать:

Именно так вы поступили бы, если бы захотели загрузить файл сохранения из интернета, а затем загрузить его в свою игру. Но настройка веб-сервера - это совсем другой процесс! А пока похлопайте себя по плечу, потому что вы только что выучили несколько приемов, которые . избавят вас от неприятностей в следующей игре!

Куда двигаться дальше?

Вы можете скачать итоговые файлы проекта вверху страницы.

Теперь вы получили мощный инструмент для создания отличных игр, позволяющий игрокам сохранять и загружать свои игры с помощью магии сериализации. Вы также узнали, что такое JSON и как его можно использовать для сохранения в облаке. Вы также узнали, для чего используется PlayerPrefs (настройки!) И для чего он не используется (сохранение игры).

Можно разделить на два пункта:
1. Это файл, существующий на жестком диске. Его можно назвать сжатым пакетом. Этот сжатый пакет можно рассматривать как папку, содержащую несколько файлов. Эти файлы можно разделить на две категории: сериализованные файлы и файлы ресурсов. (Сериализованные файлы и исходные файлы)
сериализованный файл: ресурс разбивается на объект и, наконец, записывается в один файл (только один).
файлы ресурсов: некоторые двоичные ресурсы (изображения, звуки) сохраняются отдельно для быстрой загрузки.
2. Это объект AssetBundle, который можно загрузить из конкретного сжатого пакета с помощью кода. Этот объект содержит все содержимое, которое мы добавили в сжатый пакет, и мы можем загрузить его и использовать через этот объект.

Пример упаковки ресурсов Asset Bundle

Независимо от того, являются ли это ресурсы модели или ресурсы пользовательского интерфейса, лучше всего сначала поместить их в Prefab, а затем превратить их в Assetbundle. Возьмем модель в качестве примера. Одна или несколько моделей могут быть помещены в Assetbundle. Он очень гибкий, поэтому самое важное, что нужно учитывать, - это занимаемое пространство модели.

Давайте возьмем реальную операцию. Сначала создайте два 3D-объекта, Cube и Capsule, и превратите их в Prefab, а затем перейдите к указанию свойства AssetBundle ресурса. Здесь я упакую эти две модели в пакет model.ab.
(xxxa / xxx) Здесь xxxa сгенерирует каталог с именем xxx, а следующий ab - суффиксное имя, которое можно настроить.


После настройки свойств начните сборку пакета AssetBundle. Сначала создайте папку с именем Editor. Эта папка является специальной папкой расширения редактора, которая не будет упакована. Затем мы создаем класс расширения редактора CreateAssetbundles. Напишите следующий код

Затем вернитесь в Unity и нажмите кнопку пакета, которую мы только что расширили.


После щелчка наша модель упакована. Вы можете найти каталог AssetBundles в каталоге проекта. В AssetBundles есть папка Scene, которая содержит наши упакованные файлы с расширением .ab.


Загрузка AssetBundle

Есть несколько способов загрузить AssetBundle: LoadFromMemoryAsync используется для загрузки из памяти, LoadFromFile может использоваться для загрузки из локальных файлов, а UnityWbRequest может использоваться для загрузки из Интернета на сервер. Давайте посмотрим на эти способы загрузки.
Сначала удалите Prefab Cube и Capsule двух моделей в нашем Unity, затем создайте скрипт, который будет висеть на камере, и откройте скрипт.

Первый метод загрузки (LoadFromMemoryAsync) загружается из памяти

Второй способ (LoadFromFile) загружается локально

Третий метод (UnityWbRequest) загружается с сервера или локально

Таким образом, ресурсы в нашем пакете Model загружаются и создаются в сцене. В это время запустите Unity, и вы увидите, что две модели были созданы отдельно.


Конечно, вы также можете создавать указанные ресурсы, такие как

Таким образом осуществляется загрузка ресурсов Asset Bundle.

Стратегия группировки AssetBundle

1. Поместите часто обновляемые ресурсы в отдельный пакет и отделите их от редко обновляемых пакетов.
2. Поместите ресурсы, которые необходимо загрузить одновременно, в пакет.
3. Вы можете поместить ресурсы, общие для других пакетов, в отдельный пакет.
4. Упакуйте несколько небольших ресурсов, которые необходимо загрузить одновременно, в пакет.
5. Если есть две версии одного и того же ресурса, рассмотрите возможность использования суффикса для различения v1 v2 v3 unity3dv1 unity3dv2

1. Группировка логических объектов
a, один интерфейс пользовательского интерфейса или один пакет для всех интерфейсов пользовательского интерфейса (один пакет для текстур и информации о макете в этом интерфейсе)
b, пакет для одного или всех персонажей (пакет для модели и анимации в этом персонаже)
c, пакет, общий для всех сцен (включая текстуры и модели)
2. Группировать по типу
Все звуковые ресурсы упакованы в один пакет, все шейдеры упакованы в один пакет, все модели упакованы в один пакет, а все материалы упакованы в один пакет
3. Группировать по использованию
объединяет все ресурсы, используемые в определенное время, в один пакет. Его можно разделить по уровням: все ресурсы, необходимые для уровня, включая персонажей, текстуры, звуки и т. Д., Сгруппированы в пакет. Его также можно разделить на сцены, один пакет ресурсов, необходимых для одной сцены

Упаковка зависимостей

Это означает, что, например, если две модели используют один и тот же материал и текстуру, то между моделью и картой текстуры существует отношение зависимости. Если наши две модели упакованы отдельно, тогда будут упакованы два материала и текстуры, так что пакет станет больше, тогда как мы его решим, вот способ в Unity, на который в первую очередь нужно положиться Упакуйте материалы и текстуры отдельно в папку, а затем упакуйте две модели, которые зависят от этого материала и текстуры. Таким образом, Unity будет искать карту материалов и обнаруживать, что материал и текстура были упакованы, поэтому не будет повторять упаковку материала и текстуры, что значительно уменьшает размер упаковки.



Вышеупомянутый метод предназначен для непосредственной упаковки двух моделей по отдельности. Вы можете видеть, что материалы и текстуры упакованы отдельно, каждый из которых составляет 63 КБ, а следующий - первый пакет материалов и текстур до 62 КБ. Затем упакуйте две модели по 2 КБ, а общий размер составит всего 64 КБ.


Это пакет зависимостей, поставляемый с Unity.
Но модель, материал и текстура должны быть загружены при загрузке, иначе будут финансовые потери.

Удаление AssetBundle

Есть два аспекта для удаления
1. Уменьшите использование памяти.
2, может привести к потере
Итак, когда удалять ресурсы
AssetBundle.Unload (true) Выгрузить все ресурсы, даже если ресурсы используются
1. При переключении между уровнями и сценами
2, когда ресурс не вызывается
AssetBundle.Unload (false) Выгрузить все неиспользуемые ресурсы
Как удалить отдельные ресурсы
1 через Resources.UnloadUnusedAssets.
2, когда сцена переключается

Проверка документов

Проверка файла может гарантировать целостность файла во время передачи файла. Например, A сгенерирует проверочный код перед передачей мне файла. Для этого файла будет сгенерирован только этот уникальный проверочный код, если он будет передан мне. Если файл немного отличается, код проверки будет совершенно другим. Поэтому, когда A передает мне файл, мне будут переданы и файл, и контрольный код. Когда я получу файл, я также буду использовать тот же алгоритм, что и A, для генерации контрольного кода файла, а затем возьму этот Значение сравнивается с контрольным кодом, который мне прислал A, если он совпадает, значит, файл готов, если нет, то повторите передачу. Ниже приведены контрольные значения, сгенерированные несколькими алгоритмами.
CRC MD5 SHA1
Сходства:
Все CRC, MD5, SHA1 вычисляются путем вычисления данных для создания контрольного значения, которое используется для проверки целостности данных.
Разница:

Читайте также: