Портирование Gish на Android OS: использование библиотек OpenAL и GL4ES

В средних классах школы я любил прогуливать скучные гуманитарные предметы и вместо них проходить самые разные компьютерные игры. Но одному играть было чертовски скучно, поэтому я часто приходил в гости к своему другу. Мы покупали различные вкусности, собирались вместе в его комнате, включали компьютер и погружались в захватывающие игровые миры. Как-то раз, холодным зимним вечером, мой одноклассник показал мне забавную и интересную игру под названием Gish, которую он недавно скачал в Интернете. Это была достаточно своеобразная двумерная аркада, отличительной особенностью которой являлся необычный одноименный персонаж, представляющий собой небольшой жидкий шарик из чёрной смолы. К сожалению, сейчас я не помню, смогли мы тогда пройти эту замечательную игру до конца или нет (она была достаточно хардкорной), но её мрачный сеттинг, чёрный юмор, классная музыка и необыкновенные головоломки, связанные с физикой игрового мира, остались в моей памяти навсегда. В графическом плане Gish был достаточно простым, но благодаря оригинальной рисовке, интересным локациям и классной работе освещения, создавалось впечатление достаточно современной игры. Нам с другом было весело играть в Gish’а по очереди, проходя сложные уровни всё дальше и дальше. Таким образом, эта игра стала одной из моих любимых аркад для PC и отличным платформером для небольшой компании друзей.



Порт игры Gish, запущенный на Android-устройстве Motorola Photon Q.

История разработки инди-игры Gish берёт своё начало в 2003 году. Именно тогда независимая компания Chronic Logic, занимающаяся изданием и разработкой видеоигр, заключила контракт с художником и геймдизайнером Edmund’ом McMillen’ом, который впоследствии станет известным благодаря таким популярным инди-играм, как Super Meat Boy и The Binding of Isaac. Пока Edmund работал над графикой, программисты Alex Austin (под псевдонимом Cryptic Sea) и Josiah Pisciotta занимались написанием игрового движка. Разработка Gish была завершена весной 2004 года и уже в мае Chronic Logic выпустила игру на рынок США, где аркада получила восторженные и положительные отзывы критиков. Благодаря успеху игры, её дистрибуцией в 2004 году занялась такая компания, как Stardock, а в 2007 году Gish был добавлен в библиотеку игр в Steam’е. Кроме того, в 2010 году платформер был включен в первый сборник инди-игр под названием Humble Indie Bundle, который собрал более одного миллиона долларов. В знак признательности поддержавшему их сообществу, Cryptic Sea объявил о том, что исходный код игры Gish будет открыт и выпущен под лицензией GNU GPL v2.0, но ресурсы игры нужно будет приобретать отдельно.

Содержание:

1. Краткий обзор игры Gish
2. Подготовка исходного кода Gish к портированию на Android OS
3. Решение возникших проблем порта игры Gish на Android OS
4. Выбор рендерера: OpenGL ES и OpenGL (GL4ES)
5. Сенсорное управление и лаунчер игры
6. Заключение, полезные ссылки и ресурсы

1. Краткий обзор игры Gish

Как уже было сказано выше, главным героем игры является небольшой шарик смолы по имени Gish, который может изменять своё физическое состояние. Сюжет аркады не слишком замысловат и повествуется в нескольких статичных иллюстрациях перед началом первого уровня игры. Gish и его подружка Brea прогуливались в парке, как вдруг из канализационного люка вылезло мерзкое чудовище, которое утащило девушку под землю. Бесстрашный Gish прыгает за ним в канализацию и отправляется на поиски своей возлюбленной, после чего, собственно, и начинается сама игра, конечной целью которой является спасение несчастной Brea.



Повествование сюжета игры, скриншоты с Motorola Photon Q (превью, увеличение по клику).

Первый уровень платформера выполнен в виде обучающей локации, где игрока познакомят с основными клавишами управления и методами взаимодействия с игровым миром. Gish может становиться тяжёлым и твёрдым, чтобы давить своей массой врагов, ломать преграды и тонуть в воде. Для того, чтобы карабкаться по стенам и потолкам, наш герой может выдвигать специальные шипы на своей оболочке, которые, помимо этого, позволяют ему передвигать и тащить некоторые предметы. Ещё Gish умеет становиться скользким, что помогает ему протискиваться в различные узкие проходы и секретные места, часто встречающиеся на уровнях игры. Все эти умения можно комбинировать, что позволяет игроку с лёгкостью преодолевать некоторые трудные локации.



Обучающий уровень игры, Gish с помощью шипов карабкается по потолку, чтобы добыть янтарь, скриншот с Motorola Photon Q (превью, увеличение по клику).

Кнопка прыжка создаёт некоторый резонанс в теле Gish’а, комбинируя это действие с нажатием клавиш «вниз» и «вверх», можно высвобождать накопленную энергию и тогда главный герой будет довольно высоко подпрыгивать.

Локации платформера наводнены различными головоломками, связанными с физикой самой игры. В некоторых местах нужно найти и положить ящик на кнопку открывающую двери, чтобы пройти уровень дальше. Иногда потребуется подбрасывать камни вверх, чтобы пробить потолок и высвободить тяжёлый груз, который проломит пол и откроет проход в новые локации.



Одна из физических головоломок в игре, Gish приготовился подбросить камень вверх, чтобы пробить потолок и уронить тяжёлый груз вниз, скриншот с Motorola Photon Q (превью, увеличение по клику).

