Форум Flasher.ru
Ближайшие курсы в Школе RealTime
Список интенсивных курсов: [см.]  
  
Специальные предложения: [см.]  
  
 
Блоги Правила Справка Пользователи Календарь Поиск рулит! Сообщения за день Все разделы прочитаны
 

Вернуться   Форум Flasher.ru > Блоги > Dima_DPE

Оценить эту запись

Макросы Haxe. Автоматическое встраивание ресурсов (assets embedding) 2.

Запись от Dima_DPE размещена 19.05.2013 в 20:13

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

Первым делом, мы попробуем автоматизировать макрос для автодополнения. Автодополнение выполняет все макросы, чтобы получить точные данные о всех полях класса. Но в случае автодополнения часть операций макроса можно опустить. Макрос может сам узнать, запущена сейчас компиляция или просто автодополнение:

Код AS3:
Context.defined("display")
Context.defined принимает один параметр, который может быть передан компилятору при помощи -D define_name или задан самим компилятором автоматически. Например для js сборки Context.defined(“js”) вернет true. Context.defined("display") вернет true, когда выполняется автодополнение.

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

Код AS3:
var display = Context.defined("display");
…
var data = display ? null : File.getContent(file);
Видно, что в случае автодополнения, не будет читаться содержимое файла ассета. Это дало мне примерно 0.1 секунды прироста во времени работы автодополнения в случае 4 ассетов. Время замерял с помощью директивы --times, переданной компилятору. А если замерять отдельно выполнение самого макроса (-D macro_times), то макрос с оптимизацией ускорился в 10 раз, с 0.2 секунды, до 0.02! Выводы делайте сами.

Мы уже сделали немало, но давайте пойдем дальше. А дальше мы немного углубимся в macro reification. Для начала начнем с нового синтаксиса:

Код AS3:
var complexType = macro : flash.display.BitmapData;
Обратите внимание на двоеточие после macro. Теперь complexType хранит в себе один из консрукторов enum-а ComplexType (haxe.macro.Expr), в данном случае TPath(p:TypePath):

Код AS3:
TPath({ name => BitmapData, pack => [flash,display], params => [] })
“Ага!” - скажете вы, - “Да это же почти то же, что мы делали “руками” в методе getKind.” Ну так давайте использовать это. Для начала, правда, сделаем дополнительную функцию для вычисления комплексного типа, которая пригодится дальше и изменим метод getKind:

Код AS3:
// комплексный тип
static function getComplexType(type:AssetType):ComplexType {
	return switch (type) {
		case AImage: macro : flash.display.BitmapData;
		case ASound: macro : flash.media.Sound;
	}
}
Ну а теперь вычислим базовый тип вида TDClass( ?superClass : TypePath, ?interfaces : Array<TypePath>, ?isInterface : Bool ). Обратите внимание, тут снова TypePath, как и у TPath:

Код AS3:
// Базовый тип
static function getKind(type:ComplexType):TypeDefKind {
	return switch (type) {
		case TPath(p): TDClass(p);
		default:
			Context.error("can't find asset type", Context.currentPos());
			null;
	}
}
Надеюсь, в этом методе все понятно. Мы берем комплексный тип, предполагаем, что он TPath, считываем из него TypePath и оборачиваем в TDClass. Все остальные случаи невозможны, т.к. мы точно знаем какие комплексные типы будут использованы, но на всякий случай я добавил вывод ошибки - Context.error, который выведет ошибку так, будто она была сделана на месте вызова макроса в коде. В данном случае ошибка будет не очень понятна и удобна, а вот когда макрос вызывается в коде, то это бывает очень удобно.

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

Поехали дальше. Попробуем заменить:

Код AS3:
{ expr :EConst(CString("data:" + data)), pos :filePos }
на “реифицированный” вариант:

Код AS3:
macro $v{"data:" + data}
Ура, работает! На самом деле странно, т.к. у нас Position самодельный, а в macro нельзя указать position и он берет Context.currentPos(). Но, т.к. position учитывается только при дебаге, а мы вряд ли будем дебажить ассеты, то проблем не будет. А кто не понял, что за $v{} в коде, обратитесь к коду первой статьи и дополнительным методам, о которых я писал во второй статье.

Вернемся теперь к методу getComplexType. Я ведь говорил, что он нам еще пригодится. Улучшим наше автодополнение ещё раз. Кто запускал прошлый пример, тот видел, что старое автодополнение напротив атрибутов Assets писало тип Unknown<0>, теперь же настало время вписать туда более внятный тип:

Код AS3:
var ct = getComplexType(type);
…
kind : FVar(ct, { expr : ENew( { pack : ["assets"], name : getPrefix(type) + name, params : [] }, getArgs(type)), pos : pos } ),
Наблюдательный читатель заметит, что если раньше первый параметр у FVar был null, то теперь он равен комплексному типу. И теперь автодополнение сразу знает тип переменных и подскажет их нам:



Самое время отказаться от префиксов в названиях переменных, правда для классов я решил их все таки оставить и сделать все немного красивее:



От префиксов, как видно, я не отказался полностью, а оставил их там, где без них не удалось бы получить валидный идентификатор (snd1, snd2). Можно, конечно, еще проверять на уникальность, но пока мы работаем с одним каталогом, файловая система делает это за нас.

Еще можно заинлайнить утилитарные методы, такие как getMetaName или getComplexType, что еще немного ускорит работу макроса.

Напоследок, я собирался сделать встраивание шрифтов, но не тут то было. Закралась ошибочка, с которой я не смог справится. Но Haxe не был бы Haxe-ом, если бы мы не могли решить задачу иначе.

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

Код AS3:
if (type == ASWF) {
	Compiler.addNativeLib(file);
	continue;
}
Ну, или четыре, если считать скобки. Compiler.addNativeLib - новый для вас метод, который встраивает swf или swc, точно так же, если бы мы написали -swf-lib file. Класс haxe.macro.Compiler вообще очень полезный и интересный, стоит заглянуть в него и почитать документацию по нему.

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

Код AS3:
if (type == AText) {
	res.push( {
		name : varName(type, p.file),
		access : [APublic, AStatic],
		doc : 'file: "$file"',
		kind : FVar(ct, macro $v{data} ),
		pos : pos,
	});
	continue;
}
Я практически скопировал код встраивания изображений или звуков, за исключением того, что содержимое переменной заданно иначе - macro $v{data}. Да и поле doc заполнил, на самом деле я сделал это везде, что добавило еще больше информации в автодополнение:



Теперь точно все. Сегодня мы больше всего времени уделили автодополнению и это понятно. Макросы были бы обычными шаблонами, если бы не столь тесная работа с компилятором. Еще мы оптимизировали работу макроса и добавили встраивание текстовых и swf(swc) файлов. Все это делает из нашего пробного макроса вполне юзабельный инструмент для работы над flash проектами.

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

Цитата:
Отдельная благодарность Александру Хохлову и Александру Кузьменко за помощью в написании статьи.
Всего комментариев 2

Комментарии

Старый 20.05.2013 22:17 Котяра вне форума
Котяра
 
Аватар для Котяра
Магия.
Старый 22.05.2013 12:21 Dima_DPE вне форума
Dima_DPE
Магия - скажите вы. Ловкость рук и никакого мошеничества - скажу я. При должной доработке все этоже можно сделать и для js и для всех остальных проектов. NME нечто подобное в общем и делает.
 

 


Часовой пояс GMT +4, время: 21:01.


Copyright © 1999-2008 Flasher.ru. All rights reserved.
Работает на vBulletin®. Copyright ©2000 - 2024, Jelsoft Enterprises Ltd. Перевод: zCarot
Администрация сайта не несёт ответственности за любую предоставленную посетителями информацию. Подробнее см. Правила.