/**************************************************************************** ** ** 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 \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 slide 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 and destroy 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 or in another SQL database. \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 in this case, 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 entire applications in QML. [Previous: \l {Advanced Tutorial 3 - Implementing the Game Logic}] [\l {advtutorial.html}{Advanced Tutorial}] */