Уровни разбиты на пять различных глав, в каждой из которых локации выполнены в какой-либо определённой стилистике. Gish побывает не только в мрачной и грязной канализации, но и в затопленных шахтах, в подземном Аду, в древнем Египте и даже в заброшенной церкви.



Многообразие локаций в Gish, на последнем изображении первый босс в игре, скриншоты с Motorola Photon Q (превью, увеличение по клику).

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



Переходный уровень из Ада в древний Египет, сочетание сеттинга сразу двух глав, скриншот с Motorola Photon Q (превью, увеличение по клику).

Игра требует хорошей сноровки и к её управлению необходимо привыкать. Например, некоторые места проходить значительно легче, если у Gish’а был сохранён импульс разбега. Я довольно долго не мог научиться высоко прыгать, в игре есть некоторые места, где высокие прыжки необходимы для дальнейшего прохождения. Платформер Gish наполнен множеством секретов, скрытыми уровнями и различными пасхалками, что, несомненно, будет приятным сюрпризом для большинства игроков.



Секретный уровень в Gish, представляющий собой отсылку к игре Super Mario Bros., скриншот с Motorola Photon Q (превью, увеличение по клику).

Платформер от Cryptic Sea получился необыкновенной и оригинальной двумерной аркадой. Если вы никогда не играли в Gish, то обязательно ознакомьтесь с этой отличной игрой. В 2010 году я очень обрадовался тому, что исходники Gish стали доступными для изучения, поскольку всегда хотел поиграть в этот платформер на каком-нибудь смартфоне. Мобильная Java-версия игры, называемая Gish Reloaded и являющаяся сюжетным продолжением оригинала, была весьма обрезанной в плане графики и физики, а звуковое сопровождение и вовсе было куцым.



Порт мобильной Java-игры Gish Reloaded на Android OS, скриншоты с Motorola Photon Q (превью, увеличение по клику).

Играя в школьные годы в Gish Reloaded, я не ощущал ту мрачную атмосферу, которая присутствовала в компьютерной версии игры. Поэтому я захотел портировать Gish на свой MotoMAGX-смартфон, Motorola ZINE ZN5. Скачав исходный код, я понял, что из этой затеи абсолютно ничего не выйдет. Gish требовал OpenGL, а следовательно и GPU, который отсутствовал в моём телефоне. Программный рендеринг к такой игре написать было не в моих силах, поэтому я отложил эту идею до лучших времён и до более крутых смартфонов. Теперь я с уверенностью могу сказать, что эти времена наступили и я решил портировать компьютерную версию Gish на устройства под управлением Android OS.

<< Перейти к содержанию

2. Подготовка исходного кода Gish к портированию на Android OS

Как было отмечено выше, игра использует для рендеринга старую версию библиотеки OpenGL 1.3, которая недоступна на Android OS. Программист Pickle в 2013 году написал для Gish рендерер, использующий библиотеку OpenGL ES, чтобы перенести эту игру на портативные игровые консоли Pandora и GCW Zero. Он назвал свой проект GishGLES и любезно выложил его исходный код на GitHub. Я решил форкнуться от репозитория Pickle и воспользоваться его наработками по рендереру, поскольку OpenGL ES сейчас доступен на всех Android-устройствах.

Традиционно, я начал портирование движка игры со сборки из исходного кода рабочей версии Gish под GNU/Linux. Проекту требовались следующие библиотеки: SDL, OpenAL, Ogg Vorbis, PNG и OpenGL. Установив все необходимые зависимости, я сразу же столкнулся с проблемами при компиляции. Дело было в том, что Gish использовал старую версию библиотеки PNG, а в новой сломали API и мне пришлось немного переписать функцию loadtexturepng(), чтобы исправить все ошибки сборки. Позже я понял, что эта функция используется в недоделанной свободной реализации Freegish, а оригинальная игра работает только с текстурами в формате TGA. Поэтому на будущее я избавил Android-проект от зависимости в виде библиотеки поддержки текстур формата PNG.

Полную версию Gish’а я покупал ещё давно, в первом Humble Indie Bundle, поэтому я просто развернул Data-файлы платформера рядом со скомпилированным исполнительным бинарником и игра запустилась. Теперь, когда у меня была рабочая версия Gish, я взялся за её перенос на SDL2, так как эта библиотека официально доступна для Android OS. Каких-либо проблем с портированием движка на SDL2 у меня не возникло, этот процесс я описывать не буду, так как в статье про портирование игры Ken’s Labyrinth на Android OS я уже его подробно разбирал. Единственное затруднение возникло с получением списка видеорежимов, но обратившись к документации SDL2 и структуре SDL_DisplayMode, я полностью решил и эту проблему. Цикл, добавляющий видеорежимы в список доступных в настройках игры, получился следующий:

В библиотеке SDL2 на Android OS акселерометр умеет работать как джойстик, что довольно интересно и может быть полезным. Поэтому я решил оставить поддержку джойстиков в своём порте и вынести этот параметр в игровой лаунчер позже.



Подключенный джойстик Logitech F510 к телефону Motorola Moto X, спасибо за фотографию J()KER (превью, увеличение по клику).

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

