из Flash на HaXe, из HaXe во Flash при участии NME
Сейчас я и моя команда занимаемся разработкой своей игры, в которой мы используем DragonBones + Starling + CitrusEngine + Box2D + ...
В какой-то момент получилось так, что команде программистов стало не с чем работать, т.е. арт стал настолько быстро подключаться к игре, что команда художников\аниматоров просто перестала обеспечивать нас нужными ресурсами для работы, и тут появилось время...
Дабы его не терять на просиживание в соц. сетях и т.п., было решено портировать, то что было готово на HaXe, во-первых было интересно сравнить скорость работы, во-вторых разницу в языках, в-третьих HaXe обещал кроскомпиляцию...
Начали.
- Очень порадовала работа с терминала, не надо качать swc, следить за версиями, и т.п., haxelib install lib_name, после чего остается подключить библиотеку, указав ее имя в настройках проекта.
- #define круто и удобно, в отличии от Flash'ового CONFIG::, имеют огромную свободу в использовании и, иногда, упрощают жизнь, например:
//inline будет использован только в релизе private #if !debug inline #end function addTextureAtlas(texAtlas:ITextureAtlas, name:String)... //в зависимости от платформы определяем "синоним" для сущности, этот пример подробнее описан ниже typedef DisplayBridge = #if flash11 dragonbones.display.StarlingDisplayBridge #else dragonbones.display.NativeDisplayBridge #end;
- typedef - отличная замена интерфейсам в определенной ситуации. Так получилось, что библиотеки DragonBones не оказалось под HaXe, пришлось портировать... Автор писавший ее, в некоторых ситуациях не смог создать "вменяемых" базовых классов или интерфейсов, пример(я оставил минимальный код, для компактности):
package dragonBones.display { //... public interface IDisplayBridge { //... function get display():Object; function set display(value:Object):void; //... } } //в этом классе необходим flash.display.DisplayObject public class NativeDisplayBridge implements IDisplayBridge{ //... public function get display():Object { return _display; } public function set display(value:Object):void{ ... } //... } //в этом классе необходим starling.display.DisplayObject public class StarlingDisplayBridge implements IDisplayBridge{ //... public function get display():Object { return _display; } public function set display(value:Object):void{ ... } //... }
- inline, но необходимо знать об ограничениях, небольшой выигрыш в скорости они дают.
- приватные конструкторы, сокращение кол-ва кода на акцессорах для read|write only, по-большей части привычный синтаксис.
Может быть, какие-то “плюшки” я не упомянул(они есть, боюсь не уместиться), из-за того что все рабочую неделю мне пришлось оптимизировать, изучать, тестировать, копаться в исходниках, читать багтрекеры, спрашивать более опытных коллег...
- нет is, есть Std.is, метод создает анонимные функции после компиляции, что влечет за собой утечки памяти и постоянный вызов GC(Std.int, Std.string туда же).
- нет for, есть forin, есть Lambda для обхода коллекций, боже упаси вас использовать Lambda, оно конечно красиво, сравните:
но утечки памяти и вызовы GC на наших тестах были просто нереальны, за пару-тройку минут память росла с 40мб до 400. - для массивов нет indexOf, есть Lamdba.indexOf, если сможете обойтись, вам повезло(для удаления, есть Array.remove).
- постоянная перезапись в Array тянет GC, используйте Vector(only Flash), но у него нет метода remove.
- нам пришлось написать свою реализацию Math.sin, Math.cos(UPD1:проблема не в HaXe).
- не используйте Math.isNaN(a), альтернатива (a != a), спасибо @Sync и @deep.
- если во Flash в Dictionary, можно использовать строки в качестве ключей, то в HaXe для ключей-объектов ObjectHash, для ключей-строк Hash, для ключей-интов IntHash, если же поступить иначе, проблемы рано или поздно возникнут(я потратил час на понимаем почему могу записать по стринг-ключу в ObjectHash, а при чтении получаю Null).
- кешируйте Array::length, я бы не рекомендовал вам его юзать в forin, Scout трубил о постоянном обращении к методу.
- при кросскомпиляции, необходимо досконально изучать справку, например для ByteArray указание длины для Flash можно использовать ByteArray::length, для CPP и Neko ByteArray::setLength, проблему с Loader::loadBytes для Neko мы, пока, так и не смогли решить(для всего работает, для Neko "падает" без объяснений).
- namespace так и не появились, приходится использовать Reflect;
- поддержка e4x нереально ужасна.
- битовые операции с флотами не пройдут, придется кастовать к Int;
- forin тормознее while.
- в if'aх допускается только Bool(запись if(object) не пройдет).
- любая красивая "плюшка" типа:
развернется в анонимную функцию; - не все нативные библиотеки будут вести себя хорошо для Flash.
Итог для начала цикла статей: в целом, моей команде HaXe нравится, однозначно сказать останемся мы на нем или нет, я пока не могу. Последующие наблюдения постараюсь описывать и выкладывать, ну и надеюсь, что данный материал будет интересен.
UPD1:
ссылки, которые пригодятся
the haxe magic
haxe language reference
ps
прошу простить меня за кривость изложения, поправляйте, буду редактировать.
ps2
спасибо @Котяре, @DeMoney, @Алексей Мельников, @Sync, @deep, и всем остальным кто мне помогает в работе и публикации накопившегося материала.
Всего комментариев 17
Комментарии
13.04.2013 12:28 | |
По первой пятерке:
2. очень плохой пример дефейна, для отключения инлайна есть --no-inline 3. почему не заюзать дженерик IDisplayBridge<T : (nme.display.DisplayObject, starling.display.DisplayObject)> ? Дальше: 1. Для флеша есть untyped __is__(a, ClassRef);. Std.int == untyped __int__(i); Std.string действетельно оверхед, но там разворачиваются динамические объекты, что очень удобно для дебага 3. http://try.haxe.org/#14569 или даже http://try.haxe.org/#76f60 5. ну это явно не изза Haxe, там юзается нативный Math 7. В haxe 3 есть удобная обертка, класс Map, не надо помнить какой хеш куда, мап сам решает. 10. В haxe 3 есть мета теги @:access и @:allow 12. операции инта с уинтов тоже не пройдут. 13. форин разворачивается в while, тормознее он может быть только потому, что while ты как то оптимизировал, заранее запомнив arr.length и т.д. 15. на самом деле так в js таргете, для флеша я не декомпилировал и не проверял, просто подозрение. 16. вот тут вообще не понял. что мне не удалось норм подключить в haxe было 2 библиотеки с алхимией, вместе они почему то давали ужастно низкий фпс, хотя отдельно просто летали. Чистые ас3 либы собраные в swc отлично подключаются, знаю точно работают (away3d, starling) |
|
Обновил(-а) Dima_DPE 13.04.2013 в 14:58
|
13.04.2013 12:59 | |
На самом деле все очень и очень круто! Мелкие прописные истины, которые те кто уже использует haxe не замечает, а новенькие явно сталкиваются с ними.
Но не нашел в статье ссылок на такие нужные статьи как эта http://haxe.org/doc/advanced/magic и тем более эта http://haxe.org/ref |
13.04.2013 13:07 | |
13.04.2013 13:53 | |
Спс за комментарии, добавил ссылки
Цитата:
13. форин разворачивается в while, тормознее он может быть только потому, что while ты как то оптимизировал, заранее запомнив arr.length и т.д.
по поводу haxe 3 ты прав, но еще не переехали. |
|
Обновил(-а) СлаваRa 13.04.2013 в 14:09
|
13.04.2013 14:09 | |
Цитата:
мы проверяли на простом прогоне от i in 0...100000
Кстати, обрати внимание, js таргет тут быстрее 0_о |
|
Обновил(-а) Dima_DPE 13.04.2013 в 14:21
|
13.04.2013 16:03 | |
Приветствую.
Вот этот простой тест наглядно демонстрирует, что никаких утечек нет. Проблема скорее всего в вашем коде. В этом тесте используются Lambda.iter(), Array.length и Std.is(). Память стабильно держится 21-25K (Ubuntu, Chrome, Flash 11.7) Насколько я знаю, вызов замыканий во флеше - дорогое удовольствие, так что нет ничего удивительного в том, что Lambda.iter() работает медленнее for(). Далее идём сюда и видим, что у Array.length нет ни геттера, ни сеттера, а это значит, что обращение к этому свойству должно быть ничем не дороже обращения к локальной переменной. Хотя, тут я мог что-то упустить, не уверен. Потом идём сюда и видим, что Std.is() вызывает untyped flash.Boot.__instanceof(v,t); Который, как следует отсюда вызывает untyped __is__(v,t), который и есть обычный экшнскриптовый "is". Оверхед, конечно, есть из-за двух вызовов статических методов и одного if, но я даже не представляю, насколько интенсивно нужно использовать Std.is(), чтобы его оверхед стал значимым. Зато мы имеем полностью кросплатформенный метод. Однако если вы всё-таки умудрились написать приложение, которое упирается в Std.is(), то использование вот такого метода вас спасёт (абсолютно нулевой оверхед во флеше): static public inline function is (v:Dynamic,t:Dynamic) : Bool { #if flash return untyped __is__(v,t); #else return Std.is(v, t); //или отдельная реализация для каждой платформы #end } Извините за возможную излишнюю эмоциональность, просто никак не мог пройти мимо таких оскорбительных обвинений в адрес любимого языка программирования Я уверен, что утечки памяти в таких очевидных местах были бы сразу замечены и пофиксены разработчиками компилятора. |
13.04.2013 16:13 | |
И ещё к слову о for() и кэшировании Array.length
Попробуйте это: http://try.haxe.org/#1d956 |
13.04.2013 16:36 | |
Я действительно прошу прощения, со мной иногда бывает.
Я понимаю, что вы ничего не хотели оскорблять, наверное, мне надо было как-то обозначить шутливую интонацию в последнем абзаце |
15.04.2013 13:46 | |
По макросам я могу много чего написать, есть опыт, есть примеры, но только если ктото возмется проверить грамматику и орфографию.
|
16.04.2013 00:00 | |
Я готов проверить. 100% правильность не гарантирую, но в школе учился хорошо
|
16.04.2013 00:16 | |
мы тоже поможем
|
26.09.2013 10:25 | |
А как сейчас дела со старлингом в nme?
Интересно бы портировать проект в haxe (под дроид/исо), сравнить производительность. Но отказываться перьев и скворцов не хотелось бы. |
26.09.2013 11:49 | |
есть extern starling под openfl, feathers можно подключить так же, но собирать приложение будет возможно только под flash таргет.
|
Последние записи от СлаваRa
- Тестирование кода. Разворачиваем проект с помощью Gradle. (01.11.2015)
- Тестирование кода. Разворачиваем проект с помощью Apache Ant. (01.11.2015)
- [FlashDevelop] Postfix Code Completion (07.09.2015)
- из Flash на HaXe, из HaXe во Flash при участии NME (13.04.2013)
- Подключение ADOBE ASC 2.0 компилятора к FlashDevelop. Русская инструкция. (11.11.2012)