Пришла весна. Коты думаю о кошках, мужчины о женщинах, а программист — куда бы ещё портировать имеющийся код. Я ещё прошлой осенью стал обладателем Meizu MX4 Ubuntu Edition, и поэтому выбор был давно очевиден. А тут нашлось и время, и силы.
Освоение новой платформы состояло в портировании на неё сначала библиотеки Allegro, а потом — моей игры Return of Dr. Destructo, о которой я уже здесь писал, и мобильная версия которой всё ещё находится в разработке под iOS и Android, но уже вот-вот скоро когда-нибудь выйдет.
Впрочем, на особенностях портирования именно моего проекта в этом посте я сосредотачиваться не буду, а просто расскажу, с какими подводными камнями повстречался в этот раз. Цели у меня две: во-первых, систематизировать и объединить в одном месте информацию, которая может быть полезна начинающим разработчикам под Ubuntu Touch, а во-вторых — просто напомнить всем о существовании этой платформы, с надеждой общими усилиями сделать её ведущей на мировом рынке, а то на основную пару лидеров у меня профессиональная аллергия.
Мой опыт с данной платформой распространяется исключительно на разработку OpenGL приложений, поэтому если вас больше интересует использование нативного UI (Qt), то здесь вы может найти для себя не так много интересного. Впрочем, как раз разработка Qt и QML приложений достаточно неплохо документирована на официальных ресурсах, в отличие от моих задач (и ЕЩЁ одна платформа опять забыла о геймдеве на старте...).
Чем хороша Ubuntu Touch, и как начать с ней работать
Лично для меня, как для разработчика игр, Ubuntu Touch хороша, в первую очередь, тем, что в отличие от Android здесь программы на C++ являются гражданами первого сорта. Все системные библиотеки и службы имеют C++-интерфейсы, не надо писать никакого кода на странных языках, чтобы создать окошко или отправить запрос на оплату. Официальная IDE — QtCreator, не лишена недостатков, особенно в плане отладочного UI, но в целом очень неплоха, а главное — «из коробки» хорошо поддерживает отладку C++ приложений на устройстве, которую Google в своём Android Studio до сих пор прикрутить нормально не может, бе-бе-бе. Ну, а плюс по сравнению с iOS тут главный в том, что не нужно иметь Mac (или страдать с плохо работающими виртуальными машинами)
Теперь о минусах. Во-первых, вам всё же придётся поставить Ubuntu или какой-то более-менее совместимый Linux. Причём поставить его на Virtual Box — плохой вариант! Разработчики Ubuntu Touch-плагина под QtCreator использовали OpenGL (QtQuick) для отрисовки своего UI, и как результат, в Virtual Box всё это работает очень плохо — например, окно QtCreator'a всегда находится поверх любых других окон.
Ubuntu IDE приветствует нас
Так же, лично мне не удалось заставить Ubuntu SDK увидеть моё устройство, пока я пытался работать в Virtual Box, при том, что сама Ubuntu его видела. Но тут, возможно, был мой косяк, я так и не разобрался, и поставил Ubuntu на реальное железо.
Чтобы начать разрабатывать приложение под Ubuntu Touch, проще всего поставить Ubuntu SDK. В общем-то, никто не мешает скачать нужные компоненты и руками, но мне кажется, это больше боли, чем нужно в организме. По инструкции для установки SDK надо подключить PPA, в котором оно живёт (вообще-то, SDK есть и в обычном Software Centre, но может быть какой-то не той версии?).
Если вы будете пока что довольствоваться эмулятором, то дальше надо его создать в закладке Devices. Процесс подробно описан, однако заставить его работать мне так и не удалось — эмулятор вроде бы как создавался, но видеть его IDE не хотела (возможно, я указал при создании версию framework, отличающуюся от той, с которой компилировал программу).
Чтобы запуститься на устройстве, придётся включить на нём режим разработчика. К моему невероятному удивлению, для этого потребовалось установить на нём PIN-код, которым я раньше не пользовался! Очень неудобно, минусик Canonical, Андроид такой фигнёй заниматься не заставляет.
Кроме того, компьютер будет видеть телефон, только если у того включён экран и снят лок-скрин (который с кружочком). Что интересно, набирать ПИН-код как раз не надо.
Подключённое устройство здорового человека
Если даже после этого ваш телефон (а может быть и планшет) всё равно не появился в списке устройств во вкладке Devices, то придётся вспомнить полученные при общении с всё тем же Андроидом навыки, и лезть править конфигурацию ADB (поскольку пока что все наличиные устройства с Ubuntu Touch в основе своей являются Андроид-устройствами). Посты (1, 2) рекомендуют добавить в файл ~/.android/adb_usb.ini vendorID вашего устройства.
chroot, зачем он нужен для компиляции под Ubuntu Touch, и что с ним делать
Опытные линуксоиды, давно знакомые с chroot, schroot, а может уже даже и с click, могут пропустить эту секцию, чтобы не смеяться над тем, как жалкие виндузятники познают мощь Линукса.
Чтобы скомпилировать приложение, понятное дело, нужна туча всего — компилятор, линковщик, гора библиотек. Всё это должно быть в наличие под ту платформу, под которую мы компилируем. Google решили эту проблему, задав несколько переменных окружения и создав свои собственные скрипты для сборки (ndk-build). Решение, надо сказать, довольно плохое, с трудом работающее с внешними системами сборки, и ненадёжное, как и вся поддержка C++ «корпорацией добра». Зато платформо-независимое (ну, в теории).
Ubuntu SDK пока работает только под Ubuntu, теряет ему нечего, поэтому тут всё по другому. Для сборки создаётся файловая система, полностью отражающая нормальную линуксовую, но с gcc под ARM, и с библиотеками для архитектуры armhf в нужных директориях. Далее, эта ФС объявляется рутовой на время сборки, и всё работает, как если бы у нас просто был такой вот хитрый, специально заточенный под сборку для ARM Linux.
Достигается это при помощи механизма chroot, которые позволяет работать с этими вот самыми хитрыми ФС. Нужные chroot'ы Ubuntu SDK создаёт сама по вашей команде, поэтому разбираться с ними досконально не обязательно. Но учтите, что процесс это довольно долгий, т.к. всё содержимое chroot'а (а это много-много пакетов, около 1.5Gb) выкачивается из сети, а создавать его надо под конкретную архитектуру (i386 для эмулятора, или armhf для реального устройства или другого эмулятора) и конкретную версию SDK.
Интересное начинается тогда, когда вы решили, что хотите использовать не только те библиотеки, которые идут в стандартной поставке chroot'а, а ещё какие-нибудь дополнительные. Документация гласит, что всё, что входит в поставку chroot'a — гарантированно будет на телефоне, всегда в совместимых версиях, поэтому с такими библиотеками можно смело линковаться динамически, и рассчитывать на то, что у юзера на телефоне всё заработает. Но многого из того, что нужно для жизни разработчика игр там нет: libpng, libfreetype, libogg… Всё это придётся ставить руками, и вот тут появляется необходимость знать о chroot'ах больше.
Дело в том, что chroot поддерживает не только работу с отдельной подменной ФС, но и создание её снимков. Базовый, source снимок при обычной работе остаётся неизменным, чтобы вы ни делали. Более того, можно насоздавать сессий, и у каждой сессии тоже будет свой снимок source, который будет жить, пока жива сессия (и, что важно, не будет отражать изменений в source, если они за это время произойдут!). Сессии, создаваемые Ubuntu SDK переживают не только рестарт редактора, но даже перезагрузку компьютера!
Правильные способ ставить новые библиотеки в chroot — использовать кнопочку «Maintain» в закладке «Click» в разделе «Ubuntu» в настройках IDE. Это даст вам консоль, в которой можно позвать apt-get install libpng-dev:armhf и тому подобное. При этом, изменятся будет как раз source chroot. Правда, вот не знаю, перезапустит ли свои сессии редактор.
Вот вам скриншот, чтобы не заблудиться
Если же вам захотелось полазить по chroot'у руками, из-за пределов IDE, или, скажем, написать скрипт, автоматизирующий установку нужных пакетов в свежескачанный chroot, то команды для этого описаны в документации, но я повторю их здесь, потому что лично я их там в первый раз упустил, и долго тормозил.
click chroot -a armhf -f ubuntu-sdk-15.04 run COMMANDNAME — запускает COMMANDNAME внутри (не source!) chroot'a и сразу же выходит. Параметр -a — архитектура, для которой создан chroot, -f — версия SDK. С помощью этой команды, установить в chroot ничего нельзя (всё установленное тут же исчезнет), но можно запустить, например, сборку, или find, с целью найти что-нибудь внутри chroot'овой ФС.
click chroot -a armhf -f ubuntu-sdk-15.04 install PKGNAME — запускает apt-get install PKGNAME, изменяет source chroot
click chroot -a armhf -f ubuntu-sdk-15.04 maint — аналогично кнопочке «Maintain», запускает консоль для изменения source chroot'a
И ещё пара важных команд:
schroot --list --all-sessions — покажет нам список всех активных сессий
schroot --end-session SESSION_ID — убьёт указанную сессию
Если вы поставили библиотеку в chroot самостоятельно при помощи install или maint — не забудьте убить сессии, принадлежащие Ubuntu SDK, иначе QtCreator не заметит сделанных изменений (я потерял из-за незнания этого день)!
Напоследок, уточню: то, что вы поставили в chroot руками — на телефоне по умолчанию будет отсутствовать! Поэтому с такими библиотеками надо либо линковаться статически, либо таскать их с собой в click-пакете (просто указать, какие либы вам нужны, и надеяться, что при установке вашего пакета в систему зависимости докачаются сами, как при использовании deb, нельзя — click так не умеет идеологически).
Миру — Mir
Привычных Иксов на Ubuntu Touch нет. Вместо них, там имеется давно обещанный Canonical Mir, который всё никак не придёт на десктопы, а на мобилках — уже пожалуйста, здравствуйте, вот и он. По своему API, Mir очень милый — всё просто, достаточно неплохо документировано, и, чудо из чудес, большинство функций имеют и синхронный, а асинхронный вариант, что вообще такое супер, что суперее некуда. Более того, если вдруг хочется почему-то позвать асинхронную функцию, а потом, в другой точке, дождаться её результата — есть mir_wait_for, который это сделать позволяет. Просто как будто люди писали, а не обычные инопланетяне!
Из минусов, следует отметить почти полное отсутствие официальных примеров Mir-клиента. То, что лежит на вики — демонстрирует лишь самые-самые основы, и не демонстрирует того, что нужно нам — а именно, как связать MirSurface и контекст OpenGL. К счастью, в других местах примеры найти можно.
Учтите, что API Mir'a пока не очень стабилен, поэтому приведённый выше пример не компилируется — функции mir_surface_get_egl_native_window не существует. Вместо неё, для получения из MirSurface окна, совместимого с eglCreateWindowSurface, следует использовать связку mir_buffer_stream_get_egl_native_window
(mir_surface_get_buffer_stream(surface)).
Также, следует помнить, что Ubuntu Touch не поддерживает OpenGL ES первой версии — только OpenGL ES2 и 3! Нужные хидера и библиотеки просто отсутствуют. Поэтому, если у вас, как у меня, где-то завалялся легаси-код времён Очаковских и существенно до нового покорения Крыма, то придётся обложить его ifdef'ами.
Не забудьте передать в списке атрибутов eglChooseConfig пару
EGL_RENDERABLE_TYPE, EGL_OPENGL_ES2_BIT
А в списке атрибутов контекста для eglCreateContext —
EGL_CONTEXT_CLIENT_VERSION, 2
Иначе будете долго удивлённо смотреть на многозначительные ошибки BAD_CONFIG и BAD_ATTRIBUTE. Чёрт побери, XXI век на дворе, OpenGL до сих пор не умеет рассказать, какой именно аттрибут плохой, и что ему не понравилось в конфиге…
Обработка ввода в Mir осуществляется путём установки обработчика событий. Что совершенно прекрасно — если вы используете Mir из программы на C++, то вам будет доступен вариант API с поддержкой std::function в качестве коллбэков. Ну, а если вам больше по душе обычный C, то будет вам вариант с простыми указателями на функции и void *context.
А коды клавиш Mir присылает те же самые, что X11, так что не спешите выкидывать ваш вариант функции get_key_name!
Make it Click
И вот, допустим, портируемый нами код скомпилировался, и готов к запуску. Возникает вопрос, как его засунуть на устройство, вместе с ресурсами, динамическими библиотеками и прочими чертями рогатыми. deb нынче не модно, на rpm, который вы нежно любите ещё с Mandrake 6.0, Ubuntu Touch даже не посмотрит, а использовать надо новый формат пакетов — Click. Именно его и собирает Ubuntu IDE для заливки на телефон.
На мой взгляд, существенным минусом Click-пакетов по сравнению с IPA или APK является то, что он — не ZIP, а что-то своё, хитрое. Как результат, вот просто взять, и посмотреть, что же там напаковалось — низзя, придётся лезть в консоль, разбираться в ключах click. Впрочем, конечно, скоро кто-нибудь напишет плагин для Midnight Commander, чтобы была Click VFS, и всё снова станет более-менее хорошо.
Существенным плюсом click является то, что засунуть в него можно что угодно, и как угодно внутри расположить (почти). Всё, что там лежит, во время запуска пакета будет смонтированно в качестве файловой системы от корня, и дальше вы сможете искать библиотеки и данные по привычным вам путям в каком-нибудь /usr/local. Это, конечно, если ваше приложение переехало на Touch с обычного Линукса. Если же оно — гость с враждебной Винды, то может вам и не надо дублировать структуру папок, привычную линуксоидам, а просто плюхнуть исполняемый файл прямо в корень, а данные — рядом, чтобы не искать, и всё удобно и просто. По умолчанию, Ubuntu IDE делает именно первый вариант, но уговорить её перейти ко второму при помощи редактирования CMake или QMake файлов — не сложно, если вы уже знакомы с их INSTALL-директивами.
И есть, значит, ещё один тонкий момент, а точнее, три. Для подготовки click-файла, вам ещё понадобятся:
a) manifest.json — файл с описанием, что ваша программа есть, для какой она архитектуры, и кого бить, если что-то не работает. Он же содержит имена для следующих двух файлов в разделе «hooks»
б) AppName.desktop — файл, описывающий как выше приложение на телефоне выглядит — имя, иконка и т.п. — и как себя ведёт. Последнее касается, например, настроек сплэш-экрана, а также допустимых ориентаций (см. ниже)
в) AppName.apparmor — файл, где перечисленно, что вашей программе можно (по умолчанию — всё нельзя). Аналог permissions в AndroidManifest.xml.
Тут мы с некоторым недоумением натыкаемся на отсутствие документации. Базовое содержимое всех трёх файлов проблемы не представляет — его можно подсмотреть в шаблонах, или в сети, но я приведу их здесь для полноты.
manifest.json:
{ "name": "APPNAME.DOMAINNAME", "description": "DESCRIPTION", "architecture": "ARCH", "title": "TITLE", "hooks": { "APPNAME": { "apparmor": "APPNAME.apparmor", "desktop": "APPNAME.desktop" } }, "version": "VERSION", "maintainer": "NAME SURNAME <EMAIL@ISP.COM>", "framework" : "FRAMEWORK" }
Содержимое manifest.json легко редактируется с помощью встроенного в Ubuntu IDE редактора.
AppName.apparmor:
{ "policy_groups": [ "networking", ], "policy_version": 1.3 }
Сюда надо добавлять нужные вам разрешения. Это тоже удобнее делать через редактор IDE.
AppName.desktop:
[Desktop Entry] _Name=VISIBLE_APP_NAME Exec=EXECUTABLE_NAME Icon=ICON Terminal=false Type=Application X-Ubuntu-Touch=true X-Ubuntu-Supported-Orientations=landscape
А вот desktop-файл почему-то не редактируется в IDE, точнее, открывается просто в виде текста. На первый взгляд, это не проблема, но на второй, когда добираешься до конца и видишь все эти X-Ubuntu-Whatever, сразу возникают вопросы — а сколько этих настроек вообще, и какие они бывают. Разработчики под новую платформу — они подобны исследователям нового континента, и поэтому спрашивать их про такие вещи — всё равно, что спрашивать исследователя, сколько на новом континенте рек — «а хрен его знает». Документации по нашему континенту Ubuntu в этом плане почти нет. Известно следующее:
Общая документация по формату имеется тут, и содержит много полезного, но только не X-поля.
X-Ubuntu-Touch=[true/false] — это несущий костыль, который показывает, что, что приложение телефонное, а не десктопное.
X-Ubuntu-Supported-Orientations=[portrait/landscape/primary] — допустимые для приложения ориентации экрана (primary — это, как я понимаю, portrait для телефонов и landscape для планшетов). Список взят отсюда, в более официальных доках есть только первые два варианта; чтобы поддерживать все возможные положения экрана — не указывайте этот ключ вообще.
X-Ubuntu-Gettext-Domain=[?] — имеет отношения к выбору файла перевода, который будет использовать. Точнее — я не понял, с документацией всё тухло.
Поля, контролирующие поведение сплэш-экрана:
X-Ubuntu-Splash-Show-Header=[true/false] — показывать ли заголовок приложения на сплэш-экране.
X-Ubuntu-Splash-Title=[TEXT] — какой именно текст показывать в заголовке.
X-Ubuntu-Splash-Image=[FILENAME] — изображение, которое будет показано на сплэш-экране. Изображение будет показано в том размере, в котором есть, без подгонки под размер экрана.
X-Ubuntu-Splash-Color=[COLOR] — (сплошной) цвет фона сплэш-скрина в формате QColor::setNamedColor.
X-Ubuntu-Splash-Color-Header=[COLOR] и X-Ubuntu-Splash-Color-Footer=[COLOR] — цвета верха и низа сплэш-скрина, между которыми будет сделан градиент.
Общий список этих дополнительных полей пока что отсутствует. Попытка добыть его из исходников Unity8 пока не увенчалась успехом — я нашёл место, где обрабатываются пара настроек, но остальные там нигде даже не упоминаются — хотя и работают.
Редактор manifest-файла. Вот хочу такой же для desktop-файлов
Ubuntu IDE: CMake vs. QMake
Матёрый линуксоид, конечно, с презрением смотрит на все эти ваши IDE, собирает программы при помощи старого доброго make, устанавливает их на устройство из консоли, и отлаживает консольным же gdb. Мы же, изнеженные прелестями Visual Studio и прочих богопротивных IDE, хотим, чтобы всё нам было на блюдечке с голубым графическим интерфейсом. Поэтому пытаемся использовать Ubuntu IDE, а также чуть менее инопланетные системы сборки, например, CMake или QMake.
Если вы фанат QMake, и давно используете его для всех ваших проектов, у меня для вас отличные новости — на Ubuntu Touch у вас всё будет зашибись! Подключите нужный модуль при помощи load(ubuntu-click), добавьте в проект описанные выше манифесты, и всё должно заработать.
С CMake в теории тоже всё просто. Для того, чтобы Ubuntu IDE начала запускать ваши проекты на телефоне, достаточно указать в вашем CMakeLists.txt следующую строку с путём к manifest-файлу вашего приложения:
set(UBUNTU_MANIFEST_PATH "manifest.json" CACHE INTERNAL "Path to manifest file")
Очень важный момент — обязательно укажите, что переменная попадает в кэш! IDE будет её искать именно там, а если не найдёт — ничего не скажет, но работать всё будет неправильно. Кроме того, мои эксперименты показали, что нельзя указывать путь к manifest'у через переменные CMake. Во всяком случае, значение "${CMAKE_SOURCE_DIR}/manifest.json" приводило к тому же эффекту, как если бы я вообще не указал ничего.
Далее, имеет место быть странное, и я бы даже сказал бажное поведение IDE. Даже если вы всё указали совершенно правильно, IDE создаст для вашего приложения две конфигурации запуска. Первая будет по имени совпадать с тем, что у вас было указано в CMakeLists.txt в add_executable. Это плохая, негодная конфигурация, и она выбрана по умолчанию! В этой конфигурации, сборка кода и пакета идёт под ARM, а вот запускать всё это IDE будет пробовать на десктопе. Чего у неё, естественно, не будет получаться (если у вас не ARM-десктоп), и вы будете видеть в Application Output ошибки вида
Selected architecture arm is not compatible with reported target architecture i386:x86-64 Architecture rejected target-supplied description Debugging has finished
Чтобы так не было, надо зайти в закладу Run в разделе Projects вашего проекта, и там переключиться на вторую конфигурацию. Её имя будет взято из manifest-файла, из раздела «hooks». Если оно будет совпадать с тем, что было указано в add_executable, то к нему будет дописана цифра 2 (например, «cmake_test2»). Это вот хорошая конфигурация, если выбрать её — то проект будет пытаться запускаться на устройстве (или эмуляторе).
Кстати, с библиотеками, собранными CMake, вас ждёт ещё один неприятный сюрприз: если вы вздумаете в пределах сессии IDE выстроить зависимости между либами и приложением, то ничего у вас не получится. Дело в том, что Ubuntu IDE для собранных библиотек попробует вызвать шаг Deploy (то бишь, Install в терминах CMake), и обломается, потому что у библиотеки, сюрприз-сюрприз, нет manifest.json (который ей нафиг не впёрся). Разработчики говорят, что такой способ использования IDE не предусмотрен, и, скорее всего, в этом плане ничего не изменится. Добавляйте библиотеки суб-проектами — тогда всё будет хорошо.
По первому же странному поведению, мною заведён баг в багр-треккере Ubuntu IDE.
«Плохая» конфигурация. Обратите внимание на парметры запуска — аргументы командной строки и рабочую папку. Это верный признак того, что IDE будет пытаться запускать ваш проект на десктопе вместо телефона! Не обращайте внимания на шаг «Deploy to Ubuntu Device» в списке шагов запуска — на устройство-то ваш пакет может и зальётся, но запускаться всё равно будет на десктопе.
«Хорошая» конфигурация. Как видно, вместо коммандной строки и рабочей папки — совсем другие настройки.
Как я раньше без этого жил?!
Напоследок, ещё поделюсь трюком, который не имеет к Ubuntu Touch никакого отношения, но который я узнал только недавно, а хотел узнать — как минимум, год назад, если не больше. Порой, стоит задача слинковать приложение не с динамической, а со статической версией библиотеки. Конечно, у gcc есть флаг -static, но он не очень удобен (да ещё, по моему опыту, и не работает в каких-то случаях). Но оказывается, можно делать вот так:
gcc yourprogram.o -ldynamiclib -l:libstaticLib.a
В этом случае, первая библиотека будет взята та, которая есть (то есть, обычно динамическая), а вторая — строго статическая. Обратите внимание, что после двоеточия надо указывать ПОЛНОЕ ИМЯ файла, вместе с lib и .a!
Этот трюк сработает в QMake-файле, а вот если вам хочется слинковаться со статической библиотекой в CMake, то просто надо в аргументах target_link_libraries написать не libname, а libname.a, и всё сработает автоматически.
Заключение
Разработка под Ubuntu Touch пока что полна сложностей и приключений. Единственный компонент системы, не доставивший мне хлопот, это Mir. Основные сложности представляет почти полное отсутствие документации и местами странное и непредсказуемое поведение IDE. Кроме того, постоянное обновление всего участвующего в разработке софта всегда имеет шанс что-нибудь сломать (например, у меня в течении дня был поломан chroot для сборки — но не совсем, а просто не мог обновиться; после выкатки фикса, пришлось его пересобрать, что есть большая трата времени).
Помощь по проблемам, связанным с Ubuntu Touch и разработкой под неё можно получить по следующим каналам:
AskUbuntu — StackOverflow для Убунту в целом. На вопросы про Touch отвечают, но мало и с большими задержками.
Ubuntu Phone Mailing List — список рассылки для пользователей Ubuntu Touch. На вопросы разработчиков здесь почти никогда не отвечают, но если у вас что-то глючит в телефоне — то высока вероятность, что именно тут вам помогут.
#ubuntu-app-devel IRC — IRC канал для общение разработчиков под Ubuntu Touch. Если вам удастся достучаться до кого-то из опытных разработчиков, например из Core Apps, то вам вполне могут помочь с вашими вопросами, но большую часть времени тут, как и в любом техническом IRC, стоит гробовая тишина, а вопросы «в воздух» игонрируются. Hint: не бойтесь использовать ключевое слов appdevs, чтобы захайлайтить разработчиков (см. тему канала)!
Также, существует список рассылки для разработчиков, но он мёртв. На IRC есть и другие каналы, связанные с Ubuntu Touch, см. полный список на Вики.
Что до меня, то я не уверен, что я буду заниматься разработкой под эту платформу за пределами портирования текущего проекта. Не потому, что мне не понравилось, но потому, что нет больше подходящих проектов, пока что. Тем не менее, у этого поста ещё может появиться продолжение — когда я разберусь с новым API для платежей Ubuntu Pay, с тем, почему у меня не работет звук, с проблемами сворачивания и разворачивания приложения и в целом доведу игру до состояния, близкого к релизу. Если, конечно, наберётся какого-то интересного материала.
Ну, и напоследок, скриншот моего проекта, запущенного на устройстве. Как видно, мне пока не удалось убрать верхний барчик (в IRC говорят, что это баг текущей версии ОС)
Автор: MaxEdZX