Texture2d unity чем открыть
По традиции, для начала определимся, что это и зачем нам это надо. Итак, что же такое эти внешние ресурсы. В рамках разработки игр, такими ресурсами может быть все, что требуется для функционирования приложения и не должно храниться в конечном билде проекта. Внешние ресурсы могут находится как на жестком диска компьютера пользователя, так и на внешнем веб-сервере. В общем случае такие ресурсы — это любой файл или набор данных, который мы загружаем в наше, уже запущенное приложение. Если говорить в рамках Unity 3d, то ими могут быть:
- Текстовый файл
- Файл текстуры
- Аудио файл
- Байт-массив
- AssetBundle (архив с ассетами проекта Unity 3d)
Создать аккаунт
Зарегистрируйтесь для получения аккаунта. Это просто!
Текст
Как видно из кода, здесь используется DownloadHandler по умолчанию. Свойство text это геттер, который преобразует byte массив в текст в кодировке UTF8. Основное применение загрузки текста с сервера — это получение json-файла (сериализованное представление данных в текстовом виде). Получить такие данные можно с использованием класса Unity JsonUtility.
Работа с ресурсами в режиме редактора
Последней проблемой, а точнее вопросом удобства отладки и разработки является работа с загружаемыми ресурсами в режиме редактора, если с обычными файлами проблем нет, то с бандлами не все так просто. Можно, конечно, каждый раз делать их билд, заливать на сервер и запускать приложение в редакторе Unity и смотреть как всё работает, но это даже по описанию звучит как “костыль”. С этим надо что-то делать и для этого нам поможет класс AssetDatabase.
Для того, чтобы унифицировать работу с бандлами я сделал специальную обертку:
Теперь нам необходимо добавить два режима работы с ассетами в зависимости от того в редакторе мы или же в билде. Для билда мы используем обертки над функциями класса AssetBundle, а для редактора используем упомянутый выше класс AssetDatabase.
Примечание: в коде используется класс TaskManager, о нем пойдет речь ниже, если кратко, то это обертка для работы с Coroutine.
Помимо описанного выше, также в ходе разработки полезно смотреть, что именно мы загрузили и что находится сейчас в кэше. С этой целью можно воспользоваться возможностью установки своей папки, которая будет использоваться для кэширования (в эту же папки можно записывать и скачанные текстовые и другие файлы):
Войти
Проверка доступа в интернет
Пишем менеджер загрузки внешних ресурсов
Помимо описанного выше класса, для полноценной работы с внешними данными, нам нужен отдельный менеджер, который будет не просто скачивать данные, но и уведомлять приложение о начале загрузке, завершении, прогрессе, отсутствии свободного места, а также заниматься вопросами кэширования.
Как видно, в конструкторе задается папка для кэширования в зависимости от того в редакторе мы находимся или нет. Также, мы завели приватное поле для экземпляра класса Network, который мы описали ранее.
Теперь добавим вспомогательные функции для работы с кэшем, а также определения размера скачиваемого файла и проверки свободного места для него. Далее и ниже код приводится на примере работы с AssetBundle, для остальных ресурсов все делается по аналогии.
Итак, что происходит в данной функции:
Аналогично описанному выше методу в менеджере можно/нужно завести и другие функции работы с данными: GetJson, GetTexture, GetText, GetAudio и т.д.
И напоследок необходимо завести метод, который позволит скачивает наборы ресурсов. Данный метод будет полезен, если нам надо на старте приложения, что-то скачать или обновить.
Здесь стоить понимать особенность работы TaskManager, который используется в менеджере сетевых запросов, по умолчанию он работает, выполняя все задачи по очереди. Поэтому загрузка файлов будет происходить соответственно.
Примечание: для тех, кто не любит Coroutine, все можно достаточно легко перевести на async/await, но в данном случае, в статье я решил использовать более понятный для новичков вариант (как мне кажется).
Заключение
В данной статье я постарался как можно более компактно описать работу с внешними ресурсами игровых приложений. Этот подход и код используется в проектах, которые были выпущены и разрабатываются при моем участии. Он достаточно прост и применим в несложных играх, где нет постоянного общения с сервером (ММО и другие сложные f2p игры), однако он сильно облегчает работу, в случае если нам надо скачать дополнительные материалы, языки, осуществить серверную валидацию покупок и другие данные, которые единовременно или не слишком часто используются в приложении.
Основной контент в играх — это почти всегда текстуры, поэтому нужно особенно внимательно следить за ними в целом, их размерами и сжатием.
Раньше на проекте War Robots у нас был устоявшийся и вполне рабочий пайплайн по импорту текстурных массивов, на выходе которого мы получали массивы в конечном формате (ASTC, ETC2), отлично удовлетворяющие нашим требованиям для мобильных платформ. С этим все у нас было хорошо — до поры. Проблемы начались тогда, когда возникла необходимость релиза на ПК.
В этой статье мы поговорим о проблемах, с которыми мы столкнулись и какие нам пришлось преодолеть: о проблеме специфической поддержки текстурных массивов Unity и о проблеме разных форматов под разные платформы. Наконец, расскажем о работе ScriptedImporter, который помог нам решить обе: как делать нельзя и как нужно.
Ранее мы рассказывали о том, как перешли на новую схему работы над картами: как мы сначала собираем исходные карты, а затем генерируем их вариации для разных пресетов качества, которые затем идут в мобильные и десктопные билды. Чтобы собрать финальную карту, все текстуры, материалы и меши на ней запекаются, после чего часть текстур идет в так называемые текстурные массивы.
Текстурные массивы (Texture2DArray в Unity) — это ассет-файлы, в которых записано несколько бинарных представлений текстур, уже пожатых в необходимом для конечного девайса формате. Такие массивы позволяют объединить несколько текстур в один ресурс, который можно единожды привязать к графичеcкому пайплайну (binding — в терминах графических API), и затем множество вызовов отрисовки смогут использовать этот ресурс без необходимости перепривязки между вызовами. Это снижает нагрузку на ЦПУ, поскольку уменьшает количество обращений к графическому API, что в итоге положительно сказывается на производительности. В Unity текстурные массивы позволяют использовать один материал для множества объектов, а значит — при рендере будет работать batching, и их отрисовка будет более эффективной.
Текстурные массивы можно рассматривать как альтернативу текстурным атласам, лишенной их недостатков. Так, при тайлинге использование текстурного пространства становится гораздо эффективнее. Поскольку не нужно добавлять «защитные» области, с помощью закраски (dilation) или дополнительными проверками в коде шейдера в случае с текстурными массивами выборкой данных полноценно занимается специализированный блок — sampler. В результате более эффективно используются вычислительные ресурсы и текстурный кэш GPU. Также для текстурных массивов можно использовать привычный механизм генерации mip-уровней, не боясь появления артефактов в виде швов. Однако, в отличие от атласов, все текстуры в массиве обязательно должны быть одинакового разрешения и формата.
Что удобнее в вашем конкретном случае, решение уже за вами — в нашем, например, использование текстурных массивов было очевидным.
Пример текстурного атласа и текстурного массива
В документации написано, что в Unity2019.4 (на которой сейчас разрабатывается War Robots) не существует пайплайна для импорта текстурных массивов, и предлагается создавать массивы либо прямо в рантайме, либо в эдиторных скриптах с последующим сохранением их, используя AssetDatabase.CreateAsset.
Ниже мы опишем, как выглядел наш кастомный пайплайн в самом начале, с какими проблемами мы столкнулись, как его переделывали и на чем в результате остановились — от самых костыльных решений до финальной версии.
Исходный флоу для текстурных массивов на War Robots
Изначальный флоу работы с текстурными массивами был создан нашим тех. артистом. Изначально он предназначался для мобильных платформ и отлично справлялся со своей задачей — то есть, выгонял массивы в конечном формате (ASTC, ETC2). Выглядел он следующим образом: это были конфигурации, в которые последовательно добавлялись текстуры, слой за слоем. На каждую карту существует две таких конфигурации: первая учитывает пропсы, вторая — террейн. Вот пример конфигурации пропсов для одной из карт (отображены не все слои):
Мы используем несколько пресетов качества (HD и LD), и для каждого из них можно выбирать основные настройки будущего текстурного массива: разрешение и компрессию.
Самый первый алгоритм создания текстурных массивов выглядел примерно так. Первым шагом выполнялся реимпорт исходной текстуры, содержащей несжатые данные.
Зачем вообще там нужен реимпорт текстур?
Использование любого сжатого формата должно производиться именно из оригинальной исходной текстуры. Исходная текстура — это не одно и то же, что разжатая. Проблема в том, что все без исключения форматы с компрессией являются сжатием с потерями (losy compression), и на самом деле так или иначе «уродуют» текстуру в зависимости от выбранного алгоритма сжатия. Таким образом, из любого сжатого формата восстановить исходную текстуру уже не получится: разжатая текстура будет иметь ровно те же артефакты, что и сжатая. Именно поэтому категорически нельзя делать что-то вроде «ETC2 → ASTC → DXT», поскольку на выходе будет нечто, вобравшее в себя артефакты компрессий всех трех форматов. Всегда нужно работать только с исходной текстурой и никак иначе.
Важно понимать почему обязательно следует использовать исходную текстуру без сжатия: слева картинка сжата в ASTC, а справа — каскад ETC → DXT → ASTC. Как можно заметить, справа видны артефакты после применения последовательности компрессий.
Далее выполнялась поканальная перепаковка текстур для каждого слоя. В результате из таких текстур в конфигурации для слоя 0…
. мы получаем две поканально перепакованные текстуры. Первая — AS, содержит Albedo + Smoothness, вторая — MNAO, содержит Metalness + Normals + Ambient Occlusion:
Эти текстуры не сохраняются в проекте и попадают в разные текстурные массивы (AS/MNAO). По каждому слою (из рисунка с конфигом) получаем две поканально перепакованные текстуры, каждая из которых является будущим слоем для своего текстурного массива.
Далее применяем компрессию, которая была указана в конфиге, создаем из этих текстур объект Texture2DArray (в данном примере мы создаем сразу два Texture2DArray — для AS и для MNAO) и сохраняем каждый как ассет, используя AssetDatabase.CreateAsset, как нам и советовала документация Unity. В итоге получается так называемый «запеченный» текстурный массив, над которым можно произвести только две операции: назначить его в материал или удалить из проекта. Изменить размер и/или тип компрессии у него уже невозможно.
Всё это отлично работало для мобильных платформ — ровно до момента релиза на ПК-платформы. Проблема заключается в том, что компрессия ASTC не поддерживается на standalone-платформах, в частности — под Windows. В результате почти для каждого релиза требовалось создание второго набора ассетов, но уже в формате DXT5/BC7, поддерживаемого standalone-платформами. Такое ветвление довольно сильно усложняло процесс подготовки контента, сборки билдов и, что хуже всего, требовало постоянной рутинной ручной работы в довольно большом объеме. По факту требовалась возможность создания всего одно ассета вместо нескольких (под мобильные и ПК платформы), в котором под каждую платформу можно указать настройки разрешения и компрессии по аналогии с обычными текстурами. При этом сама методика создания массивов и наполнения слоев должна была остаться прежней.
Тогда на помощь пришел ScriptedImporter из Unity. Он позволяет для любого типа файлов написать свой алгоритм импорта, чтобы Unity понимал, как работать с этим типом ассетов.
При реализации кастомного импортера было несколько требований:
исправляем исходный алгоритм создания массива только при крайней необходимости, а лучше вообще его не трогать;
результирующий ассет (текстурный массив) не должен зависеть от исходных текстур;
результирующий ассет (текстурный массив) должен попадать в кэш акселератора.
Первый вариант импортера: так делать нельзя
В одном из первых вариантов реализации предлагалось внутри каждого ассета массива хранить ссылку на нужный конфиг, а также название целевого качества и типа массива (AS/MNAO). Выглядело это примерно вот так:
То есть, это был обычный json, из которого можно было сделать полноценный Unity-ассет.
При первых попытках написать импортер сразу вылезла проблема из-за реимпорта текстур: мы не можем внутри одного импорта вызвать другой импорт. Точнее, можем, но вложенный вызов импорта по факту начнет работать только тогда, когда мы выйдем из вызывающего импорта.
На основе этой идеи оперативно родилось это чудовище — алгоритм, который мы, к счастью, не применили:
Это была «трехходовка», описание прилагается:
Мы попадаем в импорт нашего созданного ассета. В метод импорта передается контекст, который хранит всю необходимую нам информацию, а именно: путь до ассета, целевую платформу и другие полезные данные.
Читаем данные внутри ассета — в нашем случае это был просто json, в котором указывался Guid конфига, качество и тип целевых текстур. Загружаем по Guid конфиг, в котором присутствуют ссылки на исходные текстуры.
Для каждой текстуры проверяем, есть ли ее дубликат во временной директории.
Если какого-то дубликата нет (а изначально их нет вообще), инициируем копирование текстуры во временную папку и выставляем зависимость нашего исходного ассета массива от этой копии текстуры. Непосредственно в этот момент копий текстур еще не существует, но зависимость мы можем добавить даже до несуществующего ассета (баг или фича — до сих пор не понятно).
Выходим из импорта — только после этого по факту начнут копироваться текстуры.
Так как в шаге 4 мы добавили зависимости от текстур и инициировали их копирование, то после копирования Unity снова вызовет импорт нашего ассета (шаг 1). Но на этот раз все текстуры уже лежат во временной папке, так что мы доходим до шага 6.
Проверяем настройки временных текстур — у них должен быть одинаковый целевой размер и формат — кроме того, текстуры должны быть isReadable и т. д.
Если по каким-то причинам какая-либо временная текстура не удовлетворяет этим требованиям, мы правим настройки ее импортера и инициируем ее реимпорт.
Выходим из импорта и, как было отмечено ранее, только после этого по факту начнется реимпорт текстур.
Так как в шаге 7 мы сменили настройки и инициировали реимпорт текстур (помним, что в шаге 4 мы добавили зависимость от этих текстур), то после их реимпорта Unity снова вызовет импорт нашего текстурного массива, и мы снова попадем в шаг 1. Но в этот раз текстуры уже лежат во временной папке, и все их параметры соответствуют требованиям нашего алгоритма для создания текстурного массива. Поэтому на этот раз мы подходим к шагу 9.
Применяем целевую компрессию, так как наши копии текстур в данный момент без сжатия (RGBA).
По каждому слою текстур создаем поканально перепакованные текстуры.
Из поканально перепакованных текстур создаем объект типа Texture2DArray.
Назначаем этот объект в контексте импорта как основной — именно он затем подгрузится, если мы вызовем AssetDatabase.LoadAssetAtPath.
Удаляем копии текстур из временной папки.
Выходим из импорта.
Мы реализовали этот алгоритм, но у него было два огромных минуса:
Он совсем неочевиден: для каждого ассета массива мы попадаем в импорт три раза, и только после третьего захода мы получаем необходимый артефакт в Library.
Из-за удаления копий текстур (шаг 13) результирующий артефакт не попадал в кэш акселератора при сборке на TeamCity. Конечно, можно было написать очередной BuildStep для нашего кастомного билд-пайплайна, но к тому моменту уже была уверенность, что это будет совсем перебор, и нужно менять подход к созданию файла ассета и его импорту.
Второй вариант импортера: не идеальный, но рабочий
В ходе последующих дискуссий с нашими тех. артистами решили распилить алгоритм до момента, когда мы перепаковываем каналы. То есть, в процессе создания файла для текстурного массива весь замес с реимпортом текстур и перепаковкой каналов происходит до непосредственного импорта. Поканально перепакованные текстуры мы решили сохранить прямо внутри ассета с максимальным разрешением, которое нам пригодится в дальнейшем импорте. Все текстуры внутри файла ассета, естественно, хранятся без сжатия. Затем мы сохраняем все это в виде байтового массива в своем формате. Сам формат нового ассета выглядит примерно так:
В процессе сериализации текстуру в виде байтового массива мы получаем через Texture2D.GetRawTextureData() , а во время десериализации восстанавливаем текстуру через Texture2D.LoadRawTextureData() .
То есть, еще раз: в новом варианте ассета мы храним не json, а bytearray своего формата, где содержатся все необходимые данные (и перепакованные текстуры в том числе) для успешного импорта нашего текстурного массива.
В результате сам импорт стал линейным и понятным:
И эта схема оказалась действительно рабочей:
мы только частично поменяли флоу создания текстурного массива;
мы не поменяли алгоритм перепаковки каналов исходных текстур;
мы не добавили зависимости от исходных текстур;
наш результирующий ассет успешно попадает в кэш акселератора.
Естественно, когда мы убедились окончательно, что этот вариант нас устраивает, мы быстренько запилили кастомный инспектор для наших текстурных массивов. Примерно так это выглядит:
В этом инспекторе можно изменить параметры под конкретную платформу, и эти параметры обязательно применятся при очередном импорте нашего Texture2DArray.
Сравнивая первый и второй варианты форматов файлов наших ассетов (обычный json vs бинарник собственного сочинения), мы получали (забудем пока про акселератор) совершенно одинаковые текстурные массивы. Всё это из-за того, что Unity не работает с содержимым файлов ассетов напрямую — он работает с результатом импорта файлов этих ассетов, которые как раз лежат в Library.
Создайте аккаунт или войдите в него для комментирования
Вы должны быть пользователем, чтобы оставить комментарий
Аудио
Для работы с аудио необходимо использовать специальный метод создания запроса UnityWebRequestMultimedia.GetAudioClip, а также для получения представления данных в нужном для работы в Unity виде, необходимо использовать DownloadHandlerAudioClip. Помимо этого, при создании запроса необходимо указать тип аудиоданных, представленный перечислением AudioType, который задает формат (wav, aiff, oggvorbis и т.д.).
Возможности Unity 3d
Аналогичным образом можно получать не только текстовые данные, но и другие:
Работа с UWR в целом схожа с WWW в своей основе, однако есть и отличия, речь о которых пойдет дальше. Ниже приведен аналогичный пример загрузки текста.
Основные изменения, которые привнесла новая система UWR (помимо изменений принципа работы внутри) — это возможность назначать самому обработчиков для загрузки и скачивания данных с сервера, подробнее можно почитать здесь. По умолчанию это классы UploadHandler и DownloadHandler. Сам Unity предоставляет набор расширений этих классов для работы с различными данными, такими как аудио, текстуры, ассеты и т.п. Рассмотрим подробнее работу с ними.
Создайте аккаунт или войдите в него для комментирования
Вы должны быть пользователем, чтобы оставить комментарий
Изменения статусов
Kogekko » Haoose
Hi Haoose. Could you help me translating MGR using flatz’s tool? You can check my comment at page 153.
Kogekko » LinkOFF
Hi LinkOFF. Could you help me translating MGR using flatz tool? You can check my comment at page 153.
ehr235 » SerGEAnt
Помогите пожалуйста с руссфикатором для bioshock
ehr235 » SerGEAnt
Не нужно нагнетать.Лучше заправьте по утру кровать. Поцелуйте любимую жену, снимите с апельсина кожуру. Включите любимую игру, ну и конечно обнимите душеньку жену.
Не хватает свободного места
Одной из первых проблем при загрузке данных с сервера является возможная нехватка свободного места на устройстве. Часто бывает, что пользователь использует для игр (особенно на Android) старые устройства, а также и сам размер скачиваемых файлов может быть достаточно большим (привет PC). В любом случае, эту ситуацию необходимо корректно обработать и заранее сообщить игроку, что места не хватает и сколько. Как это сделать? Первым дело необходимо узнать размер скачиваемого файла, это делается по средствам запроса UnityWebRequest.Head(). Ниже представлен код для получения размера.
Здесь важно отметить одну вещь, для правильной работы запроса, сервер должен уметь возвращать размер контента, в противном случае (как, собственно, и для отображения прогресса) будет возвращаться неверное значение.
После того, как мы получили размер скачиваемых данных, мы можем сравнить его с размером свободного места на диске. Для получения последнего, я использую бесплатный плагин из Asset Store.
Примечание: можно воcпользоваться классом Cache в Unity3d, он может показывать свободное и занятое место в кэше. Однако здесь стоит учесть момент, что эти данные являются относительными. Они рассчитываются исходя из размера самого кэша, по умолчанию он равен 4GB. Если у пользователя свободного места больше, чем размер кэша, то проблем никаких не будет, однако если это не так, то значения могут принимать неверные относительно реального положения дел значения.
Пишем менеджер сетевых запросов или работа с веб-сервером
Выше мы рассмотрели основные аспекты работы с внешними ресурсами в Unity, теперь бы мне хотелось остановиться на реализации API, которая обобщает и унифицирует все выше сказанное. И для начала остановимся на менеджере сетевых запросов.
Примечание: здесь и далее используется обертка над Coroutine в виде класса TaskManager. Об этой обертке я писал в другой статье.
Заведем соответствующий класс:
Статическое поле NetworkType требуется для того, чтобы приложение могло получать сведения о типе интернет-соединения. В принципе это значение можно хранить, где угодно, я решил, что в классе Network ей самое место.
Как видно из кода, способ обработки завершения запроса изменен, по сравнению с кодом в предыдущих разделах. Это сделано с целью отображения прогресса загрузки данных. Также, все посылаемые запросы сохраняются в списке, с тем чтобы, если это необходимо, их можно было отменить.
Аналогичным образом создаются функции для текстуры, аудио, текста, байт-массива.
Теперь необходимо обеспечить отправку данных сервер через команду Post. Часто нужно, что-то передать серверу, и в зависимости от того, что именно, получить ответ. Добавим соответствующие функции.
Теперь добавим публичные методы с помощью, которых мы будем осуществлять загрузку данных, в частности AssetBundle
Аналогично добавляются методы для текстуры, аудио-файла, текста и т.д.
И напоследок добавляем функцию получения размера скачиваемого файла и функцию очистки, для остановки всех созданных запросов.
На этом наш менеджер для работы с сетевыми запроса завершен. По необходимости, каждая подсистема игры, которая требует работы с сервером может создавать свои экземпляры класса.
Текстура
Загрузка текстур схожа с таковой для аудио файлов. Запрос создается с помощью UnityWebRequestTexture.GetTexture. Для получения данных в нужном для Unity виде используется DownloadHandlerTexture.
Создать аккаунт
Зарегистрируйтесь для получения аккаунта. Это просто!
Войти
Основные проблемы и решения при работе с веб-сервером и внешними данными
Выше были описаны простые способы взаимодействия приложения с сервером по части загрузки различных ресурсов. Однако на практике все обстоит гораздо сложнее. Рассмотрим основные проблемы, которые сопровождают разработчиков и остановимся на путях их решения.
Благодарности
Хочу также поблагодарить своих коллег из Pixonic: Романа Вишнякова, Павла Кирсанова, Александра Агапкина, Александра Богомольца и Дмитрия Четверикова — они очень помогли в процессе работы над импортером текстурных массивов, а также оказали мощнейший саппорт в описании технической части по графике и вообще в создании данной публикации.
Когда вы работает с игровыми объектами в Unity3d, вам потребуется также использовать материалы. Например, мы хотим добавим материал с текстурой к кубу.
Для начала сохраним нашу текстуру в папку Assets (в корневой папке проекта).
Добавим директорию Materials (мы помним, что всегда удобно поддерживать «иерархический порядок» в проекте) и назовем наш файл bulb texture (расширение *.jpg (в оригинальном уроке *.psd), см. примечание).
Переключимся назад в Unity3d, убедимся что в Project View появилась папка Materials c нашей текстурой.
Создадим куб. Добавить нашу текстуру к кубу, можно просто перетащив ее из Project View на куб (материал создастся автоматический). Но, возможно, вы захотите настроить его заранее, тогда выбираем Create->Material.
Назовем его bulb и положим в папку Materials.
Выберем buld и перенесем нашу текстуру из Project View на пустой серый квадрат в Inspector View (с надписью none(Texture 2D)), либо нажав на Select все в том же квадрате и выбрав ее из появившегося списка.
Взамен серого квадрата появится иконка с текстурой.
Перетяним наш материал на куб. Куб сразу станет текстурированным.
При выборе материала в Inspector View вы так же можете выбрать шейдер, по которому будет расчитываться его конечный вид
PS: О шейдерах мы обязательно поговорим отдельно, но много позже.
Так же у материала можно настраивать tiling (мозаичное размещение текстуры). Чем больше значения tiling по x-ой или y-ой координате, тем больше раз она повторяется по соответствующей стороне. Чтобы понять как это работает — поставте значения по x и y равным 10 (при изменении значения текстура на кубе будет обновляться в реалтайме).
Параметр offset (смещение) отвечает за сдвиг текстуры «по кругу». Чтобы лучше понять как он работает, поэкспериментируйте c ним.
Помимо PSD, Unity3d так же поддерживает импорт следующих форматов:
TIFF, JPG, TGA, PNG, GIF, BMP, IFF, PICT.
Как и при всяком наложении текстур, наиболее эффективным (для производительности) будет, если стороны текстуры являются степенью двойки (128, 256, 512, и т.д.). Но при этом не обязательно чтоб текстура была двадратной (к примеру, 256x512 допустимый размер текстуры).
Базовый Урок 10 — Основы работы с аудио
В уроке рассказывается, как проигрывать компонент Audio Source (аудио ресурс) и вызывать звуковые клипы с помощью скриптинга.
- AudioListener (аудио приемник)
- AudioSource (аудио ресурс)
Обычно AudioListener является компонентом у камеры. Выберем камеру и убедимся в этом, посмотрев в Inspector View.
Для того чтобы слушать звуки вы должны иметь хотя бы один AudioListener в каждой сцене.
Добавим пустой GameObject и добавим к нему компонент Audio Source.
Добавить аудио-клип можно либо через Inspector View, либо с помощью скрипта.
В нашем проекте есть два аудио-файла — моно и стерео.
Моно обычно используется для звуковых эффектов. Только для него можно использовать затухание или другие 3d-эффекты.
Стерео, соответственно, для музыки, т.к. стереозвук прогирывается как есть.
В окне Preview можно проиграть выбранный файл.
Добавим наш клип (GreenadeLaunch) в поле Audio Clip. Если включен параметр Play On Awake, то звук будет проигрываться сразу после запуска сцены.
Добавим скрипт к пустому GameObject, а значение переменной myClip у скрипта проиницилизируем звуковым файлом (GreenadeLaunch). Теперь нажав на play вы снова усшылите звук.
Проигрывать аудио можно и без использования Audio Source. Удалим компонент Audio Source c нашего GameObject, а код в теле метода Start() заменим следующим:
Тут, PlayClipAtPoint — статическая функция класса AudioSource, которая создает новый GameObject, с соответствующим Audio Source (в нашем случае это myClip) и после проигрывания аудио-клипа удаляет данный GameObject.
Сохраните скрипт и убедитесь что он есть у нашего GameObject, а значение myClip равно GreenadeLaunch.
Жмем Play и сразу после загрузки сцены жмем на Pause чтобы убедиться что на сцене появился новый игровой объект с именем One Shot Audio.
Базовый Урок 11 — Основы соединений
В уроке рассказывается, как использовать Fixed joint (неподвижное соединение) и Hinge joint (шарнирное соединение) для создания цепи у «шара для разрушения зданий»
Для создания интересной физики в Unity3d воспользуемся «соединениями» (joints). И для илюстрации этого создадим модель шара для разрушения зданий.
Исходная сцена содержит плоскрость (Floor), камеру (Main Camera) и точечный источник света (Point light).
Для начала создадим cферу, добавим к ней компонент Rigidbody и назовем ее Ball.
Добавим на сцену цилиндр, так же добавив к нему Rigidbody. Размер и положения цилиндра и сферы указанны на рисунке ниже. Для цилиндра scale равно 0.15, 0.25 и 0.15, для x, y и z соответственно, цилиндр свинут «вверх» к «краю» шара (грани при этом пересекаются).
Чтобы связать сферу и цилиндр — выбирете сферу (Ball) и добавьте к ней компонент Fixed Joint. Переменную Connected Body проинициализируем значением Cylinder.rigidbody. Напомню, что для этого достаточно перетащить Cylinder из Hierarchy view в Inspector View, на поле напротив Connected Body.
Переименуем Cylinder на более подходящее названия для крепежа шара, например, Ballstem. Значение переменной Connected Body поменяется автоматический.
Нажмите play и увидите как шар падает вместе с прикрепленным к нему цилиндром.
Теперь начнем создавать подвижные участки цепи.
Создадим капсулу (назовем ее Chain1), при этом размеры ее будут аналогичны размерам Ballstem'а, а находится она будет чуть выше него, чтоб крайние грани объектов пересекались. Chain1 так же будет с компонентом rigidbody.
А к Ballstem'у добавим компонент Hinge Joint, у которого
Connected Body мы инициализируем Chain1.rigidbody.
Место закрепление обозначенно ораньжевой стрелкой.
Нам потребуется еще один участок цепи. Выберем Chain1 и сделаем его дубликат (ctrl+D или cmd + D). Переименуем его в Chain2, так же сдвинув его к верхушке Chain1. К Сhain1 добавим Hinge Joint, с
Connected Body равным Chain2.rigidbody.
Аналогично добавим Chain3.
В конце, мы добавим HingeJoint к Chain3, при этом мы не будем задавать значение Connected Body. В этом случае объект будет прикрепленн «сам к себе» (точнее сказать к пустому месту в сцене).
Выделем Ball, Ballstem, Chain1, Chain2, Chain3 и наклоним их.
Жмем play и смотрим как перемещаются наш шар с цепью.
Базовый Урок 12 — Ввод с использованием виртуальных осей
В уроке рассказывается, как использовать стрелки (arrow keys), для перемещения влево и вправо, с помощью «осей» (axes)
На это раз на сцене у нас есть плоскость (floor), камера (Main Camera), источник света (Point light) и пустой gameObject.
Еще один вариант ввода — использование «осей» (Axes).
Откроем Input Manager (Edit->Project settings->Input). Разверните Axis и затем Horizontal.
Посмотрим на некоторые дефолтные параметры:
Параметр | Значение | Комментарий |
Negative Button | left | Клавиша для перемещения в отрицательном направлении |
Positive Button | right | Клавиша для перемещения в положительном направлении |
Alt Negative Button | a | Альтернативная клавиша для перемещения в отрицательном направлении |
Alt Possitive Button | d | Альтернативная клавиша для перемещения в положительном направлении |
Type | Key or Mouse Button | Тип ввода, для перемещения вдоль данной оси (в нашем случае X) |
Axis | X axis | Ось объекта, вдоль которой мы будем перемещаться |
Для того, чтобы использовать данное перемещение (Horizontal), нам потребуется метод GetAxis() класса Input.
- если написано «метод класса», то предполагается что он static.
- если написано «метод объекта», тогда метод принадлежит конкретному объекту.
Добавим скрипт к пустому gameObject'у, и нажмем play. Если зажать стрелку влево/вправо (или же a/d), в status bar'е вы увидите как меняется значение переменной horiz:
Значение этой переменной может меняться от -1 до 1 (в зависимости от зажатой клавиши). Как только вы отпустите клавишу, horiz станет снова равен 0.
Добавим еще строчку кода в Update(), сразу за Debug.Log():
Запустим сцену и нажмем на стрелку — вы увидите, как наш gameObject начнет перемещаться вдоль оси X.
Продвигаемые темы
Автор: james_sun
Создана 7 марта
Автор: Daniil Kortez
Создана 31 марта
Автор: Morfius354
Создана 30 марта
Автор: Outcaster
Создана 3 марта
Автор: pipindor666
Создана 17 декабря, 2021
В инсталяторе, который размещен в ZOG вроде показывается этап с обновлением видео файлов
Однако видео на англ а русский язык только в катсценах на игровом движке.
Или этот перевод не совсем верно встает на Steam версию?
Для тех кто играет на эмуляторе Yuzu. Кидаете папку romfs сюда. C:\Users\”ИМЯ ПОЛЬЗОВАТЕЛЯ”\AppData\Roaming\yuzu\sdmc\atmosphere\contents\0100FF500E34A000
Понимаю, сам не люблю разного рода “чернушность” . Но тут скорее под ретро сделали, более-менее оправдано.
Я и не спорил что они качественные и интересные. Про графон пошутил просто. --------------------- Из подборки только только Batora: Lost Haven заинтересовало… сначала, но посмотрев страницу в Стиме такое себе опять (кстати с русской локой). Silt арт шикарный… но игры с ним нет.
Разве что только интересно, напоминает смесь Акварии и Insanely twisted shadow planet, но, отпугивает эта, чёрно-белая тематика. Остальное, на мой вкус, какое-то слишком авангардное.
Я не силен в дотане,но имхо там наверное ещё что то идет..может косметика. Upd Погуглил ,точно Боевые пропуски это покупаемые предметы которые дают доступ к турниру и возможностям события, также как и множеству получаемых косметических предметов. Ранее они были известны как компендиумы.
Работа с ресурсами
Кэширование
Следующей, и одной из самых важных проблем, является кэширование скачиваемых файлов. Для чего же нужно это кэширование:
- Экономия траффика (не скачивать уже скаченные данные)
- Обеспечение работы в отсутствии интернета (можно показать данные из кэша).
Аналогично, получение данных из кэша.
Примечание: почему для загрузки текстур не используется тот же самый UWR с url вида file://. На данный момент наблюдается проблемы с этим, файл просто напросто не загружается, поэтому пришлось найти обходной путь.
Примечание: я не использую прямую загрузку AudioClip в проектах, все такие данные я храню в AssetBundle. Однако если необходимо, то это легко сделать используя функции класса AudioClip GetData и SetData.
В отличие от простых ресурсов для AssetBundle в Unity присутствует встроенный механизм кэширования. Рассмотрим его подробнее.
В своей основе этот механизм может использовать два подхода:
- Использование CRC и номера версии
- Использование Hash значения
Итак, каким образом осуществляется кэширование:
- Запрашиваем с сервера manifest файл бандла (данный файл создается автоматически при его создании и содержит описание ассетов, которые в нем содержаться, а также значения hash, crc, размера и т.п.). Файл имеет тоже самое имя, что и бандл плюс расширение .manifest.
- Получаем из manifest’a значение hash128
- Создаем запрос к серверу для получения AssetBundle, где помимо url, указываем полученное значение hash128
В приведенном примере, Unity при запросе на сервер, сначала смотрит, есть ли в кэше файл с указанным hash128 значением, если есть, то будет возвращен он, если нет, то будет загружен обновленный файл. Для управления всеми файлами кэша в Unity присутствует класс Caching, с помощью которого мы можем узнать, есть ли файл в кэше, получить все кэшированные версии, а также удалить ненужные, либо полностью его очистить.
Примечание: почему такой странный способ получения hash значения? Это связано с тем, что получение hash128 способом, описанным в документации, требует загрузки всего бандла целиком, а затем получения из него AssetBundleManifest ассета и оттуда уже hash значения. Минус такого подхода в том, что качается весь AssetBundle, а нам как раз нужно, чтобы этого не было. Поэтому мы сначала скачиваем с сервера только файл манифеста, забираем из него hash128 и только потом, если надо скачаем файл бандла, при этом выдергивать значение hash128 придется через интерпретацию строк.
Бонус. Ненормальная работа со Scripted Importer
А теперь для закрепления темы кастомных импортеров давайте немного пошалим.
Для начала найдем в интернете картинку и просто сохраним ее URL в текстовом файле с именем, например, “pixonic.wwwtexture”, а затем добавим этот файл в проект. Если открыть этот «ассет» в текстовом редакторе, мы просто увидим нашу ссылку:
На данный момент Unity понятия не имеет, как обращаться с файлами, у которых расширение “wwwtexture”, поэтому мы видим следующую картину:
Давайте объясним Unity, что мы от нее хотим — для этого напишем вот такой маленький импортер:
Тут все должно быть максимально ясно: читаем файл ассета и получаем URL, затем скачиваем эту картинку, получаем объект Texture2D, после чего просто назначаем этот объект как основной артефакт в данном ассете. Et voila!
Картинка скачалась, случился успешный импорт нашего ассета, и теперь в инспекторе это выглядит так:
Сразу замечу: скачивать надо именно синхронно — иначе чуда не произойдет.
Теперь мы можем назначить этот ассет в материал, и все будет работать:
Естественно, так делать не надо, и у меня даже идей нет, где подобные ассеты с таким импортом могли бы пригодиться. Этот пример следует рассматривать как шутку и просто как демонстрацию возможностей ScriptedImporter.
Заключение
Текстурные массивы выгодно отличаются от текстурных атласов, позволяя эффективнее использовать ресурсы GPU и значительно сэкономить память на мобильных девайсах. Однако их импорт Unity не поддерживается и требует особого внимания к методу сжатия, если речь идет о работе над билдами под разные платформы.
Unity ScriptedImporter — очень мощный инструмент, который позволяет наладить работу с форматами файлов, которые не поддерживаются Unity «из коробки». С его помощью мы:
реализовали пайплайн для импорта файлов текстурных массивов нашего формата, слегка поправив существующий пайплайн создания контента;
избавились от нужды поддержки дополнительных релизных веток (mobile/standalone) и обязательных периодических ручных трудозатрат;
получили полный карт-бланш для дальнейшей поддержки и модификации процесса импорта таких ассетов.
Автор статьи — Александр Скотников, Automation Developer в Pixonic
Похожие публикации
Но здесь есть нюанс: если взять только первую тысячу самых популярных игр, то на Unity и Unreal в среднем выходит примерно одинаковое количество — чуть больше 10 игр каждый месяц.
Платформа SteamDB научилась определять движки у игр, вышедших в Steam. Пока что технология работает неидеально, но уже позволяет сравнить популярность современных технологий.
С отрывом самым популярным движком у разработчиков является Unity — с января 2020 года на нем вышло примерно в 4 раза больше игр (~10400 против ~2800), чем на различных версиях Unreal Engine.
Если брать актуальные данные то, в июне 2021 года вышло 899 игр на Unity против 199 игр на Unreal.
Но здесь есть нюанс: если взять только первую тысячу самых популярных игр, то на Unity и Unreal в среднем выходит примерно одинаковое количество — чуть больше 10 игр каждый месяц. Более того, в этом случае к паре лидеров вплотную приближаются Game Maker и RPG Maker.
Самые популярные движки в Steam:
Unity — 24714 Unreal — 6117 Game Maker — 2204 RPG Maker — 1719 Construct — 1241 RenPy — 1076 XNA — 540 Adobe Air — 386 Godot — 305 Mono Game — 269
По оценкам самой Unity, на нем работает примерно половина всех мобильных игр, а также ряд крупных проектов, включая Fall Guys: Ultimate Knockout и Ori and the Will of the Wisps.
В первый день торгов на бирже стоимость Unity выросла на 32% до 18,1 миллиарда долларов.
Этот факт не так важен сам по себе, но подобная оценка активов Unity делает ее более состоятельной компанией, чем Epic Games. Хотя у последней есть не только более мощный Unreal Engine, но и суперпопулярный Fortnite. Не так давно Epic провела несколько раундов финансирования, на которых компанию ценили в 17,3 миллиарда долларов.
Несмотря на успешное размещение акций, Unity остается убыточной. За 2018 год убытки составили $131,6 млн, за 2019-й — $163,2 млн, за 2020-й — $54,1 млн. Несмотря на это, инвесторы верят в потенциал производителя самого популярного движка: по оценкам самой Unity, на нем работает примерно половина всех мобильных игр, а также ряд крупных проектов, включая Fall Guys: Ultimate Knockout и Ori and the Will of the Wisps.
AssetBundle
Читайте также: