I.
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: 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 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:
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:
Upload image