Теперь, когда игра работала через SDL2, мне требовалось найти порты остальных нативных библиотек, отсутствующих в стандартной поставке Android OS. Движок Gish использовал для вывода звука библиотеки Ogg Vorbis и OpenAL. Зависимость от первой либы разрешалась достаточно легко, я просто перенёс в проект внутренние куски кода из библиотеки SDL2_mixer, которые отвечали за поддержку звукового формата Ogg Vorbis. Здесь мне тоже пришлось столкнуться с некоторыми проблемами несовместимости API старых и новых библиотек. Gish использовал новый Ogg Vorbis, а куски из распотрошённого SDL2_mixer почему работали со старым, кроме того, названия заголовочных файлов немного отличались. Единственная проблема крылась в функции ov_read(), в новой версии библиотеки она была снабжена дополнительными параметрами. Раскидав все отличия по дефайнам и написав сценарий сборки статической библиотеки, я полностью исправил ошибки компиляции и продолжил своё исследование.

Далее оставалось дело за библиотекой OpenAL, а точнее, за её свободной реализацией под именем OpenAL Soft. Кроме официального репозитория, в котором была обеспечена поддержка Android OS, я нашёл в интернете несколько других портов этой либы: openal-soft-android, openal-soft/android и openal-soft/android/lowlatency. Собрав несколько тестовых приложений, я абсолютно не заметил какой-либо разницы между ними, и, поэтому, выбрал вариант openal-soft/android, так как эта библиотека получилась самой компактной и её исходный код располагался в официальных репозиториях проекта OpenAL Soft. Единственное, меня смутило то, что репозиторий не обновлялся с 2012 года. Зато там был необходимый мне файл Android.mk, являющийся сборочным рецептом либы. Современный OpenAL использовал CMake даже для сборки на Android OS и мне пришлось бы писать Android.mk самому по сборочному логу, что было весьма неблагородным делом. Тем не менее, старая библиотека отлично работала и мало весила, что меня полностью устраивало. Кроме того, я активировал в OpenAL поддержку вывода звука через OpenSL ES, что, как мне показалось, немного снизило нагрузку на CPU. Библиотека OpenSL ES сразу доступна в стандартной поставке Android NDK и используется практически на всех Android-девайсах для работы со звуком.

К слову, я немного изменил своё окружение для разработки Android-приложений. Если раньше я использовал дистрибутив Arch Linux, KDE Plasma 5, Eclipse и ant, то теперь я перешёл на Fedora 25, GNOME 3, Android Studio и Gradle. Потихоньку привыкаю к новой обстановке, которая кажется мне достаточно удобной для работы. Единственное, чтобы скрыть крупные и неказистые заголовки окон у развёрнутых программ, я установил специальное дополнение Pixel Saver, которое доступно на официальном сайте расширений GNOME 3.



Работа с кодом игры Gish в Android Studio, скриншот из окружения GNOME 3 (превью, увеличение по клику).

Компания Google в последнем обновлении Android SDK полностью сломала Android ADT и возможность создания приложений в Eclipse, поэтому мне пришлось переводить все свои предыдущие проекты на Android Studio. К сожалению, в Android Studio всё ещё достаточно слабая поддержка Android NDK, C, C++ и CMake, поэтому в качестве второй среды разработки у меня был постоянно запущен Qt Creator, в котором я вносил изменения в нативный код движка игры Gish.



Запуск нативной сборки Gish в GNU/Linux, скриншот из окружения GNOME 3, на фоне интегрированная среда разработки Qt Creator (превью, увеличение по клику).

Возможно, в будущем я полностью перейду на Android Studio, но пока мне нравится скорость работы Qt Creator и качество разбора кода парсером Clang.

Итак, на следующий день после начала работы над кодом, я смог собрать рабочий APK-пакет, который запускал игру Gish на моём Android-устройстве Motorola Photon Q. Конечно, я столкнулся с большой кучей проблем, которые позже постарался решить наиболее оптимальными способами. Но сам факт того, что игра заработала на моём смартфоне без значительных изменений, меня настолько обрадовал, что я забросил все дела и начал проходить эту игру на девайсе, используя физическую клавиатуру. Мне даже удалось полностью пройти две главы, чему я несказанно удивился.

<< Перейти к содержанию

3. Решение возникших проблем порта игры Gish на Android OS

Запустив игру на Android-смартфоне, я заметил, что в ней полностью отсутствовали звуковые эффекты, но при этом музыка работала без проблем. Внимательно изучив исходный код Gish, я увидел, что звуки в формате WAV и DAT загружаются с помощью библиотеки SDL2, посредством макроса SDL_LoadWAV(), но при этом SDL2 никак не участвует в их проигрывании, этим занимается OpenAL. Кроме того, я обнаружил, что в исходном коде очень часто используется функция chdir() для изменения директории с нужными Data-файлами игры. Отладка помогла мне выявить проблему отсутствия звука. Оказывается, этот макрос в Android-версии SDL2 не дружит с относительными путями, а только с абсолютными. Возможно, это как-то связанно с поддержкой assets’ов в функциях подмножества SDL_RWops. Мне пришлось написать небольшую функцию stringconcat(), конкатенирующую строки и передавать в макрос полный путь к звуковым файлам:

Функция loadwav() содержит в своём теле макрос SDL_LoadWAV(), которому передаётся абсолютный путь. Это исправление помогло вернуть все звуковые эффекты обратно в игру.

Далее, я заметил, что стрелки на клавишах в первом обучающем уровне Gish никак не отображаются. Заглянув в исходный код игры, я удивился тому, что IDE запрещала редактировать мне файл gish/src/main/cpp/Gish/menu/menu.c, ссылаясь на невозможность определения его кодировки. В этом большом файле на добрую тысячу строк я с трудом обнаружил такое:



Проблема с распознаванием непечатных символов, скриншот из интегрированной среды разработки Qt Creator.

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

