В своем докладе Скотт Пултц рассказал об опыте портирования Vegas World с ActionScript 3 на Haxe.
Видео-версия доклада также доступна на официальном сайте haxe. Также в блоге Haxe появился краткий пересказ этого доклада на английском языке.
Кодовая база Vegas World довольно большая - более миллиона строк.
Проект с историей - развивается уже более 10 лет.
В нем использовались в основном векторные ассеты, так что были сильно завязаны на проприетарные технологии Adobe.
При этом ни у кого в компании FlowPlay до этого не было опыта работы с Haxe.
Чтобы упростить процесс перехода с чисто Flash на Haxe, было решено сначала выполнить следующие действия:
После этого уже выполнялся процесс портирования AS3-кода на Haxe.
Сначала планировалось получивший Haxe-код компилировать в SWF.
Затем следовало создание html5-версии, т.к. веб - приносит компании FlowPlay бОльшую часть дохода.
Ну а далее - создание мобильных портов игровой платформы.
При портировании кода в теории у вас есть 2 варианта:
Но на практике обычно все обстоит иначе, и улучшить код как-нибудь потом будет значительно сложнее (т.к. проект постоянно развивается, то код будет усложняться, будут появляться все новые “костыли”, также всегда есть и другие задачи, которым может даваться более высокий приоритет выполнения). Поэтому было решено подойти к портированию обстоятельно и выполнить его качественно.
Так как Vegas World уже был запущен на Flash и постоянно происходило обновление AS3-кода, то при портировании было решено придерживаться следующих принципов:
В качестве подходов к процессу портирования рассматривались два варианта:
Первый - это структурное преобразование, при котором используются специализированные инструменты, наподобие AS3HX, которые могут преобразовывать конструкции языка ActionScript в соответствующие им конструкции Haxe. При данном подходе весь код прогоняется сначала через такую утилиту, а затем вносятся оставшиеся правки (т.к. инструмент может не справиться с преобразованием всех языковых конструкций).
Второй подход - использование автоматической замены текста. Процесс портирования в таком случае состоит из: переименования as-файлов в hx, поиске и замене текста согласно заданному списку, и дальнейшем ручном поиске и исправлении ошибок.
Преимуществами первого подхода является автоматизация и получение чистого Haxe-кода.
AS3HX умеет преобразовывать часто встречающиеся конструкции типа циклов и описания свойств у классов.
Однако у данного подхода имеются и недостатки:
1. Теряется форматирование кода. В компании FlowPlay часто пользуются форматированием кода таким образом, чтобы лучше видеть его структуру. На слайде приведен один из небольших фрагментов кода, но на практике можно встретить и гораздо более объемные конструкции, занимающие десятки строк кода. Но на выходе из AS3HX такой код будет преобразован в одну строку - все форматирование теряется;
2. Теряются некоторые комментарии, которые помогают в понимании того или иного фрагмента кода;
3. Не всегда на выходе получается верный код. И это наиболее серьезный недостаток структурного преобразования. Так у AS3HX выявлены такие проблемы как:
Для выявления и исправления таких ошибок может понадобиться сесть и сравнить AS3-код и Haxe-код строчку за строчкой, а для этого в таком большом проекте как Vegas World понадобится огромное количество времени.
У второго подхода, связанного с автозаменой текста, также есть недостатки:
В итоге было принято решение пойти по второму пути.
Для автозамены была составлена таблица соответствия:
А также утилита, которая копирует содержимое as-файлов, сохраняя их в hx-файлы, осуществляет автозамену текста в соответствии с таблицей.
Для того, чтобы ускорить работу программистов, которые затем вручную правили код, использовались шаблоны в IntelliJ Idea.
Достоинствами подхода с автозаменой текста по мнению Скотта является то, что:
Однако многое требуется портировать вручную.
FlowPlay изначально пошли по пути структурного преобразования кода, но когда столкнулись с его недостатками, то решили переключиться на автозамену.
Далее Скотт поделился личным опытом и чувствами, с которыми он столкнулся на этом пути.
Ему пришлось пройти через пять стадий принятия неизбежного: отрицание, гнев, торг, депрессия и принятие :D
Чем быстрее вы смиритесь с изменениями, тем лучше для вас.
В конце концов код, который получится на выходе будет лучше, чем исходный, и это говорит в пользу Haxe.
В начале процесса у вас может сложиться представление, что компилятор троллит вас: в начале у вас будет 1002 ошибки компиляции, через 6 часов работы - 997 ошибок, еще через полдня - 758 ошибок и так далее. В процессе исправления этих ошибок вы будете все лучше и лучше их понимать и привыкать к синтаксису Haxe.
И вот у вас осталась одна ошибка - не хватало какой-то точки с запятой в конце строки, вы ее ставите, пытаетесь скомпилировать, и...
и у вас уже 1343 ошибки. Problems?
Ничего страшного, продолжайте работу. И вот, в какой-то момент программа успешно скомпилируется. И это значит, что вы близки к тому, что игра скоро снова заработает.
У Скотта заняло примерно 2 месяца, чтобы портировать первые 100 тыс. строк кода, и потребовался всего день, чтобы этот код заработал.
И правильным ответом на вопрос: “Сколько времени займет портирование?”, Скотт считает - “Столько, сколько потребуется”.
Далее Скотт сравнил синтаксис и возможности ActionScript и Haxe, перечислил моменты, с которыми он столкнулся в новом для себя языке.
В Haxe описание функций типизировано, что является плюсом по сравнению с AS3.
Также в Haxe есть приватные конструкторы, которых не хватает в AS3.
Но в Haxe поля, помеченные как private, на самом деле ведут себя как protected. Кроме того, в Haxe есть мета @:access, которая позволяет получить доступ к любым полям. Таким образом, в Haxe нельзя полностью сокрыть какие-либо свойства или методы, что, по мнению автора, является недостатком, провоцирующим плохие практики программирования.
В Haxe нет ключевого слова internal, но в качестве более мощной альтернативы предлагается использовать мету @:allow, с помощью которой можно явно указать какие методы, классы, или даже пакеты будут иметь доступ к помеченному метой полю.
Синтаксис циклов также сильно отличается, из-за чего потребовалось много времени на их портирование.
Плюсами циклов в Haxe по сравнению с AS3 являются:
Главным минусом является то, что в for-циклах счетчик может только увеличиваться на единицу, и если нужен другой шаг итерации, то приходится использовать цикл while.
Try/catch в принципе такой же, как и в AS3, только отлавливать нужно не Error, а любые типы.
В Haxe нет ключевого слова finally, поэтому необходимо перерабатывать такой код.
Массивы в Haxe типизированы, что также является плюсом по сравнению с AS3.
Мапы типизированы, плюс для проверки существования записи по ключу есть метод exists(), однако в качестве ключей нельзя использовать Dynamic или Float, что также потребовало от программистов внести соответствующие изменения в портируемый код.
Синтаксис явного приведения типов очень сильно отличается, поэтому с ним плохо справляется AS3HX, который может спутать попытку приведения с вызовом метода.
Также в Haxe нет ключевого слова “as”.
В начале процесса портирования везде использовалось небезопасное приведение (unsafe cast), но т.к. его поведение отличается от безопасного, и использовать его следует только тогда, когда точно знаешь, что тип подходит, то от него отказались.
В Haxe нет автоматического приведения типов как в AS3, что в принципе можно считать плюсом, т.к. повышает типизацию кода и нужно явно приводить типы.
Большие трудности вызвали отличия в описании свойств объектов, поэтому для программистов была составлена шпаргалка с описанием ситуаций, для которых нужно применять ту или иную форму.
В Haxe в switch/case не используется ключевое слово break, поэтому при портировании кода, в котором switch/case находится внутри цикла, нужно быть внимательным, т.к. компилятор пропустит такой код с break, но работать он будет неверно - break будет прерывать цикл тогда, когда вы этого не ожидаете.
Также в switch/case выражениях не поддерживаются объекты, поэтому для них используется if/else.
Разбор XML-документов имеет совершенно другой синтаксис:
Также как и JSON:
Для разбора JSON-файлов можно использовать как Dynamic, так и typedef с описание структуры JSON, тогда у вас появляется типизация, позволяющая избежать ошибок, связанных с ее отсутствием.
В Haxe нет ключевого слова const, взамен ему рекомендуется использовать или inline переменные (для простых типов), или свойства (default, never).
Также ожидается, что в Haxe 4 появится ключевое слово final, которое также можно будет использовать для замены const.
В if’ах нет автоматического приведения чисел, строк и объектов к булевым значениям, что требует от программиста явно указать условие, которое он имеет в виду. Это, по мнению автора, также является плюсом.
Далее Скотт остановился на особенностях сред исполнения.
Хорошей практикой является инициализация переменных при их объявлении, т.к. на одних платформах им могут автоматически присваиваться значения по-умолчанию (на flash int присваивается нулевое значение), а на других - нет (на html5 у int’а значением будет undefined), что может явиться причиной множества ошибок.
Порт AS3-кода на Haxe для компиляции в swf - наиболее простая задача, т.к. портированный код скорее всего (если при портировании не было ошибок) будет работать также. При этом и в IntelliJ Idea и в FlashDevelop доступен отладчик.
Но при портирования кода для работы на других платформах у вас могут появиться проблемы связанные с особенностями реализации API для них, при этом flash/html5 менее строги к типизации вашего кода, и программа может относительно легко запуститься на этих платформах, однако на статических платформах (C++) та же программа может просто сразу же упасть.
Поэтому в случае, когда у вас уже есть рабочая кодовая база на flash, то лучше начинать портирование именно на Haxe именно под flash. Но если вы начинаете проект с нуля, то, по мнению Скотта, лучше начинать со статических платформ, которые предъявляют более строгие требования, таким образом у вас получится более чистый и стабильный код, который будет проще портировать под html5.
Поддержка шрифтов в OpenFL работает вполне достойно, однако отрисовка текста все же разнится от платформы к платформе.
Работа со звуками также имеет свои особенности:
В случае с Vegas World реализация Display List’а в OpenFL была не так важна, т.к. игра использует Starling, для которого также есть порт, поддерживаемый в актуальном состоянии.
Однако возник ряд проблем при работе с текстурами - разные платформы поддерживают разные форматы сжатых текстур, поэтому нельзя просто взять ATF-текстуру из flash/AIR проекта и использовать ее везде.
Формат хранения пользовательских данных отличается, поэтому при переходе с Flash на html5 был написан конвертер этих данных, иначе бы пользователи, игравшие на flash-клиенте потеряли бы все свои данные при переходе на html5.
Для работы на разных платформах также использовались и специфические для них средства:
Пройдя все стадии портирования, FlowPlay наконец запустили html5-клиент для Vegas World. При этом пользователи практически не заметили этого изменения, главным плюсом для пользователей стало, что теперь браузер не блокирует его.
В ближайшее время планируется запуск мобильного приложения, которое хорошо оптимизировано и показывает стабильные 60 fps.