1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
|
/****************************************************************************
**
** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies).
** Contact: Qt Software Information (qt-info@nokia.com)
**
** This file is part of the documentation of the Qt Toolkit.
**
** $QT_BEGIN_LICENSE:LGPL$
** No Commercial Usage
** This file contains pre-release code and may not be distributed.
** You may use this file in accordance with the terms and conditions
** contained in the either Technology Preview License Agreement or the
** Beta Release License Agreement.
**
** GNU Lesser General Public License Usage
** Alternatively, this file may be used under the terms of the GNU Lesser
** General Public License version 2.1 as published by the Free Software
** Foundation and appearing in the file LICENSE.LGPL included in the
** packaging of this file. Please review the following information to
** ensure the GNU Lesser General Public License version 2.1 requirements
** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
**
** In addition, as a special exception, Nokia gives you certain
** additional rights. These rights are described in the Nokia Qt LGPL
** Exception version 1.0, included in the file LGPL_EXCEPTION.txt in this
** package.
**
** GNU General Public License Usage
** Alternatively, this file may be used under the terms of the GNU
** General Public License version 3.0 as published by the Free Software
** Foundation and appearing in the file LICENSE.GPL included in the
** packaging of this file. Please review the following information to
** ensure the GNU General Public License version 3.0 requirements will be
** met: http://www.gnu.org/copyleft/gpl.html.
**
** If you are unsure which license is appropriate for your use, please
** contact the sales department at qt-sales@nokia.com.
** $QT_END_LICENSE$
**
****************************************************************************/
/*!
\page advtutorial4.html
\example declarative/tutorials/samegame/samegame4
\title Advanced Tutorial 4 - Finishing Touches
Now we're going to do two things to liven the game up. Animate the blocks and add a web-based high score system.
If you compare the \c samegame3 directory with \c samegame4, you'll noticed that we've cleaned the directory structure up.
We now have a lot of files, and so they've been split up into folders - the most notable one being a content folder
which we've placed all the QML but the main file.
\section2 Animated Blocks
The most vital animations are that the blocks move fluidly around the board. QML has many tools for fluid behavior,
and in this case we're going to use the \l SpringFollow element. By having the script set \c targetX and \c targetY, instead of \c x
and \c y directly, we can set the \c x and \c y of the block to a follow. \l SpringFollow is a property value source, which means
that you can set a property to be one of these elements and it will automatically bind the property to the element's value.
The SpringFollow's value follows another value over time, when the value it is tracking changes the SpringFollow's
value will also change, but it will move smoothly there over time with a spring-like movement (based on the spring
parameters specified). This is shown in the below snippet of code from \c Block.qml:
\code
property int targetX: 0
property int targetY: 0
x: SpringFollow { source: targetX; spring: 2; damping: 0.2 }
y: SpringFollow { source: targetY; spring: 2; damping: 0.2 }
\endcode
We also have to change the \c{samegame.js} code, so that wherever it was setting the \c x or \c y it now sets \c targetX and \c targetY
(including when creating the block). This simple change is all you need to get spring moving blocks that no longer teleport
around the board. If you try doing just this though, you'll notice that they now never jump from one point to another, even in
the initialization! This gives an odd effect of having them all jump out of the corner (0,0) on start up. We'd rather that they
fall down from the top in rows. To do this, we disable the \c x follow (but not the \c y follow) and only enable it after we've set
the \c x in the \c createBlock function. The above snippet now becomes:
\snippet declarative/tutorials/samegame/samegame4/content/BoomBlock.qml 1
The next-most vital animation is a smooth exit. For this animation, we'll use a \l Behavior element. A Behavior is also a property
value source, and it is much like SpringFollow except that it doesn't model the behavior of a spring. You specify how a Behavior
transitions using the standard animations. As we want the blocks to smoothly fade in and out we'll set a Behavior on the block
image's opacity, like so:
\snippet declarative/tutorials/samegame/samegame4/content/BoomBlock.qml 2
Note that the \c{opacity: 0} makes it start out transparent. We could set the opacity in the script file when we create the blocks,
but instead we use states (as this is useful for the next animation we'll implement). The below snippet is set on the root
element of \c{Block.qml}:
\code
property bool dying: false
states: [
State{ name: "AliveState"; when: spawned == true && dying == false
PropertyChanges { target: img; opacity: 1 }
}, State{ name: "DeathState"; when: dying == true
PropertyChanges { target: img; opacity: 0 }
}
]
\endcode
Now it will automatically fade in, as we set spawned to true already when implementing the block movement animations.
To fade out, we set 'dying' to true instead of setting opacity to 0 when a block is destroyed (in the \c floodFill function).
The least vital animations are a cool-looking particle effect when they get destroyed. First we create a \l Particles element in
the block, like so:
\snippet declarative/tutorials/samegame/samegame4/content/BoomBlock.qml 3
To fully understand this you'll want to look at the Particles element documentation, but it's important to note that emissionRate is set
to zero, so that no particles are emitted normally.
We next extend the 'dying' state, which creates a burst of particles by calling the burst method on the particles element. The code for the states now look
like this:
\snippet declarative/tutorials/samegame/samegame4/content/BoomBlock.qml 4
And now the game should be beautifully animated and smooth, with a subtle (or not-so-subtle) animation added for all of the
player's actions. The end result is shown below:
\image declarative-adv-tutorial4.gif
\section2 Offline High Scores
Another extension we might want for the game is some way of storing and retrieving high scores. This tutorial contains both online and offline high score storage.
For better high score data, we want the name and time of the player. The time is obtained in the script fairly simply, but we
have to ask the player for their name. We thus re-use the dialog QML file to pop up a dialog asking for the player's name (and
if they exit this dialog without entering it they have a way to opt out of posting their high score). When the dialog is closed we store the name and high score, using the code below.
\snippet declarative/tutorials/samegame/samegame4/content/samegame.js 2
For offline storage, we use the HTML 5 offline storage javascript API to maintain a persistant SQL database unique to this application. This first line in this function calls the function for the web-based high scores, described later, if it has been setup. Next we create an offline storage database for the high scores using openDatabase and prepare the data and SQL query that we want to use to save it. The offline storage API uses SQL queries for data manipulation and retrival, and in the db.transaction call we use three SQL queries to initialize the database (if necessary), and then add to and retrieve high scores. To use the returned data, we turn it into a string with one line per row returned, and show a dialog containing that string. For a more detailed explanation of the offline storage API in QML, consult the global object documentation.
This is one way of storing and displaying high scores locally, but not the only way. A more complex alternative would have been to create a high score dialog component, and pass the results to it for processing and display (instead of resusing the Dialog). This would allow a more themable dialog that could present the high scores better. If your QML is the UI for a C++ application, you could also have passed the score to a C++ function to store it locally in a variety of ways, including a simple format without SQL.
\section2 Web-based High Scores
You've seen how to store high scores locally, but it is also easy to integrate a web enabled high score storage into your QML application. This tutorial also shows you how to communicate the high scores to a web server. The implementation we've done is very
simple - the high score data is posted to a php script running on a server somewhere, and that server then stores it and
displays it to visitors. You could request an XML or QML file from that same server, which contained and displayed the scores,
but that's beyond the scope of this tutorial. The php script we've used is available in the examples directory.
if the player entered their name we can send the data to the web service in the following snippet out of the script file:
\snippet declarative/tutorials/samegame/samegame4/content/samegame.js 1
This is the same \c XMLHttpRequest() as you'll find in browser javascript, and can be used in the same way to dynamically get XML
or QML from the web service to display the high scores. We don't worry about the response here though, we just post the high
score data to the web server. If it had returned a QML file (or a URL to a QML file) you could instantiate it in much the same
way as you did the blocks.
An alternate way to access and submit web-based data would be to use QML elements designed for this purpose - XmlListModel
makes it very easy to fetch and display XML based data such as RSS in a QML application (see the Flickr demo for an example).
By following this tutorial you've now ben shown how to write a fully functional application in QML, with the application logic
written in a script file and with both many fluid animations and being web-enabled. Congratulations, you should now be skilled
enough to write your own QML applications.
[Previous: \l {Advanced Tutorial 3 - Implementing the Game Logic}] [\l {advtutorial.html}{Advanced Tutorial}]
*/
|