И не знать проблем в дальнейшем. Поправив код, я скомпилировал движок под десктоп и удостоверился, что всё работает отлично. Позже скомпилировал Gish под Android и не поверил своим глазам: стрелки снова не отображались. Я потратил целых два часа на поиск проблемы, перебирая различные варианты, начиная с полной очистки директории сборки и заканчивая пошаговым выполнением некоторых функций. Баг упорно не давал себя обнаружить.



Баг с отсутствием стрелок на кнопках в первом уровне Gish, скриншот с Motorola Photon Q (превью, увеличение по клику).

В конец отчаявшись, я начал в уме перебирать отличия архитектуры x86 от ARM и сразу же вспомнил про unsigned char, по умолчанию использующийся в ARM-компиляторах. Я выставил компилятору флажок -fsigned-char, пересобрал APK-пакет и стрелки появились. Но мне стало интересно, где находится корень этой проблемы и можно ли сделать так, чтобы не приходилось прибегать к использованию этого флага. Я когда-то где-то читал, что беззнаковый unsigned char на ARM’ах был сделан в угоду эффективности и производительности. Спустя некоторое время работы в отладчике я забрёл в огромную функцию drawtext() и нашёл там проблемное место. Я написал небольшую программку, демонстрирующую этот баг:

А вот результат её выполнения при параметрах компилятора -fsigned-char (по умолчанию на x86) и -funsigned-char (по умолчанию на ARM):

Как видно, проблема была в том, что символ рисовался, но одна его координата была рассчитана неверно, за пределами необходимых границ. Явное приведение символа к типу unsigned char помогло исправить этот двойной баг. Вот с такой интересной проблемой я столкнулся и успешно её решил.

Далее, на экране титров я обнаружил немного съехавший текст. И снова, эта проблема проявлялась только на Android-устройстве, а на компьютере было всё отлично. Погрузившись в код, я нашёл очень подозрительное место, которое я выделил в следующую программку, демонстрирующую баг:

Попробуем её скомпилировать для x86 и ARM, а затем запустить:

Координаты в первом случае на разных архитектурах получились разными. Не знаю, что хотели сказать авторы кода подобной конструкцией в sizeof(), возможно они просто хотели сдвинуть надпись на один пиксель вправо, но ошиблись и вставили единичку не туда, куда нужно. Я исправил это небольшое недоразумение.

Далее, гуляя по меню игры, я столкнулся с ещё одним непонятным и неприятным моментом. Дело было в том, что мультиплеерные карты, рассчитанные на несколько игроков, просто не запускались. Оказывается, в форке Freegish некий FrozenCow зачем-то полностью сломал совместимость движка с оригинальной игрой Gish, изменив названия некоторых карт. GishGLES и, соответственно, мой порт унаследовали эту проблему от Freegish. Мне пришлось переписать код загрузки уровней, вернув туда оригинальное поведение.

Я решил выделить этот фикс в отдельный параметр «Fix cache», который отключен по умолчанию, а при активации возвращает названия карт от FrozenCow’а.



Некоторые мультиплеерные мини-игры в Gish, скриншоты с Motorola Photon Q (превью, увеличение по клику).

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

<< Перейти к содержанию

4. Выбор рендерера: OpenGL ES и OpenGL (GL4ES)

К сожалению, рендерер OpenGL ES, написанный программистом Pickle, имел весьма посредственную реализацию освещения, отличающуюся от оригинала. Помимо этого, в компьютерной версии Gish на главном герое игры просчитывались блики света и отражения, что было вырезано из упрощённого рендерера OpenGL ES. Мне захотелось вернуть в игру её оригинальную отрисовку, работающую на OpenGL. Но так как десктопный OpenGL недоступен на Android OS из коробки, мне пришлось искать проекты, которые занимались трансляцией вызовов OpenGL в OpenGL ES. Хорошенько обследовав GitHub, я нашёл два интересных проекта: nanogl от Olli Hinkka и GL4ES от ptitSeb, известного разработчика в тусовке обладателей игровой консоли и UMPC под названием Pandora.

Суть сводилась к следующему: я должен был отключить OpenGL ES рендерер в Gish, заменив его на оригинальный OpenGL’овский. Затем просто прилинковать статически наиболее подходящую библиотеку-транслятор. Свои эксперименты я начал с либы nanogl, но довольно быстро выяснилось, что необходимое мне расширение GL_ARB_texture_env_dot3 там отсутствовало. Проект с этой библиотекой даже не собирался и я переключился на GL4ES. Этот транслятор оказался просто отличным, он реализовывал все необходимые мне функции из множества OpenGL 1.3 и завёлся на Android OS без каких-либо серьёзных проблем. Пересобрав Gish вышеописанным образом я заметил, что освещение и блики стали такими, как в оригинале, что сильно меня порадовало.



Сравнение рендереров OpenGL ES и OpenGL (GL4ES), скриншоты с Motorola Photon Q (превью, увеличение по клику).

Но без нескольких ложек дёгтя, к сожалению, не обошлось. Во-первых, отрисовка через GL4ES была немного медленнее, чем через нативный OpenGL ES. Во-вторых, после нескольких тестов я обнаружил проблемы с отображениями 2D-текстур курсора и некоторых надписей. В-третьих, этот транслятор отказывался работать на моём старом смартфоне Motorola Droid 2. Третью проблему мне не удалось исправить и поэтому я решил в своём порте Gish реализовать выбор метода рендеринга. Для этого в лаунчере игры я сделал специальный переключатель, затем я поправил рецепт Android.mk для сборки нативного движка игры таким образом, чтобы из исходного кода компилировалось две библиотеки: libGish.so и libGishGLES.so, отличающиеся лишь набором дефайнов и прилинкованных либ. Первая библиотека будет загружена, если в лаунчере выбран транслятор GL4ES, соответственно, вторая библиотека будет использована при выборе нативного OpenGL ES. В Java-обёртке SDL2, которая загружает нативные библиотеки приложения, это выглядит следующим образом:

