Макросы Haxe. Автоматическое встраивание ресурсов (assets embedding) 3.
Запись от Dima_DPE размещена 22.05.2013 в 02:17
В прошлый раз я не смог показать встраивание шрифтов из ttf файлов. Скажу честно, меня это сильно расстроило, и я провел небольшое расследование и нашел рабочий метод. Поэтому и появилась третья статья про встраивание ресурсов.
Но первое, что мы сделаем, будет автосборка. Заменим наше прошлое определение класс Assets:
на более короткую и менее понятную:
Класс Assets остался, но только теперь он реализует некий интерфейс IAssets, да еще и параметр какой-то непонятный, переданный в роли дженерика, равный нашей папке из прошлого примера. А все дело в очередной магической мета дате - @:autoBuild объявленной в интерфейсе IAssets:
package deep.macro;
Не знаю, что вы на этот раз воскликните. Но да, это почти тотже @:build, но без параметров в которых был бы указан путь к папке с ассетами. Значит, что у нас вышло. У нас есть интерфейс с указанным макросом в автобилде и класс, который реализует этот интерфейс. Все это заставит компилятор выполнить наш макрос по разу для каждого класса, реализующего наш интерфейс. В роли интерфейса может выступить и класс. В документации сказано, что autoBuild добавит всем наследующим классам @:build мета тег с аналогичным содержимым.
Польза от @:autoBuild понятная - более короткая запись наследующих классов и автоматическое добавление мета тега. Минусов не знаю, разве что можно в ветке иерархий забыть и/или не заметить, какие билд методы вызываются.
Но мы забыли один важный момент. А именно путь к папке ассетов. Если раньше мы передавали его в виде строки, то теперь он спрятан в параметре родительского интерфейса. И нам придется найти способ прочитать это значение из макроса. И вот тут начнется самое интересное.
Метод Context.getLocalClass() вернет нам Type (haxe.macro.Type) текущего класса, в нашем случае Assets, а внимательно изучив Type, мы сможем получить и искомую строку. Сказать по правде, Context.getLocalClass() вернет Ref<ClassType>, где Ref<T> это обертка, с одним методом get():T (еще есть toString, но о нем не сейчас). Обертка эта нужна, чтобы не давать доступ сразу к экземпляру класса T, а лишь по требованию - get(), тем самым уменьшает нагрузку и создает экземпляры ClassType только по необходимости. Весь метод получения пути из параметра интерфейса выглядит вот так:
static function getPath(type:ClassType):String { for (i in type.interfaces) { if (i.t.toString() == "deep.macro.IAssets") { switch (i.params[0]) { case TInst(t, _) : var ct:ClassType = t.get(); switch (ct.kind) { case KExpr( { expr:EConst(CString(s)) } ): return s; case _: throw "assert"; } case _: throw "assert"; } } } throw "assert"; }
Кому сложно, сделает просто trace и сам все увидит. Если бы не Ref в TInst, можно было бы это записать в один case (спасибо крутому pattern matching из Haxe 3), но пришлось сделать в два switch-а. Сначала получив Ref<ClassType>, а потом уже распарсив весь ClassType в один case - case KExpr( { expr:EConst(CString(s)) } ). В последней s и будет храниться наша строка “../assets”. Остальные случаи для нас ошибочны, поэтому я вставил там “подробный” throw “assert”, как обычно и делает Николас в таких местах.
Согласен, было проще, но теперь вы знаете, чего можно добиться простым методом Context.getLocalClass(), при том, что он всегда возвращает именно тот тип, в котором вызван макрос. А вот и пример использования метода getPath:
И я снова считаю, что получилось отлично. Да, немного громоздко, зато универсально. Если вы запустите проект сейчас, то все заработает как и раньше, главное, убрать у функций embed обязательный параметр path.
Ну, и напоследок вернемся к шрифтам. Здесь нужно сделать небольшое, но важное отступление.
Раньше мы встраивали ассеты с помощью хака, когда в тело меты вписывался весь контент ассета после префикса “data:”. Так вот, для шрифтов он не работает. Поэтому было решено отказаться от трюка, и во все мета даты записывать только путь к файлу, это позволит унифицировать код для шрифтов, битмап и звуков, да и сам отказ от хака только плюс.
В Haxe 3 появился удобный метод встраивания шрифтов для flash проектов. Точнее удобная мета дата:
Если с первым параметром все понятно, это обычный путь к файлу, то вот второй параметр интереснее, он вроде как не обязателен, но вряд ли кто-то решится встраивать весь шрифт целиком. А для остальных случаев нужно передать строку типа "a-z0-9", которая укажет, какие именно символы встраивать во флешку.
Для небольшого упрощения, предположим, что для всех шрифтов вам нужен один набор символов. И сделаем макро метод, для задания этого набора:
static var fontsRange:String = "a-zA-Z0-9.,;:'\"`@#$%^&*()[]{}"; macro static function setFontsRange(range:String) { fontsRange = range; return macro null; }
Все, что нам осталось - это собрать мета параметры, т.к. большинство утилитарных методов было адаптированны под AFont еще в коде для предыдущей статьи. Возьмем то, что было в прошлый раз, точнее почти то же, но без “data:”, а просто путь к файлу. И добавим fontsRange если у нас шрифт и если fontsRange задан:
var metaParams = [ macro $v{file} ]; if (type == AFont && fontsRange != null) metaParams.push( macro $v{fontsRange} );
Обратите внимание, что в PT Sans остался 161 символ, т.к. мы взяли его из другого swf. А вот Arial импортировали из arial.ttf 86 символов. Важно: нужно бы импортировать и символ пробела, иначе пробелов в тексте вам не видать. Ну а теперь выполнив:
мы увидим [Arial, regular]. Именно то, чего мы и ожидали.
Ура! Мы это сделали. Теперь встраиваются и шрифты, и картинки, и звуки, и текстовые файлы. Правда, во время тестов обнаружилась одна проблема - не удается указать интервалы символов отличных от латинских, но это, видимо, ограничение neko работы с utf-8 строками и если кто-то вкурсе как это обойти, то подскажите или сделайте pull request на гитхабе.
На этом я снова остановлюсь, и на этот раз точно. Я убедил себя, что рекурсивная обработка нам ни к чему, так же как встраивание бинарных файлов. Сегодня вы научились работать с новым мета тегом autoBuild, узнали кое-что новое о haxe.macro.Type и его содержимом. И главное, что теперь работает встраивание шрифтов из ttf файлов.
Исходный код всего урока лежит на гитхабе.
Цитата:
Отдельная благодарность Александру Хохлову и Александру Кузьменко за помощью в написании статьи.
Всего комментариев 1
Комментарии
Последние записи от Dima_DPE