[Из песочницы] Qt 5.2, от желания до Google Play

Здравствуйте, коллеги.Случилось так, что мне рассказали о QT5.2 и его новой возможности быстро и легко создавать кроссплатформенные приложения под Android и iOS. С QT я знаком был уже давно, но в последнее время работа была связана с другими технологиями и я немного запустил его развитие. Узнав это, я отправился на сайт QT, посмотрел красивое видео, где за 10 минут HelloWorld приложение создается сразу под android и ios. Впечатления были очень положительные.

Было принято решиение заняться мобильной разработкой. Появился план пройти путь от желания сделать приложение до его публикации в Google Play. Но на первом этапе хотелось пройти это с тем что не жалко и в чем можно делать ошибки. И все это на новом QT5.2.

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

Выбор с++ или javascript Я конечно люблю c++, но писать на нем такую мелочь я не решился. C++ заставляет разработчика писать вдумчиво и быть очень внимательным чтобы не «отстрелить себе ногу», мне же хотелось сделать проект в стиле XP, быстро и главное чтобы работало. Выбор пал на QML и javascript, заодно возможность разобраться с этими так продвигаемыми Digia технологиями.Первый набросок Быстро поставив qt, почитав документацию на qml и рассмотрев игровую механику Flappy Bird (до этого я о ней не слышал) был сделан первый набросок игры. Были использованы простые NumberAnimation для имитации полета птицы и передвижения столбов. Все работало отлично, но появились вопросы:как сделать проверку столкновений как масштабировать на разные разрешения графику и физику как быть с дизайном что делать со звуками игры как встроить монетизацию Физика Единственно правильным решением, для 2-х выше указанных проблем, я посчитал использовать всем известный Box2D. Плагин для qml нашелся быстро — github.com/qml-box2d/qml-box2d. Пара дней экспериментов, чтения документации box2d и все переписано и прекрасно работает. Но проблемы еще ждали впереди.Звук От фоновой музыки я отказался так как сам ее не люблю да и не смог бы ни подобрать хороший вариант ни тем более сделать самостоятельно. Так на www.freesound.org были подобраны 3 звука: взмах крыльями, столкновение и новое очко.Для воспроизведения был использован хороший пример создания Flappy Bird из V-play с AudioManager-ом. Но без напильника не обошлось. import QtQuick 2.2

Item { id : audioManager

property QtObject effect1: Qt.createQmlObject("import QtMultimedia 5.0; SoundEffect{}", audioManager); property QtObject effect2: Qt.createQmlObject("import QtMultimedia 5.0; SoundEffect{}", audioManager);

property int hit: 22 property int point: 33 property int silence: 44 property int wing: 55

property bool effectSwitcher: false;

function play( sound) {

var effect;

if( !effectSwitcher){ effect = effect1; effectSwitcher = true; }else if( effectSwitcher){ effect = effect2; effectSwitcher = false; }

if(effect == null) return;

switch(sound) { case hit: effect.source = "audio/sfx_hit.wav" break case point: effect.source = "audio/sfx_point.wav" break case silence: effect.source = "audio/sfx_silence.wav" break case wing: effect.source = "audio/sfx_wing.wav" break }

effect.play(); } } воспроизведение:

audioManager.play( audioManager.wing); Все заработало на desktop машине, на телефоне же приложение падало. Причина оказалась банальна, надо было в .pro файл добавить следующее:QT += multimedia

Зачем здесь два SoundEffect и для чего sfx_silence станет понятно ниже в описании встретившихся багов.