Статическая переменная GishSettings.openGles связана с лаунчером игры и меняется в зависимости от выбора пользователя. Функция getLibraries() возвращает массив названий динамических библиотек. Две библиотеки движка Gish’а отличаются между собой лишь тем, что libGishGLES.so собрана с дефайном -DGLES, а libGish.so с -DGL4ES и ещё к этой либе статически линкуется транслятор GL4ES.

Баг, связанный с проблемой отображения некоторых 2D-текстур, мне удалось полностью пофиксить с помощью расстановки OpenGL-функций glPushMatrix() и glPopMatrix() перед вызовами glBegin() и после вызовов glEnd() соответственно. Я не являюсь специалистом в области компьютерной графики и OpenGL и поэтому не уверен, что это самое верное и оптимальное решение. Но эта проблема, после внесения моих изменений в код, волшебным образом исчезла.

Для того, чтобы повысить FPS в игре, я решил добавить в лаунчер Gish’а несколько параметров. Первые два из них позволяют отключать освещение и тени, а третий задаёт размер проекции (viewport) игрового экрана. Проведя несколько экспериментов, я убедился в том, что отключение освещения и теней повышает FPS, но не слишком значительно, а вот уменьшение параметра «zoom», то есть приближение картинки к игроку, действительно положительно сказывается на количестве кадров в секунду. Хотя, конечно, слишком маленький параметр «zoom» сильно сокращает угол обзора уровня и создаёт много неудобств, но подобрать оптимальное значение всё-таки можно.



Сравнение уровней с отключенным и включенным освещением и тенями, рендерер OpenGL (GL4ES), zoom 8.0, скриншоты с Motorola Photon Q (превью, увеличение по клику).

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

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

Для исправления проблемы с боссом мне пришлось даже написать специальную функцию setupframelighting_fix_cave6_boss(), которая вызывается только на уровне с приведением и повышает количество источников света в нужный момент. Такой вот интересный баг.



Босс-приведение второй главы с выключенным и включенным освещением, OpenGL (GL4ES) рендерер, скриншоты с Motorola Photon Q (превью, увеличение по клику).

Отключенное освещение не слишком сильно влияет на атмосферу и прохождение игры. Кстати, хотел бы выразить огромную благодарность человеку с ником SysLord. Я интегрировал в свой порт несколько интересных возможностей, которые он добавил в репозиторий форка Gish’а на GitHub’е, например, отображение FPS.

<< Перейти к содержанию

5. Сенсорное управление и лаунчер игры

Как и в своих прошлых проектах, в порте Gish на Android OS я сделал возможность выбора нескольких вариантов сенсорного управления. Реализация первого типа управления является наиболее простой и использует прозрачную подложку с изображениями навигационных кнопок, которая элементарно накладывается поверх отрисованной картинки.



Первый вариант сенсорного управления в виде прозрачной подложки, скриншот с Motorola Photon Q (превью, увеличение по клику).

Далее отлавливаются нажатия на импровизированные сенсорные кнопки и, в зависимости от попаданий в эти прямоугольные области, в движок игры отправляются различные события. Методы создания такого варианта управления я подробно описывал в статье, посвящённой портированию Ken’s Labyrinth на Android OS.

Второй тип управления был придуман программистом, использующим никнейм [SoD]Thor. Его отличительной особенностью является удобный сенсорный джойстик и индикация нажатий на кнопки.



Второй вариант сенсорного управления в виде наэкранного джойстика и нескольких кнопок, скриншот с Motorola Photon Q (превью, увеличение по клику).

Здесь задействован приблизительно такой же механизм работы, как и в первом варианте управления. Только вместо статичного изображения на игровой экран накладывается специально подготовленный объект класса GishTouchControlsView, который, в свою очередь, является наследником системного класса View и занимается отрисовкой всех графических элементов и обработкой прикосновений к дисплею. Я модифицировал реализацию [SoD]Thor’а под свои нужды и интегрировал такой тип сенсорного управления в несколько своих портов различных игр. Более подробную информацию о моих изменениях, интеграции и принципах взаимодействия управления вы можете посмотреть в статье про портирование игры Adamant Armor Affection Adventure на Android OS.

Попробовав пройти несколько глав в Gish’е с помощью сенсорного управления, я удивился тому, каким удобным и отзывчивым получился второй вариант с наэкранным джойстиком. Даже на физической клавиатуре проходить игру мне оказалось несколько сложнее. А вот первый вариант с подложкой, для Gish’а совсем не подошёл и вышел неудобным. К недостаткам обоих типов управления можно отнести небольшую просадку FPS на старых устройствах. Ещё обнаружился довольно неприятный баг: из-за того, что экран настроек в игре управляется только курсором, там невозможно поменять какую-либо опцию. Я решил вместо этого экрана выводить предупреждение, которое говорит о том, что для изменения опций игры требуется отключить сенсорное управление.



Предупреждение об отключении сенсорного управления при заходе в опции игры, скриншот с Motorola Photon Q (превью, увеличение по клику).

