понедельник, 19 декабря 2011 г.

Создание порта движка для создания двухмерных игр Flixel на Haxe

1. Что такое Haxe?

Haxe – это объектно-ориентированный язык для кроссплаторменной разработки, в настоящее время в качестве целевых платформ для него выступают flash, javascript, php, c++ (в начале 2012 года ожидаются c# и java). Если Вы раньше писали программы на ActionScript 3, то синтаксис Haxe покажется Вам знакомым (еще один немаловажный плюс). При этом для haxe существует множество различных библиотек, существенно облегчающих различные задачи. Одной из таких библиотек является NME (Neko Media Engine), позволяющая пользоваться flash api при разработке графических приложений для Windows, Mac, Linux, Android, iOS, WebOS, HTML5 и, конечно, flash (куда же без него), это означает, что Вы пишете один код (ну, почти что один), и он работает на всех перечисленных платформах. Кроме того, программы под Windows, Mac, Linux, Android, iOS и WebOS используют аппаратное ускорение графики (через OpenGL, OpenGL ES), что на данный момент дает значительное преимущество в производительности перед флэшом (аппаратное ускорение в котором есть на персоналках, но на мобильных платформах его пока только обещают).

2. Что такое Flixel?

Flixel – это, как ясно из заголовка статьи, движок для 2d-игр, написанный на AS3. Он очень прост для изучения и имеет множество плюшек (далее вольный перевод с официального сайта):
- отображение на экране тысяч объектов;
- объединение объектов в группы (например, для обработки столкновений между группами);
- встроенная простая система столкновений между объектами;
- системы частиц;
- создание уровней с помощью tilemap’ов (для этого существует ряд редакторов, лучшим считается dame-editor);
- дополнительные классы для отображения текста, работы с сохранением игр, скроллинга игрового мира;
- классы для работы с клавиатурой и мышью;
- утилиты для работы с цветом и геометрическими преобразованиями.
В число «продвинутых» особенностей движка входят:
- возможность записи и воспроизведения игрового процесса;
- встроенный визуальный отладчик;
- система камер, позволяющая создавать игры в режиме split-screen (и не только);
- функция поиска пути в плиточном мире;
- простая система многократного использования объектов (object recycling).

3. Flixel + Haxe = HaxeFlixel

В качестве эксперимента (и более глубокого понимания языка, а также основ геймдева) решил я портировать Flixel на Haxe. Было это в середине лета (2011). К тому времени уже существовал ряд таких портов, единственным их минусом было «моральное устаревание» (просто это порты более старых версий). В качестве основы был взят этот порт, т.к. он считался наиболее завершенным и у него было наибольшее число «наблюдателей». Спасибо автору, т.к. без его работы моего порта могло бы и не быть. И так, в середине июля, пошел процесс: в одной вкладке FlashDevelop’а я открывал файл на as3 и переводил его на haxe, а во второй – его старый порт, с которым сверялся в трудных для понимания местах (т.к. язык знал плохо). Примерно за 2 недели все классы были переведены, потом я на две недели уехал в «отпуск». А после возвращения начались попытки компиляции простеньких примеров – сразу же посыпался «мильон» ошибок, большая часть из которых решалась крайне просто – путем приведения типов переменных к нужному виду. Дело в том, что в Haxe более строгая типизация, чем в AS3, поэтому, если некоторый метод ожидает в качестве аргумента получить целочисленное значение (Int), а Вы передается ему число с плавающей точкой (Float – местный аналог Number), то это вызовет ошибку компиляции. AS3 в этом отношении менее строг и сам отбросит дробную часть. На данном этапе больше всего мне запомнился момент, когда после очередной попытки запуска проекта вылетело где-то 250 ошибок, а после исправления пары из них их стало за 300 (рифмовать не надо :)). К середине сентября простые примеры уже начали запускаться и начался отлов ошибок портирования, чистка кода.
Началом следующего этапа в создании порта стала новость о выходе новой версии библиотеки NME 3.0 (начало октября), которая обещала более простой процесс кроссплаторменной разработки и множество улучшений в работе. Почитав на официальном форуме в списке рассылки о преимуществах новой версии этой библиотеки, решил интегрировать их, чтобы была возможность писать игрушки под мобилы (так, ради интереса, смартфона-то у меня и нет). Это было несложно: те места, которые не хотели компилироваться на c++, я просто комментировал так, чтобы они исполнялись только на flash-платформе. Тогда из порта пришлось выбросить сохранения игр. Где-то за неделю я управился и оставил на форуме haxenme.org сообщение о своем порте. Ответы не порадовали: для меня оказалось новостью, что операции с битмапами (а они являются основой системы рендеринга flixel) выполняются на cpu (а он, на телефонах слабоват) и простые демки дико тормозили (ну вот такой я нуб-самоучка). Кроме опроса на форуме, проводились тесты и на компьютере, они тоже дали неутешительный результат – программы, скомпилированные под с++, работали даже медленнее, чем во flash-player’e. После этого начались поиски наиболее оптимальных методик для работы с графикой. Слава Богу, что эту работу до меня уже сделали члены haxe-сообщества: для быстрой обработки графики предлагалось использовать операции drawTriangles и drawTiles. С первой Вы, скорее всего, знакомы, т.к. она присутствует во флэше (для тех, кто не знает: с помощью этой команды можно нарисовать треугольник, используя в качестве «текстуры» любую битмапдату). Вторая операция – нововведение в NME, она аналогична drawTriangles, но отрисовывает уже прямоугольник (пока что работает только в c++). Первоначальная реализация данного метода была довольно негибкой: можно было задавать только положение такого прямоугольника, поэтому я решил использовать drawTriangles и посвятил пару недель экспериментированию. Но результаты меня не устраивали, ведь drawTriangles не позволяет задавать цветовую трансформацию отрисовываемой текстуры, а я очень этого хотел (иначе пришлось бы сделать еще одно «обрезание» моему порту). И тут вышел первый релиз-кандидат NME 3.1, в котором появилась возможность кроссплатформенной работы с LocalSharedObject (т.е. вернулась возможность сохранять игры на всех поддерживаемых платформах), улучшенная работа с ассетами (шрифтами, графическими, звуковыми и текстовыми файлами), а также (барабанная дробь) расширение функционала метода drawTiles – теперь он позволяет задавать тайлам масштаб, поворот, прозрачность и цветовую трансформацию. Радости моей не было предела, начался новый этап экспериментирования с этим методом. С нюансами его работы разобраться удалось не сразу. Оказалось, что работать с ним нужно аккуратно, т.к. если пытаться использовать одну и ту же битмапдату в качестве «текстуры» для двух разных tileSheet’ов (объектов для отрисовки тайлов), то «волшебным» образом пропадали прозрачность и цветовые трансформации. Но оно и к лучшему, т.к. это заставило более внимательно относиться к ресурсам, не создавая «дублирующих» объектов. Таким образом, я пришел к идее класса-менеджера, отвечающего за работу всех tileSheet’ов – он является центром всей новой системы рендеринга. Если в оригинальном Flixel’е каждый объект непосредственно рисуется на битмапдате камеры, то в новой системе каждый объект передает в менеджер свои данные (положение, поворот, масштаб и т.д.), и только после того, как все объекты передадут свои данные, идет отрисовка всех тайлов. С этой особенностью связана еще одна проблема – проблема сортировки глубины отрисовки объектов. Если в оригинальном Flixel’е порядок отрисовки можно изменить отсортировав массив самих объектов (отрисовка ведется в порядке их расположения в таком массиве), то в новой системе объекты отрисовываются пакетно, т.е. все объекты, имеющие в качестве графического представления одну и ту же битмапдату, отрисовываются за одну операцию. Эту проблему удалось решить частично за счет:
- возможности отдельной сортировки объектов, делящих между собой одну и ту же битмапдату (т.е. уже имеющимися средствами flixel);
- а также возможности сортировки самих tileSheet’ов в менеджере (для этого были добавлены соответствующие методы для получения текущей глубины отрисовки tileSheet’а и ее изменения).
Разработав основу новой системы, я провел несколько тестов и увидел значительный прирост в производительности, т.к. теперь операции отрисовки в с++ программах стали осуществляться с помощью gpu. Убедившись, что теперь можно показать предварительный результат работы, я снова написал на форум; отзывы показали, что работа не была потрачена впустую (это было в начале декабря). Эти отзывы придали мне новых сил, и я с удвоенным рвением продолжил доработку системы рендеринга, попутно заборов еще парочку багов.
Теперь немного о грустном. К сожалению, в NME еще не все так гладко (хотя в последнее время библиотека очень сильно преобразилась в лучшую сторону), например, есть некоторые проблемы с клавиатурой, когда нажатия на разные клавиши выдают одинаковые значения кодов (keyCode), поэтому пока что неправильно работают клавиши цифрового блока, а также F1-F12. Также для звука на с++ приходиться конвертировать файлы в wav-формат (а это дополнительный объем), если же использовать mp3, то одновременно получается воспроизводить только один звук. Кроме того, под c++ реализован еще не весь flash api, и мне пришлось писать свою упрощенную реализацию метода hitTest для битмапдаты. Некоторые методы класса BitmapData работают не так, как на флэше, из-за чего возникают артефакты – здесь тоже пришлось писать свою реализацию (для зеркального отражения битмапдаты).
Но, как я уже сказал раньше, библиотека постоянно улучшается, и я надеюсь, что эти проблемы скоро уйдут в небытие.
Ах да, чуть не забыл дать ссылку на репозиторий проекта: https://github.com/Beeblerox/HaxeFlixel
А также скомпилированная на c++ демка (под Windows): https://github.com/downloads/Beeblerox/HaxeFlixel/ModeDemo20122011.zip

1 комментарий:

  1. Не бросай этого дела :)

    //Можешь выключить ввод каптчи для комменатриев? Ой как бесит, а боты здесь редко водятся. Чтобы ее выключить, надо включить старый режим настроек блога, и где-то там есть галочка которая за нее отвечает. Если боты появятся, просто опять включишь.

    ОтветитьУдалить