Qt 5.2, From a Sketch to Google Play

Mobile Development

Dear colleagues,

I’ve been told about Qt 5.2 and its new feature of simple and quick creation of cross-platform applications for Android and iOS. I opened Qt website and watched a great video, in which Hello World application was developed for Android and iOS in 10 minutes. I was impressed.

So I decided to take up mobile development. I had a plan of working my way up from a wish to develop an application to its publication in Google Play.

At the same time I learnt about the famous Flappy Bird and that its developer had decided to delete his application. So I wanted to develop another copy of thus game. But my main goal, of course, was to try out new Qt facilities.

C++ or Javascript?

I like C++, but I didn’t dare to write such a small thing on it. C++ makes developer be very thoughtful and attentive so that he wouldn’t “shoot himself in the foot”, while I just wanted to quickly create an operable project in XP style. I chose QML and JavaScript so that I also could look into Digia as well.

The First Sketch

After I had setup Qt, read documentation on qml, took a look at Flappy Bird game mechanics (I hadn’t heard about it) I made the first sketch of the game. I used simple NumberAnimations in order to mimic bird’s flight and poles movement. Everything operated well, but I had some questions:

  • how to carry out a collision check
  • how to scale graphics and physics for different definitions
  • what should I do about design
  • what should I do about game sounds
  • how to build-in monetization
Physics

The only right solution for the two mentioned problems was using Box2D. Plug-in for qml had been quickly found — github.com/qml-box2d/qml-box2d. After few days of experimenting and reading box2d documentation I rewrote everything and it operated. But I had more problems ahead.

Sound

I decided not to use the background music as I don’t like it and wouldn’t be able to choose a nice variant, and of course I couldn’t make it myself. So I chose 3 sounds from www.freesound.org: wing beat, collision and new point. For the playback I used a good example of Flappy Bird development from V-play by the Audio Manager.

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();
  }
}

playback:

audioManager.play( audioManager.wing);

Everything worked well on the desktop machine, while the application fell on the phone. The reason turned out to be quite trifle, the following should be added to .pro file:

QT += multimedia

You’ll understand later, in the bugs description, why there are two Sound Effects and sfx_silence.

Scaling

Scaling was standard. 480x800 has been used as the basis resolution (it’s small but the most popular for the moment). So I strictly assigned the bird and poles size relative to it. Then I calculated the scale factor for the current resolution relatively to the reference one. Then all sizes needing scaling were multiplied by it. The following example helped a lot: bitbucket.org/wearyinside/cute-plane, though not all of the problems had been solved there.

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)

All physical objects are scaled linearly, while the text at such scaling at high definitions broke all frames. I had to use the scaling by a square root of the main scale factor.

Design

I’m a programmer, so design is a deep forest for me. But having read different articles on the topic, I chose vector graphics and Inkscape editor. I drew a cartoonish bird in 2 days. At first a made a sketch on paper and drew several possible variants. Then the best of them was transferred to svg. All other images were made in vector format. In order to use svg files in qml the following should be added to .pro file:

QT += xml svg
QTPLUGIN += qsvg

Monetization

When the main part had been written, a question about monetization arose. Though the given project is just a test, we still wanted to know about monetization as well. I decided to to use admob. That’s where serious problems arose. It turned out that there are no plug-ins for qt/qml to be built in admob. I found an out-of-date implementation of qadmob and closed implementation of V-play AdMob plugin. I even thought about ending the project. Having rummaged everything in the Internet I realized that I had to dive in Qt source code and understand how it’s made for Android. In four days, a test ad banner was in the game. Here’s an example of how you can do it: github.com/AlexMarlo/AdMob-Qt5.2-Example. It took me a week to display the banner.

Bugs

It was obvious that all the main parts had been done and I had to fix small bugs.

Memory Leak

100 MB of memory ware leaking every minute of the game. After I had commented qml code and check the results, the problem was found. It turned out that memory was leaking at such assignment:

linearVelocity.x = 220;
        linearVelocity.y = -420;

having chamged this variant to the following one:

var flyImpulseVelosityY = -420 * measure;
        var flyImpulseVelosityX = 220 * measure;
        var impulse;
        impulse = Qt.point( flyImpulseVelosityX, flyImpulseVelosityY);
        applyLinearImpulse( impulse, getWorldCenter());

leaks stopped. It’s similar to the problem with qml-box2d, but I didn’t dig deeper.

Sound Fallout

At a very often playback the sound often fell out till the other sound file was played. That’s where two Sound Effects appeared. It occurred on Androids only. In order to get rid of the sound fallout, Sound Effects are played in turns. The problem seemed to be in Qt itself.

The application crashed on assert in Box2D with the switched-on scaling
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)

The problem lies in the first two lines. Until qml is designed, the element in which Screen is called, Screen.width and Screen.height will be zero. So the scale factor is initially zero and box2d ends the application on asset, as physical objects can’t be on zero size. It was fixed by dynamically creating the objects at the moment when the scale factor has not zero value.

Volume Control Isn’t Working

As it turned out, in the current Qt version for Android, volume control buttons aren’t working. All the pieces of advice I’d found were to handle the button press in Activity, and that’s exactly what I did.

@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);
    }

Testing on Different Devices and Bugs Again

Then it was the time for testing the app on different Androids. Here’s the approximate list of devices: - 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

All the problems arose on Samsungs. The better the phone was the worse were the bugs. According to www.appbrain.com/stats/top-android-phones statistics bugs should never be left on Samsungs.

Lagging When the First Sound Was Played

Due to some strange reason the sound playback via Sound Effect hung and then everything continued operating as usual. It was especially evident on Samsung Galaxy S3. There was no such problem on other manufacturer devices. That’s where sfx_silence.wav appeared. It’s an empty sound file which is played during the game loading.

Lagging During the Dynamic Creation of Box2D Objects

The next problem appeared as Box2D objects had been created dynamically for proper scaling. This creation was super slow on Samsungs and especially on Samsung Galaxy S3.

Creating the ground objects:

Nexus One 97 ms
Samsung Galaxy S Duos 986 ms
The difference is evident, but I didn’t go into details of qml-box2d and Box2D implementations, I just shifted all creation to the moment of the game loading. It loads longer, but there are no hangs during the game.

Summary

Qt for Android is quite good for development, despite many strange bugs and imperfections. Especially if you already know Qt. But you should be prepared to meet problems with no ready solutions in the Internet.

One of drawbacks I couldn’t solve is the big size of the application itself:

  • apk ~ 10 MB
  • setup application ~38 MB

70% are Qt libraries and we can not but accept it. But for modern devices such size isn’t that critical.

Screenshots of the result:

Comments

  1. Hi, interesting project and good work. Actually, I tried to compile the AdMob example with Qt5.3 under Win8.1 and got the following build error:

    BUILD FAILED C:\Android\sdk\tools\ant\build.xml:577: google-play-services_lib resolve to a path with no project.properties file for project C:\projects\build-AdMobQtSample-Android_for_armeabi_v7a_GCC_4_9_Qt_5_4_0-Release\android-build

    Do you have any idea, what the system is expecting here? What kind of «project.properties» am I supposed to supply?

    Any help is greatly appreciated!

    Best, CBA

3,751

Ropes — Fast Strings

Most of us work with strings one way or another. There’s no way to avoid them — when writing code, you’re doomed to concatinate strings every day, split them into parts and access certain characters by index. We are used to the fact that strings are fixed-length arrays of characters, which leads to certain limitations when working with them. For instance, we cannot quickly concatenate two strings. To do this, we will at first need to allocate the required amount of memory, and then copy there the data from the concatenated strings.