По умолчанию, в библиотеке SDL2 тачскрин Android-устройства действует так же, как тачпад в ноутбуке, поэтому в работе курсора нет особых проблем. Все настройки игры я вынес в специальный игровой лаунчер, который украсил небольшим изображением-обложкой. Параметры сохраняются после выхода из приложения, что достаточно удобно и практично. Для быстрого сброса прогресса и всех опций игры и для удаления игровых профилей и конфигурации, я добавил в лаунчер Gish’а специальную кнопку «Reset Game…».



Лаунчер моего порта игры Gish, скриншоты с Motorola Droid 2, Photon Q, Droid 4 (превью, увеличение по клику).

После нажатия на кнопку «Run Gish!» с формы лаунчера собираются все настройки и передаются в специальный класс GishSettings, который является статическим. Далее, запускается сам движок игры и с помощью технологии JNI получает необходимые параметры из этого класса и обновляет игровую конфигурацию. Более подробно создание подобных лаунчеров описано в моей статье, посвящённой портированию игры Spout на Android OS.

Поскольку в Android OS нет стандартного файлового диалога, то для выбора директории, в которой расположены Data-файлы игры, я использую специальный класс GishFilePickerActivity, реализацию которого я подсмотрел у программиста, использующего ник Solexid.



Файловый диалог для выбора каталога с Data-файлами Gish, скриншоты с Motorola Droid 2, Photon Q, Droid 4 (превью, увеличение по клику).

Этот диалог позволяет выбрать любой каталог в файловой системе Android-устройства и пробросить его с помощью JNI в нативный движок Gish’а. Получить дополнительную информацию об этом механизме можно в моей статье про портирование игры Adamant Armor Affection Adventure на Android OS.

Движок Gish’а использует несколько специальных файлов для сохранения профиля игрока, его прогресса и конфигурации. В начале своего портирования я расположил эти файлы в директорию с кешем игры, но на последних версиях Android 6 и Android 7 я столкнулся с некоторыми проблемами записи данных на карту памяти и на другие внешние накопители. Поэтому было решено перенести эти файлы во внутреннее хранилище приложения, что избавило меня от подобных неприятностей. В SDL2 есть специальная функция SDL_AndroidGetInternalStoragePath(), которая при вызове возвращает путь до внутреннего хранилища игры.

В Gish’е из некоторых пунктов меню можно открывать ссылки на полезные интернет-ресурсы. Чтобы перенести эту функциональность в порт на Android OS, пришлось использовать технологию JNI.

Функция openUrlFromJNI() вызывается из нативного движка игры и вызывает из Java статический метод openUrl(), который находится в классе GishActivity и принимает String в качестве аргумента.

Объект m_GishActivity является просто статической ссылкой на текущий экземпляр класса, то есть this. При вызове этого метода управление передаётся в браузер, в котором и открывается необходимая URL-ссылка. Если браузеров несколько, то пользователю предлагается их выбор.

Таким образом, сенсорное управление, опции игры и её лаунчер реализованы в Java-обвязке приложения, тогда как сам движок и его вспомогательные зависимости подгружаются в качестве нативных динамических библиотек.

<< Перейти к содержанию

6. Заключение, полезные ссылки и ресурсы

Портирование игры Gish на Android OS дало мне огромное количество ценного опыта и удовольствия. В первую очередь стоит отметить то, что я научился работать в новой среде GNOME 3 и в Android Studio. Я разобрался в том, как собирать с помощью Gradle приложения, использующие нативные библиотеки, Android NDK и JNI. Я познакомился с такими интересными либами, как OpenAL и GL4ES. Кроме того, я смог решить множество интересных проблем и интегрировать в проект современный и достаточно удобный вариант сенсорного управления и файловый диалог для выбора каталога с Data-файлами игры.



Порт игры Gish, запущенный на Android-устройстве Motorola Photon Q, демонстрация на видеохостинге YouTube.

Размер установочного APK-пакета получился весьма небольшим, всего лишь 2.7 МБ для двух архитектур armeabi-v7a и x86. От armeabi пришлось отказаться, поскольку устройства на ARMv6 достаточно старые и слабые, а игра довольно требовательна к ресурсам смартфона. Минимальные системные требования: Android OS 2.3.3 и ARMv7- или x86-совместимый процессор.

Скачать APK-пакет с игрой Gish, готовый для установки и запуска на любом Android-устройстве, можно по этой ссылке:

[Скачать | Download] — APK-пакет Gish, v1.0, armeabi-v7a, x86, 2.7 МБ.

Приобрести официально игру Gish и получить Data-файлы, подходящие для запуска игры на Android OS, можно по этим ссылкам:

Купить игру Gish в Steam
Купить игру Gish на сайте Chronic Logic

Я проверил следующие версии игры и Data-файлов: Gish 1.53 (non-steam, Linux), Gish 1.6 (non-steam, MS Windows) и Gish 1.7 (steam version). Все они без каких-либо проблем работали с моим портом. Демоверсия Gish’а, к сожалению, работать не будет.

Дополнительно APK-пакет можно получить со следующих зеркал: Yandex.Disk и GitHub Releases.

Если игра на вашем девайсе идёт несколько медленно, то вот небольшие советы по увеличению FPS. Во-первых, необходимо отключить освещение «GFX Lights» и тени «GFX Shadows». Во-вторых, нужно выставить зум на 6.0 или 7.0, это значительно повысит FPS за счёт сокращения размера отображаемой сцены. В-третьих, следует переключиться на рендерер OpenGL ES, как на самый оптимальный. Если это не помогает, попробуйте ещё отключить музыку или все звуковые эффекты в игре.

Все исходные коды и проект в целом выложен в репозиторий на ресурсе GitHub. Мои изменения и исходные файлы доступны под лицензиями MIT и GNU GPL v2.0, исходный код игры Gish от Cryptic Sea доступен под лицензией GNU GPL v2.0, Data-файлы игры не являются свободными и их необходимо приобрести отдельно. Ссылка на репозиторий:

https://github.com/EXL/Gish

Проект можно открыть для изучения в Android Studio или собрать под десктопный GNU/Linux с помощью CMake. В этой работе я использовал огромное количество материалов, основные из них я выделю в полезных ссылках ниже. Огромное спасибо ресурсам stackoverflow.com и google.com за то, что они есть.

  1. Репозиторий с форком оригинального исходного кода игры Gish;
  2. Форк GishGLES от Pickle, в котором добавлен OpenGL ES рендерер;
  3. Обсуждение порта GishGLES на GCW Zero от Pickle, форум Dingoonity;
  4. Обсуждение порта GishGLES на Pandora от Pickle, форум Official Pyra and Pandora Site;
  5. Форум Chronic Logic, посвящённый игре Gish;
  6. Исходники библиотеки GL4ES от ptitSeb;
  7. Репозиторий порта библиотеки OpenAL Soft на Android OS;
  8. Обсуждение порта Gish Reloaded на Android OS, форум 4PDA;
  9. Обсуждение моего порта Gish на Android OS, форум 4PDA.

Update 31-JUL-2017: Спустя два месяца после выхода первой версии порта игры Gish для Android OS, я поднакопил немного исправлений и решил оформить новый релиз v1.1 приложения. Во-первых, мне удалось запустить игру на Android OS < 4.0 с рендерером GL4ES. Ранее при выборе этого параметра игра просто зависала сразу после запуска. Так происходило из-за того, что функция dlopen() в старых версиях Android OS не является рекурсивной. Более подробно о решении этой проблемы можно прочитать в этом issue репозитория GL4ES на GitHub’е.



Отключенная рамка в игре Gish, скриншот с Motorola Photon Q (превью, увеличение по клику).

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

  • Исправлено зависание при выборе рендерера GL4ES на устройствах с Android OS < 4.0;
  • Исправлен сброс настроек формы при выходе из различных диалогов;
  • Добавлен параметр, который отключает рамку в игре;
  • Значения счётчика FPS теперь обновляются несколько медленнее;
  • Исправлен вылет приложения в файловом диалоге на Android OS >= 7.0;
  • Добавлен перевод лаунчера на русский язык (спасибо MaximuM FaziL);
  • Оптимизация некоторых функций рендеринга (спасибо ptitSeb);
  • Обновление библиотеки GL4ES до версии 1.0.0-16-g1f4841f;
  • Исправление размера viewport’а при отключенной рамке;
  • В связи с обновлением Android NDK, ANDROID_PLATFORM_LEVEL был увеличен с 9 до 14;
  • Добавлена дополнительная информация в ReadMe-файлы.

Update 01-MAR-2018: Подготовлен новый релиз v1.2 порта Gish на Android OS, который включает в себя некоторые небольшие исправления, основным из которых является переход на новый компилятор нативного кода Clang, благодаря ему размер APK-пакета уменьшился на 200 КБ. Список изменений:

  • Инструментарий Gradle обновлён до актуальной версии;
  • Произведена миграция кодовой базы проекта с компилятора GCC на Clang;
  • Исправлен баг паузы при сворачивании окна приложения;
  • Отцентрирован вывод текста в некоторых меню;
  • Исправлена разблокировка уровней сложности и дополнительных режимов игры;
  • Исправлен баг с непроходимым уровнем «cave6.lvl» при выключенном освещении (спасибо bitvalser за багрепорт).

Скачать готовый к установке APK-пакет последней версии порта Gish можно со странички проектов или из раздела релизов на GitHub’е.

<< Перейти к содержанию

Android, Dev

