Как открыть ссылку в браузере unity
Порой перед игровыми разработчиками встают нетривиальные задачи. Такой задачей может стать, например, необходимость показать пользователю обычную веб-страницу на игровом движке unity3d, который, несомненно, может похвастаться множеством интегрированных технологий, но HTML в этот список не входит. Сразу скажу, что «Серебряной пули нет»… или пока нет. Это зависит от степени вашего оптимизма.
Прежде всего необходимо определиться с целями для которых вам требуется HTML: оформление текста с помощью базовых тегов, создание пользовательского интерфейса с помощью HTML, оформление справочного раздела (например, внутриигровой энциклопедии), показ статичной веб-страницы или вообще интеграция полноценного веб-браузера. Соответственно, плагины или HTML движки делятся по количеству поддерживаемых тегов, поддержке анимация, технологии реализации (OnGUI или текстуры), интеграция с GUI (OnGUI или NGUI), поддержка CSS, HTML5, JavaScript, технология реализации работы с изображениями и т.п.
Вообще-то, выбор HTML плагинов довольно беден. Я нашёл всего 5 представителей.
Начнём наше знакомство с пожалуй самого неказистого варианта:
1. HTML Engine for NGUI & Unity GUI за 25$
- Пожалуй единственный плагин совместимый не только со стандартным OnGUI, но и с NGUI, причём достаточно новыми версиями.
- Нет демоверсии.
- Для работы с изображениями их сперва необходимо скомпоновать в атлас и указывать путь к этому атласу при вставке изображения.
- Из-за принципа генерации HTML, объём отображаемой страницы существенно ограничен, так что взять обычную веб-страницу, засунуть её в в проект и показать, не получится.
- Не совсем очевидная установка (придётся помучиться, чтобы понять почему при установке вылезает столько ошибок).
2. PowerUI — HTML/CSS за 60$
Сайт разработчиков.
Хороший плагин. Кроме поддержки многих тегов и CSS ещё и хорошо оптимизирован.
Легко интегрирует простые html страницы в проект.
Достаточно совершить простые подготовительные действия:
1. Добавьте новый слой с названием: PowerUI.
2. Убедитесь что остальные камеры не видят этот слой.
3. Создайте новый объект и добавьте к нему скрипт Powerui Manager.
4. Настройте изображения (все картинки должны храниться в папке Resources, в их свойствах тип должен быть Advanced, «Non Power of 2» сменить на none, поставить галку напротив read/write enabled, и убрать галку с generate mipmaps)
Теперь достаточно присвоить вашу веб страницу публичному полю созданного объекта.
-
— не компилируется под iOS, так что халява не пройдёт
- Поддержка CSS, JavaScript.
- Возможность отображения не только в OnGUI, но и в текстуру.
- Хорошая оптимизация, минимум DC.
- Много примеров.
- Триал версия не компилируется под маками.
3. GUI Design HTML CSS за 30$
- Поддержка CSS.
- Хорошая документация.
- Нет демоверсии.
4. HuG:Hugging HTML on Unity за 45$
-
.
- Поддержка CSS, JavaScript.
- Проигрывание музыки по сети.
- Возможность показа как локальных, так и интернет-страниц.
- Поддержка JavaScript достаточно условная.
- Далеко не каждая интернет страница может адекватно отобразиться.
- Проблемы с кросс-платформенностью, то что хорошо работает под Windows в IOS может сильно глючить, впрочем возможно в платной версии это излечимо.
5. U3DXT iOS SDK Pro за 100$
Т.к. этот плагин очень многофункционален, то прилагаю только один скрин сделанный в собственном приложении, демонстрирующий качество веба. Голубая полоска вверху — это часть NGUI интерфейса, а ниже расположен viewport браузера.
- Применимо только для iOS.
- Глюки с разрешением (программа считает что у ios гаджетов есть только два разрешения 320 на 480 и 768 на 1024)
Обзор, конечно, достаточно поверхностный, но надеюсь будет полезен разработчикам, впервые столкнувшимся с подобной задачей. Теперь вам будет понятнее куда копать.
HTML-страница, которая содержит контент Unity Web Player может взаимодействовать с ним и наоборот. Есть два направления взаимодействия:
- Веб-страница вызывает функции внутри контента web-плеера Unity.
- Содержимое Unity веб-плеера вызывает функции на веб-странице.
Каждое из этих направлений взаимодействия будет описано более подробно ниже.
Вызов функций контента веб-плеера Unity с веб-страницы
Объект Unity Web Player имеет функцию, SendMessage() , что можно назвать с веб-страницы, чтобы вызвать функции в контенте веб-плеера Unity. Это функция очень похожа на функцию GameObject.SendMessage в Unity. При вызове с веб-страницы вы передаете имя объекта, имя функции и ещё один аргумент, а SendMessage() будет вызывать данную функцию на указанном игровом объекте.
Чтобы вызывать функции в Unity Web Player с помощью SendMessage() , необходимо сначала получить ссылку на объект веб-плеера Unity. Вы можете использовать функцию GetUnity() , которая по умолчанию генерируется Unity в HTML для получения ссылки на объект. Вот пример функции JavaScript, которая будет выполнять функцию SendMessage() ; в свою очередь SendMessage() будет вызывать в игре на объекте с именем MyObject функцию MyFunction() , передавая строку данных в качестве аргумента:
В игре у Вас на игровом объекте ( GameObject ) с именем MyObject должен быть скрипт, который должен содержать функцию с именем MyFunction :
Примечание: имейте в виду, что если функция не имеет аргументов, то в качестве аргумента необходимо передать пустую строку ("").
Строка, float или целое число должно быть передано при использовании SendMessage() , этот параметр является обязательным. Если вам это не нужно просто предайте нулевое значение или другие значения по умолчанию и игнорируйте его в Unity. Кроме того, указанное имя игрового объекта может быть предоставлено в виде пути. Например, /MyObject/SomeChild где SomeChild должен быть дочерним элементом MyObject , а MyObject должен быть на корневом уровне, т.к. перед его именем стоит ‘/’.
Примечание: u.getUnity() может возвращать значение null, если игра ещё не полностью загружена, так что можно проверить, если это значение не равно null перед использованием SendMessage(). Или ждать полной загрузки вашей игры, прежде чем пытаться взаимодействовать с ней.
Вызов функций веб-страницы из веб-плеера Unity
Для того, чтобы вызвать функцию веб-страницы из содержимого веб-плеера Unity, следует использовать функцию Application.ExternalCall() . С помощью этой функции можно вызывать любую функцию JavaScript, определенную на веб-странице, передавая ей любое количество параметров. Вот пример Unity скрипта, который использует функцию Application.ExternalCall() для вызова функции с именем SayHello() на веб-странице, передавая часть строковых данных в качестве аргумента:
На веб-странице необходимо определить функцию SayHello() , например:
Выполнение произвольного кода браузера из содержимого веб-плеера Unity
Вам даже не придется определять функции на веб-странице, вместо этого вы можете использовать функцию Application.ExternalEval() , которая выполнит произвольный код браузера из веб-плеера.
Thank you for helping us improve the quality of Unity Documentation. Although we cannot accept all submissions, we do read each suggested change from our users and will make updates where applicable.
Submission failed
For some reason your suggested change could not be submitted. Please try again in a few minutes. And thank you for taking the time to help us improve the quality of Unity Documentation.
Declaration
Parameters
Description
Opens the URL specified, subject to the permissions and limitations of your app’s current platform and environment. This is handled in different ways depending on the nature of the URL, and with different security restrictions, depending on the runtime platform.
Note: This method can be used to open more than just web pages; therefore, it has important security implications you must be aware of.
For this reason, the OpenURL command can be unexpectedly powerful. On some platforms it can open local files, run commands, or open connections over any protocol that the platform and security sandbox supports.
The OpenURL method runs with the same permissions as your app itself. For example, if your app is running as a WebGL player in a desktop web browser, it will not be able to access local files on the machine, because the WebGL platform itself runs inside a security sandbox which prevents that. If you are targeting other platforms such as standalone EXE app, your app runs with fewer security restrictions and no security sandbox, so this method is more powerful.
Important: You must be extremely careful that you do not provide a string to this function which could possibly be maliciously crafted or modified by a third party.
On standalone platforms, you should consider this method to have similar security implications as an eval type function, present in many other programming languages.
If your app uses OpenURL to open URL strings which come from a third party, or which are put together using any user-supplied data, the user-supplied data should be considered untrusted and may be used to run arbitrary code under the same permissions of your app itself. You must sanitise the untrusted data and validate that it is the expected input for your application.
WebGL: From version 2019.4.25f1, 2020.3.5f1, 2021.1.2f1, and 2021.2.0a11, Application.OpenURL opens url in a new browser tab. In previous versions, Application.OpenURL opens url in the same browser tab, which terminates the running Unity application.
Android: Due to security changes in Android 7.0 (More information), Application.OpenURL can no longer be used for opening local app files, you need to use FileProvider which allows you to share files with other applications.
iOS: Application.OpenURL cannot be used for opening local files.
ЗЗЫ Если это WebGL, то кто б поделился секретами, как с сием билдом работать, ибо пока у меня даже не получается посмотреть скомпилированную игру, пока не закинешь куда-то в онлайн. И ВебГЛ весит очень много и компилится очень долго, я очень надеюсь что это не вебГЛ в примере, а какой то альтернативный способ. Ибо не разобрался я с этим заморским зверем.
На самом деле, не так уж и много)
Первым делом нужно заставить юнити "обрезать" всё и вся.
Открываем настройки платформы и выставляем значение в Fastest https://gyazo.com/6d67cb9037f82c6dc823ab1c4d912522
После открываем Other Settings, выставляем оптимизацию https://gyazo.com/7fd673abef9baf767658d1ce46c66300
Дальше нужно определить минимальный размер памяти, который требуется для игры. Переходим в раздел Publishing Settings и выставляем этот размер в поле https://gyazo.com/d6efc7c02a1c9fb0310b347f1d0f7e2f
Но стоит учитывать, если будет стоять мало памяти, игра будет "падать" с ошибкой нехватки памяти для игры, если много, то с ошибкой не хватки памяти на устройстве)
По поводу открытия билда. Локально можно посмотреть в лисе, она нормально воспринимает билд. Хром да, только удаленные файлы любит.
Дальше смотрим на содержимое папки с билдом: https://gyazo.com/57b03a710d77bbfdf17552e07c5129d1
Как видим, у нас 5 "файлов".
Index.html - страница с разметкой и настройками канваса
.htaccess - тут описаны правила для браузера, что и откуда брать
TemplateData - картинки используемые в index.html
Release - вот тут находится наша игра
Compressed - а во тут находится наша игра в zip архиве, пригодным для распаковывания в браузере
На веб хостинг нужно переместить все, за исключением папки Release. По правилам описанным в htaccess, браузер должен грузить файлы из папки Compressed, но если у него не сложилось с этим, то он будет пытаться загрузить файлы из Release. Думаю этим будут заниматься древние браузеры, не умеющие хавать архивы. но они в любом случае идут лесом, ибо нужен WebGL.
В итоге билд становится размером ~15 метров, с чем уже можно работать! :)
Успехов.
PS. Так же в настройках можно отключить исключения (билд от этого будет работать немного быстрее) и стоит учитывать, что Unity WebGL не работает в мобильных браузерах
Часто стоишь в пробке или очереди? Эта игра поможет скоротать время 7Bricks - головоломка с цифрами.
По традиции, для начала определимся, что это и зачем нам это надо. Итак, что же такое эти внешние ресурсы. В рамках разработки игр, такими ресурсами может быть все, что требуется для функционирования приложения и не должно храниться в конечном билде проекта. Внешние ресурсы могут находится как на жестком диска компьютера пользователя, так и на внешнем веб-сервере. В общем случае такие ресурсы — это любой файл или набор данных, который мы загружаем в наше, уже запущенное приложение. Если говорить в рамках 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. Если у пользователя свободного места больше, чем размер кэша, то проблем никаких не будет, однако если это не так, то значения могут принимать неверные относительно реального положения дел значения.
Проверка доступа в интернет
Кэширование
Следующей, и одной из самых важных проблем, является кэширование скачиваемых файлов. Для чего же нужно это кэширование:
- Экономия траффика (не скачивать уже скаченные данные)
- Обеспечение работы в отсутствии интернета (можно показать данные из кэша).
Аналогично, получение данных из кэша.
Примечание: почему для загрузки текстур не используется тот же самый 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 придется через интерпретацию строк.
Работа с ресурсами в режиме редактора
Последней проблемой, а точнее вопросом удобства отладки и разработки является работа с загружаемыми ресурсами в режиме редактора, если с обычными файлами проблем нет, то с бандлами не все так просто. Можно, конечно, каждый раз делать их билд, заливать на сервер и запускать приложение в редакторе Unity и смотреть как всё работает, но это даже по описанию звучит как “костыль”. С этим надо что-то делать и для этого нам поможет класс AssetDatabase.
Для того, чтобы унифицировать работу с бандлами я сделал специальную обертку:
Теперь нам необходимо добавить два режима работы с ассетами в зависимости от того в редакторе мы или же в билде. Для билда мы используем обертки над функциями класса AssetBundle, а для редактора используем упомянутый выше класс AssetDatabase.
Примечание: в коде используется класс TaskManager, о нем пойдет речь ниже, если кратко, то это обертка для работы с Coroutine.
Помимо описанного выше, также в ходе разработки полезно смотреть, что именно мы загрузили и что находится сейчас в кэше. С этой целью можно воспользоваться возможностью установки своей папки, которая будет использоваться для кэширования (в эту же папки можно записывать и скачанные текстовые и другие файлы):
Пишем менеджер сетевых запросов или работа с веб-сервером
Выше мы рассмотрели основные аспекты работы с внешними ресурсами в Unity, теперь бы мне хотелось остановиться на реализации API, которая обобщает и унифицирует все выше сказанное. И для начала остановимся на менеджере сетевых запросов.
Примечание: здесь и далее используется обертка над Coroutine в виде класса TaskManager. Об этой обертке я писал в другой статье.
Заведем соответствующий класс:
Статическое поле NetworkType требуется для того, чтобы приложение могло получать сведения о типе интернет-соединения. В принципе это значение можно хранить, где угодно, я решил, что в классе Network ей самое место.
Как видно из кода, способ обработки завершения запроса изменен, по сравнению с кодом в предыдущих разделах. Это сделано с целью отображения прогресса загрузки данных. Также, все посылаемые запросы сохраняются в списке, с тем чтобы, если это необходимо, их можно было отменить.
Аналогичным образом создаются функции для текстуры, аудио, текста, байт-массива.
Теперь необходимо обеспечить отправку данных сервер через команду Post. Часто нужно, что-то передать серверу, и в зависимости от того, что именно, получить ответ. Добавим соответствующие функции.
Теперь добавим публичные методы с помощью, которых мы будем осуществлять загрузку данных, в частности AssetBundle
Аналогично добавляются методы для текстуры, аудио-файла, текста и т.д.
И напоследок добавляем функцию получения размера скачиваемого файла и функцию очистки, для остановки всех созданных запросов.
На этом наш менеджер для работы с сетевыми запроса завершен. По необходимости, каждая подсистема игры, которая требует работы с сервером может создавать свои экземпляры класса.
Пишем менеджер загрузки внешних ресурсов
Помимо описанного выше класса, для полноценной работы с внешними данными, нам нужен отдельный менеджер, который будет не просто скачивать данные, но и уведомлять приложение о начале загрузке, завершении, прогрессе, отсутствии свободного места, а также заниматься вопросами кэширования.
Как видно, в конструкторе задается папка для кэширования в зависимости от того в редакторе мы находимся или нет. Также, мы завели приватное поле для экземпляра класса Network, который мы описали ранее.
Теперь добавим вспомогательные функции для работы с кэшем, а также определения размера скачиваемого файла и проверки свободного места для него. Далее и ниже код приводится на примере работы с AssetBundle, для остальных ресурсов все делается по аналогии.
Итак, что происходит в данной функции:
Аналогично описанному выше методу в менеджере можно/нужно завести и другие функции работы с данными: GetJson, GetTexture, GetText, GetAudio и т.д.
И напоследок необходимо завести метод, который позволит скачивает наборы ресурсов. Данный метод будет полезен, если нам надо на старте приложения, что-то скачать или обновить.
Здесь стоить понимать особенность работы TaskManager, который используется в менеджере сетевых запросов, по умолчанию он работает, выполняя все задачи по очереди. Поэтому загрузка файлов будет происходить соответственно.
Примечание: для тех, кто не любит Coroutine, все можно достаточно легко перевести на async/await, но в данном случае, в статье я решил использовать более понятный для новичков вариант (как мне кажется).
Заключение
В данной статье я постарался как можно более компактно описать работу с внешними ресурсами игровых приложений. Этот подход и код используется в проектах, которые были выпущены и разрабатываются при моем участии. Он достаточно прост и применим в несложных играх, где нет постоянного общения с сервером (ММО и другие сложные f2p игры), однако он сильно облегчает работу, в случае если нам надо скачать дополнительные материалы, языки, осуществить серверную валидацию покупок и другие данные, которые единовременно или не слишком часто используются в приложении.
Читайте также: