воскресенье, 13 апреля 2014 г.

Unity3D - Scaleform - Render to Texture (Part 1)


В конце 2012 года в Autodesk Scaleform появилась возможность вынимать картинку из "флэш"-контента и ставить ее на любой трехмерный объект, называется это Render to Texture. Работая в Interactive Lab (Moscow) я провел небольшой эксперимент с этой технологией, результат попробую описать в этой статье.

Autodesk Scaleform это технология растеризации векторной графики с последующей ее визуализацией через ресурсы GPU. По сути дела это аналогия Flash Player (Air) визуализатора, но только с изначально заложенным отображением графики через видео ускоритель. Поэтому Scaleform может использовать .swf файлы векторной графики.
"Scaleform uses a proprietary library to load a Flash swf as a "camera overlay" for your Unity game. All functionality within the SWF is preserved, including actionscript functionality, and treats the Unity game as a "webpage" which can send information up to the SWF via PInvokes that the Scaleform library has wrapped into handy method calls and supported Value objects. You can also send updates down from the SWF via Flash's usual ExternalInterface calls." - Lethil
Adobe Flash Professional это уникальный инструмент для создания высоко качественной анимации и векторной графики который уже много лет служит людям для создания 2D контента. Существует версия Scaleform оптимизованная для работы в игровом движке Unity3D. Скачать Scaleform для Unity можно с сайта Autodesk Gameware бесплатно (для некоммерческих целей). Внутри будет стандартный "пакет" (.unitypackage), который импортируется в Unity. Да, кстати, для успешной работы этой технологии вам понадобится Unity версии новее 4.2. После импорта "пакета" Scaleform, в проекте будет куча не нужных папок и файлов. Для корректной работы Scaleform в Unity нужно всего один .dll файл и вспомогательные скрипты, они находятся в папке Plugins (libgfxunity3d.dll и папка SF). Остальное, впринципе, нам будет не нужно, но я рекомендую сохранить это в отдельную папку, потому что там есть полезные инструменты, к примеру инструменты для удобного и быстрого импорта из Flash Professional (Assets\Scaleform\Resources\Tools).


Начну с создания векторного контента в Adobe Flash Professional. Здесь я сделал простые объекты - пару текстовых полей и один кружок, который мы заставим двигаться с помощью кода. Этому кружку (MovieClip) нужно дать имя-идентификатор, я назвал его просто mc_ball. Сам .fla файл я назвал RTTDisplayContent, потому что он будет представлять отображение одного экрана, соответственно также назван и основной управляющий скрипт RTTDisplayContent.as.


Сам скрипт небольшой и он позволяет общаться "флэш"-контену с Unity через вызов "стороннего интерфейса" - ExternalInterface. Также здесь я использую стандартную Flash систему Tween, которая позволяет анимировать параметры объекта. В кратце опишу функционал.


Сначала мы "слушаем" когда "флэш"-объект будет инициализирован и начнет испускать первые сообщения ENTER_FRAME. На самом деле мы могли использовать событие ADDED_TO_STAGE результат был бы такой же. В методе Initialize первым делом удаляем "прослушку" ненужного нам события, затем, для шарика задаем случайную позицию в третьей четверти сцены и инициализируем "твины", при этом мы учитываем что движение шарика будет равномерным (т.е. задаем "линейный изинг")  и синхронизированным по осям (т.е. движение по X и Y просиходит за одно и тоже время). С помощью ExternalInterface.call(...) уже можно вызвать публичную фукнцию Unity (на объекте который хранит в себе ссылку на данный "флэш-файл"). Об этом мы поговорим позже, но для тестирования работы "флэш"-скрипта эту строчку можно пока закомментировать. Поскольку мы дали имя-идентификатор шарику на сцене, то можем обращатся к нему напрямую, оно является публичной переменной основного класса (mc_ball, префикс mc_ говорит нам о том, что объект взят "флэша", а не создается в "рантайме"). По завершению "твина", вызывается функция Handler_Tween_OnFinish, в которой проверяются два параметра tweenYready и tweenXready для точного определения завершения анимации. Затем конечное положение и цвет шарика заменяются и твин запускается заново. Особая функция Regenerate может быть вызвана из Unity через функционал Scaleform.

Файлы .swf для Scaleform должны располагаться в папке StreamingAssets, поэтому здесь я расположил fla-файлы в той же папке что и swf, хотя в крупном проекте их лучше разделять, и хранить все fla-файлы в отдельной папке. А еще лучше создавать "флэш" контент в формате .xlf, тогда будет проше взаимодействовать с системой контроля версии (к примеру git-ом).

Также я подготовил простой .swf файл для наложения как стандартный Scaleform UI на камере.


В нем есть небольшой функционал который увеличивает счетчик жизней и вызывает метод в Unity - OnMouseClickHandler.



Теперь перейдем в Unity. Мне удобно использовать структуру папок где основные типы данных разделены в соответствующие разделы - _Materials, _Prefabs, _Scenes, _Scripts, _Shaders и т.д. (см. 1).


Я подготовил несколько "шаблонов-префабов" (Prefabs) для удобного использования Render To Texture (см. 2). Все они имеют в своей основе простейщую геометрию четырехугольника (Quad), основными является прикрепленный скрипты. Объект _Logic (см. 3) - это Empty Game Object на который наложен скрипт управляеющий некоторыми действиями в Unity, к примеру, вызов фукции Regenerate во всех созданных "флэш" объектах, о нем будет рассказано ниже, как и о всех RTT-скриптах.

Отдельно нужно сказать про камеру (см. 4), на нее обязательно нужно повесить компонент SFCamera, иначе Scaleform работать не будет. При интеграции в проект вспомогательных скриптов из папки Plugins (SF), у меня возникла одна трудность с определением этого компонента редактором Unity, он не мог прикрепить его как компонент (выдавал ошибку) и даже не определял. Чтобы это исправить я исключил SFCamera из пространства имен Scaleform.


Также для камеры я отключил Init Sound и убрал Use System Font, потому что в этом нет необходимости.

Для этого примера рассмотрим RTTDisplay.cs (см. 5). Этот файл создан на основе рекомендуемого метода от разработчиков системы Render To Texture, по сути дела тоже что в примере. В RTTDisplay нужно указать файл .swf который будет загружаться (Swf Name), и Unity класс который будет ассоциироваться с ним (Movie Class Name), а также необходимо задать размер текстуры Render Texture Width\Height (они должны быть равны). Стоит отдельно сказать про этот самый класс (RTTMovie.cs), именно в нем нужно описать функционал который будет вызываться из "флэша" в Unity и обратно. Он хранит в себе ссылку на объект "флэша" в специальном формате Scaleform - Value:
The Value class provides a wrapper for the native Scaleform Value which can represent a number, string, ActionScript VM object, ActionScript array, functions etc. It provides a set of methods such as Get/SetMember to directly interact with Scaleform Values.


В момент инициализации "флэш" контента, в нем вызывается ExternalInterface.call куда в качестве параметров передается имя вызываемого в Unity метода и ссылка на сам объект "флэша", который и сохраняется в параметре Value: ExternalInterface.call("OnRegisterSWFCallback", this). Также, здесь есть вспомогательный лист-массив для удобного доступа ко всем созданным копиям этого объекта, но об этом чуть позже.

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

Чтобы запустить пример с RTT, в самом простов случае, нам нужно:

  1. Поместить на сцену два объекта MainCamera (со скриптом SFCamera) и "префаб" RTTDisplay;
  2. Задать правильное имя Swf Name и разрешение в RTTDisplay;

Пойдем дальше и сделаем так чтобы можно было создавать несколько копий этого объекта RTTDisplay сразу, а также сделаем возможность вызывать метод из "флэша". Для этого создадим специальный скрипт - Logic.cs, и применим его на Empty Game Object (_Logic).


Мы можем создать несколько объектов RTTDisplay с разным или одинаковым контентом. Я подготовил отдельно 20 .swf файлов (они одинаковые, как пример), путь до которых выставляется для каждой копии RTTDisplay (см. 1). При нажатии на пробел мы вызываем метод Regenerate для каждого созданного RTTDisplay объекта в RTTMovie.InstanceList. Я думаю  скрипт описывает себя лучше чем любые слова. В результате мы получим : 


Способ вызова метода из Unity во "флэш" стандартный для Scaleform и рекомендуется в документации (The Direct Access Public Interface): theMovie.Invoke("Regenerate"). 


Теперь, давайте добавим на камеру "флэш"-меню (стандартный Scaleform UI). Для этого создадим скрипт Menu.cs, который будет этот объект подгружать из .swf файл, а также MenuMovie.cs в котором будет функционал (вызовы функций из\в "флэш"). Также добавил на сцену трехмерный объект - вращающийся куб (SpinningCube), вращение которого мы будем контролировать из загруженного "флэш" меню. 


В последнем обновлении Scaleform добавился вспомогательный скрипт Utils через который можно быстро и удобно подключить SWF: 


В классе MenuMovie.cs определяем те функции которые мы будем вызывать из "флэша", это - OnRegisterSWFCallback и OnMouseClickHandler


В результате получим:
  • меню отдельным объектом на камере (размер которого подгоняется под размер экрана)
  • отдельные трехмерные объекты с наложенными на них текстурами взятыми из .swf файлов 
При клике на Menu (событие из "флэш" объекта) мы можем контроллировать любые объекты внутри Unity и в самом "флэше". А также события из Unity мы можем контроллировать объекты в "флэше".  (см .gif, 17 fps, build from Unity)


В следующей части я покажу как можно отображать несколько "флэш"-анимаций из одного .swf файла на разных объектах с помощью RTTAtlass и RTTSprite.

Весь проект можно скачать на github

Комментариев нет:

Отправить комментарий