Комментарии: 30

  1. В общем, мой девайс ты знаешь, все идет чутка дёрганно, но фпс не проседает. Грешу на скорость sd карточки. Кое-как на своем мелком экране заставил эту несчастную нефть прыгнуть на высокий уступ.

    В данном случае, простое управление (кнопочки без стика) могут быть неюзабельными, т.к там надо будет зажимать две стрелки одновременно, что иногда практически нереально сделать.

    1. Спасибо за тестирование. Скорость карты памяти по идее должна влиять только на продолжительность загрузок уровней и меню. Хотя на китайской низкокачественной карточке у меня были проблемы с проигрыванием музыки: она иногда прерывалась и заикалась.

      Кое-как на своем мелком экране заставил эту несчастную нефть прыгнуть на высокий уступ.

      Там нужно немного хитро поступать. Стиком делать движения вниз и вверх, чтобы создать резонанс жидкости внутри главного героя, затем нужно подобрать момент, нажать клавишу прыжка и Gish достаточно высоко подпрыгнет. Попробуй так, потом просто привыкнешь и будешь делать это интуитивно.

      Рад, что игра работает даже на достаточно слабых устройствах, всё-таки она довольно требовательна к железу и подтормаживает даже на моём ноутбуке с интегрированной в процессор видеокартой. Сколько FPS в среднем на уровнях?

      1. Пробовал 1-2 уровни, без освещения под 40-50, с освещением — просадки были до 30. Но и там просадки до низкого фпс изза продвижения по уровню дальше, то есть, определённая зона прогрузилась, ты прошёл прилично, просело немного, дальше высокое значение. В целом, некритично, в статичном положении гиша фпс не проседает вовсе. В целом, хорошо вышло.

  2. Кое-как на своем мелком экране заставил эту несчастную нефть прыгнуть на высокий уступ.

    Я имел ввиду, что мне неудобно на мелком экране эту инерцию проделывать. В экране вся проблема. Смолу уводит в разные стороны.

    1. Есть такой проект, как Falltergeist, являющийся попыткой сделать для Fallout движок с открытым исходным кодом. В интернете есть видеоролик, в котором показано, как запускается эта игра на Android-устройстве. Но я никогда не тестировал этот движок и не знаю, насколько он хорош в работе.

      1. Пока не особо (нет перехода между локациями, боев, прокачки)
        Да и андроид-порт отломан на данный момент (я его делал до того, как перепилил рендер с sdl2 на голый ogl)

  3. Отрыл у себя чудовище под названием Zte Blade AF3 ( https://4pda.ru/devdb/zte_blade_af3 )
    Без теней и света — от 30 до 70, с тенями или светом от 25 до 60, с тенями и светом от 16 до 50 (макс просадки и подъёмы FPS, зафиксированные методом «На глаз»)
    FPS слишком быстро обновляется. может стоит на вывод ставить средние значения за секнду сбора показаний в реальном времени?

    1. Спасибо за ещё один тест, Spreadtrum SC7731 довольно хиленький процессор, хорошо, что хоть до 30 дотягивает.

      По поводу FPS, поправлю, если будет свободное время.

  4. Я, кстати, баг вроде бы нашёл. Некритичный. У меня музыка пропадает менюшная при показе слайдов после нажатия «New Game»
    Хотя в версии для ПК музыка не исчезает

    1. Это не баг, я отключил там музыку специально. Дело в том, что текстуры с этими слайдами при их переключении подгружаются с файловой системы. Это достаточно ресурсоёмкая операция, из-за чего для проигрывания музыки не хватает тактов и/или IO и она заикается. Особенно это сильно заметно на картах памяти с низкой скоростью работы. Поэтому отключил музыку в этих слайдах полностью. Была идея сделать некий кеш всех текстур, но тогда бы игра загружалась слишком долго.

    1. Не особо. Просто не использую большое количество плагинов.
      Кстати, скоро сделаю релиз порта Gish версии 1.1, в котором была проведена некоторая оптимизация и другие улучшения.

        1. С ней можно ссылки теперь выкладывать?

          Да, как обычно. Даже если комментарий попадёт на премодерацию, я его проверю и подтвержу.

          капчу зачем добавил?

          Капчу долго не хотел добавлять, но надоело чистить и смотреть спам. После добавления Google Captcha количество спама уменьшилось со 150-200 сообщений в день до 0.

  5. Вроде ничего так. Багов не ловил, счётчик кадров работает нормально. Версии андроида ниже 4.0 — это с 2.3? Или 2.3.6?

    1. Спасибо за тест. Это на каком устройстве? Всё Explay N1 мучаешь?

      Версии андроида ниже 4.0 — это с 2.3? Или 2.3.6?

      Если точно, то с Android 2.3.3, так как именно эта версия SDK (API Level 10) прописана в манифесте. Это необходимое условие для библиотеки SDL2, на более ранних версиях Android игра работать не будет.

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

    Предположим, в оригинале, для прохождения уровня, нужно зажечь 10 источников света. Если один источник выделяет 5 единиц энергии (цифра с потолка), значит для прохождения нужно 50 единиц энергии.

    Если игрок в настройках игры ограничил количество источников света 2 штуками, то тогда один источник должен излучать 25 единиц энергии — (10 источников в оригинале * 5 единиц энергии в оригинале) / (2 источника в текущем конфиге).

    Итак, если игрок на уровне включит 2 источника, то выделяемая энергия станет 50. А ровно стольно нужно для прохождения уровня в оригинале игры.

    Итого:
    1. Нам нужно ввести коэффициент, зависящий от максимально-разрешенного количества источников света.
    2. Триггер выполнения уровня зависит от количества источников света умноженных на этот коэффициент.

    1. На самом деле всё гораздо прозаичнее и босс реагирует просто на повышение самого количества источников света на уровне. При значении больше единицы он умирает, а чтобы эта логика работала при отключенном освещении, нужно просто «подсунуть» ему требуемое значение в нужный момент.

      Если бы босс реагировал не на количество источников света, а на единицы энергии, то ваше решение действительно было бы оптимальным. Правда, всё же остаётся один вопрос: что делать если игрок полностью отключил освещение в игре и источников света стало 0? 🙂

      1. Так мое предложение и состояло в том, что бы ввести энергию, а не количество источников света.

        Если игрок полностью отключил источники света, то сделать clamp(1, max_light_sources) для этого уровня.

        p.s. reCaptcha глючит и ее лучше поместить под сообщением, а не до него — последовательно заполнил поля, подтвердил, что не робот. Но пока писал сообщение, капча заэкспарилась и последующее подтверждение, что я не робот не сработало.

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

          p.s. reCaptcha глючит и ее лучше поместить под сообщением, а не до него — последовательно заполнил поля, подтвердил, что не робот. Но пока писал сообщение, капча заэкспарилась и последующее подтверждение, что я не робот не сработало.

          И правда, спасибо за совет, переставил её после формы ввода комментария. Так гораздо удобнее.

Добавить комментарий

Ваш адрес email не будет опубликован. Обязательные поля помечены *