Масштабирование Масштабирование было сделано стандартно. За основу было взято разрешение 480x800 (маленькое но наверно самое распространенное на данный момент). Относительно него были жестко заданы размеры птицы, и столбов. Дальше просто было сделано вычисление коэффициента масштабирования для текущего разрешения относительно эталонного, и затем все размеры требующие масштабирования просто на него умножались. Со всем этим сильно помог вот этот пример bitbucket.org/wearyinside/cute-plane, но как обычно множество проблем там решено не было. width: Screen.width height: Screen.height property int defaultWidth: 480 property int defaultHeight: 800 property double measure: Math.min(Math.min(width, height) / defaultWidth, Math.max(width, height) / defaultHeight) property double textScale: Math.sqrt( measure) Все физические объекты мастабируются линейно, а вот текст при таком мастабировании на высоких разрешениях разрывал все рамки. Для него пришлось сделать масштабироваие по квадратному корню от основного коэффициента масштабирования.Дизайн Так как я программист, дизайн был для меня дремучий лес, но интернет наше все и через день чтения различных статей на эту тему была выбрана векторная графика и редактор Inkscape. Для того чтоб нарисовать мультяшную птицу тоже понадобился всего 1-2 дня. Первоначально был сделан набросок на бумаге и нарисовано несколько возможных вариантов. Затем самый лучший был перенесен в svg. Далее и все остальные изображения были сделаны в векторном формате. Для того чтобы использовать svg фаилы в qml в .pro файл надо добавить следующее: QT += xml svg QTPLUGIN += qsvg Монетизация Основная часть была написана и встал вопрос монетизации. Данный проект хоть и тестовый, но хотелось в нем уже разобраться и с монетизацией. Выбрана была монетизация рекламой admob. И тут начались первые серьезные проблемы. Оказалось что для qt/qml нет никаких плагинов для встраивания admob. Была найдена устаревшая реализация qadmob и закрытая реализация V-play AdMob plugin. Тучи сгустились и начали появляться мысли оставить qt до лучших времен. Перерыв весь интернет стало понятно что надо перерыть исходники qt и разобраться как он сделан под Android. И через 4 дня раскопок, тестовый рекламный баннер отобразился в игре. Вот пример как это делается github.com/AlexMarlo/AdMob-Qt5.2-Example. На отображение баннера в общем ушла неделя.Баги Далее стало ясно что все основные части сделаны и надо поправить мелкие, отложенные на потом, баги.Утечка памяти Память утекала по 100 MB за минуту игры. После переодического комментирования qml кода и проверки результатов, проблема была найена. Оказалось что память утекала при таком присвоении: linearVelocity.x = 220; linearVelocity.y = -420; поменяв этот вариант на var flyImpulseVelosityY = -420 * measure; var flyImpulseVelosityX = 220 * measure; var impulse; impulse = Qt.point( flyImpulseVelosityX, flyImpulseVelosityY); applyLinearImpulse( impulse, getWorldCenter()); утечки прекратились. Здесь похоже на проблему с qml-box2d, но копать глубже я не стал.Пропадание звука При очень частом тапанье и следовательно очень частом воспроизведении, звук стабильно пропадал до проигрывания другого звукового файла. Тут и появилось два SoundEffect. Причем это проявлялось только на андроидах. Для того чтоб этого пропадания не было SoundEffect-ы проигрываются поочередно. К такому решению пришел просто опытным путем. Судя по всему это какая-то проблема в самом qt.Приложение завершалось на assert в Box2D при включенном масштабировании width: Screen.width height: Screen.height property int defaultWidth: 480 property int defaultHeight: 800 property double measure: Math.min(Math.min(width, height) / defaultWidth, Math.max(width, height) / defaultHeight) Проблема кроется в первых 2-х строчках. Как оказалось пока не сконструирован qml элемент в котором вызывается Screen, Screen.width и Screen.height будут равны 0. Получается что коэффициент масштабирования первоначально равен 0 и здесь box2d завершает приложение на assert, так как физические объекты не могут быть нулевого размера.Это удалось исправить только динамически создавая объекты в тот момент, когда коэффициент масштабирования примет не нулевое значение.Не работающее управление громкостью Как оказалось, в текущей версии QT под андроид, кнопки управления громкостью не работают. Все советы на форумах того же qt предлогали перехватывать нажатия кнопок в Activity, что и было сделано. @Override public boolean onKeyDown(int keyCode, KeyEvent event) { if ( keyCode == KeyEvent.KEYCODE_VOLUME_UP) { AudioManager am = (AudioManager) this.getSystemService(Context.AUDIO_SERVICE); int index = am.getStreamVolume( AudioManager.STREAM_MUSIC) + 1; if( index <= am.getStreamMaxVolume( AudioManager.STREAM_MUSIC)) am.setStreamVolume( AudioManager.STREAM_MUSIC, index, 0); } if( keyCode == KeyEvent.KEYCODE_VOLUME_DOWN){ AudioManager am = (AudioManager) this.getSystemService(Context.AUDIO_SERVICE); int index = am.getStreamVolume( AudioManager.STREAM_MUSIC) - 1; if( index >= 0) am.setStreamVolume( AudioManager.STREAM_MUSIC, index, 0); } return super.onKeyDown(keyCode, event); } Тестирование на разных устройствах и снова баги Дальше наступил этап тестирования на разных андроидах, вот примерный список устройств:Motorola Droid Razr Nexus One Samsung Galaxy S Duos Nexus S Nexus 7 1st gen Nexus 7 2nd gen Nexus 5 Sone Xperia Ray Samsung Galaxy S3 Nexus 4 Acer Iconia Tab A510 Galaxy S Plus Alcatel OneTouch M'POP 5020D Samsung Galaxy Fame GT-S6810 ASUS Transformer Pad TF300TG Samsung И все проблемы вылезли на самсунгах, причем чем лучше телефон тем сильнее проявлялись баги, а судя по вот этой статистике www.appbrain.com/stats/top-android-phones оставлять баги на самсунгах просто нельзя.Лаг при проигрывании первого звука По непонятной причине первое проигрывание звука через SoundEffect подвисало и потом все работало нормально. Особенно сильно это проявлялось на Samsung Galaxy S3, на других самсунгах тоже было, но не так заметно. На устройствах других производителей данной проблемы не было. Тут и появился sfx_silence.wav. Это по сути пустой звукойвой файл который проигрывается при загрузке игры.Лаг при динамическом создании Box2D объектов Следующая проблема была по причине того что, Box2D объекты создавались динамически для корректного масштабирования и это создание очень тормозило на самсунгах, особенно на том же Samsung Galaxy S3.Создание объектов земли:Nexus One 97 ms Samsung Galaxy S Duos 986 ms Разница на порядок, но разбираться в нюансах реализации qml-box2d и самого Box2D я не стал, а просто перенес все создание на момент загрузки игры. Грузится дольше но зато во время игры никаких тормазов.Выводы: QT под Android не смотря на множество непонятных багов и недоделок вполне пригоден для разработки под Android. Особенно если вы уже знакомы с QT. Но надо быть готовым к тому что вы встретите проблемы на которые еще нет ответов в сети.Одним из минусов, который так и не удалось решить, стал большой размер приложения:

apk ~ 10 MB установленное приложение ~38 MB Самое печальное, что примерно 70% это библиотеки самого qt и с этим придется мириться. С другой стороны для современных устройств такой размер уже не так критичен.

© Habrahabr.ru