summaryrefslogtreecommitdiffstats
path: root/doc/src/declarative
diff options
context:
space:
mode:
authorAlan Alpert <alan.alpert@nokia.com>2009-10-07 03:12:24 (GMT)
committerAlan Alpert <alan.alpert@nokia.com>2009-10-07 03:12:24 (GMT)
commit19d080d319dccac15654294af80530bed9ef11ea (patch)
tree239a0a0b4b3dc5905b0ecbee1416c62e170f8217 /doc/src/declarative
parent5b77922f3782de4b96d6cf07ebb88419de130eac (diff)
downloadQt-19d080d319dccac15654294af80530bed9ef11ea.zip
Qt-19d080d319dccac15654294af80530bed9ef11ea.tar.gz
Qt-19d080d319dccac15654294af80530bed9ef11ea.tar.bz2
Switch Same Game tutorial to using snippets properly
Diffstat (limited to 'doc/src/declarative')
-rw-r--r--doc/src/declarative/advtutorial1.qdoc80
-rw-r--r--doc/src/declarative/advtutorial2.qdoc76
-rw-r--r--doc/src/declarative/advtutorial3.qdoc166
-rw-r--r--doc/src/declarative/advtutorial4.qdoc80
-rw-r--r--doc/src/declarative/pics/declarative-adv-tutorial4.gifbin0 -> 58337629 bytes
-rw-r--r--doc/src/declarative/pics/declarative-adv-tutorial4.pngbin273086 -> 0 bytes
6 files changed, 36 insertions, 366 deletions
diff --git a/doc/src/declarative/advtutorial1.qdoc b/doc/src/declarative/advtutorial1.qdoc
index b940986..48b32cd 100644
--- a/doc/src/declarative/advtutorial1.qdoc
+++ b/doc/src/declarative/advtutorial1.qdoc
@@ -10,46 +10,7 @@ The first step is to create the items in your application. In Same Game we have
Here is the QML code for the basic elements. The game window:
-\code
-import Qt 4.6
-
-Rectangle {
- id: Screen
- width: 490; height: 720
-
- SystemPalette { id: activePalette; colorGroup: Qt.Active }
-
- Item {
- width: parent.width; anchors.top: parent.top; anchors.bottom: ToolBar.top
-
- Image {
- id: background
- anchors.fill: parent; source: "pics/background.png"
- fillMode: "PreserveAspectCrop"
- }
- }
-
- Rectangle {
- id: ToolBar
- color: activePalette.window
- height: 32; width: parent.width
- anchors.bottom: Screen.bottom
-
- Button {
- id: btnA; text: "New Game"; onClicked: print("Implement me!");
- anchors.left: parent.left; anchors.leftMargin: 3
- anchors.verticalCenter: parent.verticalCenter
- }
-
- Text {
- id: Score
- text: "Score: Who knows?"; font.bold: true
- anchors.right: parent.right; anchors.rightMargin: 3
- anchors.verticalCenter: parent.verticalCenter
- }
- }
-}
-\endcode
+\snippet declarative/tutorials/samegame/samegame1/samegame.qml 0
This gives you a basic game window, with room for the game canvas. A new game
button and room to display the score. The one thing you may not recognize here
@@ -59,49 +20,14 @@ feel you would use a QPushButton). Since we want a fully QML button, and the Fx
primitives don't include a button, we had to write our own. Below is the code
which we wrote to do this:
-\code
-import Qt 4.6
-
-Rectangle {
- id: Container
-
- signal clicked
- property string text: "Button"
-
- color: activePalette.button; smooth: true
- width: txtItem.width + 20; height: txtItem.height + 6
- border.width: 1; border.color: activePalette.darker(activePalette.button); radius: 8;
-
- gradient: Gradient {
- GradientStop {
- id: topGrad; position: 0.0
- color: if (mr.pressed) { activePalette.dark } else { activePalette.light } }
- GradientStop { position: 1.0; color: activePalette.button }
- }
-
- MouseRegion { id: mr; anchors.fill: parent; onClicked: Container.clicked() }
+\snippet declarative/tutorials/samegame/samegame1/Button.qml 0
- Text {
- id: txtItem; text: Container.text; anchors.centerIn: Container; color: activePalette.buttonText
- }
-}
-\endcode
Note that this Button component was written to be fairly generic, in case we
want to use a similarly styled button later.
And here is a simple block:
-\code
-import Qt 4.6
-Item {
- id:block
-
- Image { id: img
- source: "pics/redStone.png";
- anchors.fill: parent
- }
-}
-\endcode
+\snippet declarative/tutorials/samegame/samegame1/Block.qml 0
Since it doesn't do anything yet it's very simple, just an image. As the
tutorial progresses and the block starts doing things the file will become
diff --git a/doc/src/declarative/advtutorial2.qdoc b/doc/src/declarative/advtutorial2.qdoc
index c17f9c4..2d2fe19 100644
--- a/doc/src/declarative/advtutorial2.qdoc
+++ b/doc/src/declarative/advtutorial2.qdoc
@@ -13,68 +13,7 @@ in the ECMA script, as opposed to using a Repeater.
This adds enough script to justify a new file, samegame.js, the intial version
of which is shown below
-\code
-//Note that X/Y referred to here are in game coordinates
-var maxX = 10;//Nums are for tileSize 40
-var maxY = 15;
-var tileSize = 40;
-var maxIndex = maxX*maxY;
-var board = new Array(maxIndex);
-var tileSrc = "Block.qml";
-var component;
-
-//Index function used instead of a 2D array
-function index(xIdx,yIdx) {
- return xIdx + (yIdx * maxX);
-}
-
-function initBoard()
-{
- //Calculate board size
- maxX = Math.floor(background.width/tileSize);
- maxY = Math.floor(background.height/tileSize);
- maxIndex = maxY*maxX;
-
- //Initialize Board
- board = new Array(maxIndex);
- for(xIdx=0; xIdx<maxX; xIdx++){
- for(yIdx=0; yIdx<maxY; yIdx++){
- board[index(xIdx,yIdx)] = null;
- createBlock(xIdx,yIdx);
- }
- }
-}
-
-function createBlock(xIdx,yIdx){
- if(component==null)
- component = createComponent(tileSrc);
-
- // Note that we don't wait for the component to become ready. This will
- // only work if the block QML is a local file. Otherwise the component will
- // not be ready immediately. There is a statusChanged signal on the
- // component you could use if you want to wait to load remote files.
- if(component.isReady){
- dynamicObject = component.createObject();
- if(dynamicObject == null){
- print("error creating block");
- print(component.errorsString());
- return false;
- }
- dynamicObject.parent = background;
- dynamicObject.x = xIdx*tileSize;
- dynamicObject.y = yIdx*tileSize;
- dynamicObject.width = tileSize;
- dynamicObject.height = tileSize;
- board[index(xIdx,yIdx)] = dynamicObject;
- }else{//isError or isLoading
- print("error loading block component");
- print(component.errorsString());
- return false;
- }
- return true;
-}
-
-\endcode
+\snippet declarative/tutorials/samegame/samegame2/samegame.js 0
The gist of this code is that we create the blocks dynamically, as many as will fit, and then store them in an array for future reference. The 'initBoard' function will be hooked up to the new game button soon, and should be fairly straight forward.
@@ -93,18 +32,11 @@ You now have the code to create a field of blocks dynamically, like below:
To hook this code up to the 'New Game' button, you alter it as below:
-\code
-Button {
- id: btnA; text: "New Game"; onClicked: initBoard() ;
- anchors.left: parent.left; anchors.leftMargin: 3
- anchors.verticalCenter: parent.verticalCenter
-}
-\endcode
+\snippet declarative/tutorials/samegame/samegame2/samegame.qml 1
We have just replaced the 'onClicked: print("Implement me!")' with 'onClicked: initBoard()'. Note that in order to have the function available, you'll need to include the script in the main file, by adding a script element to it.
-\code
- Script { source: "samegame.js" }
-\endcode
+
+\snippet declarative/tutorials/samegame/samegame2/samegame.qml 2
With those two changes, and the script file, you are now dynamically creating a field of blocks you can play with. They don't do anything now though; the next chapter will add the game mechanics.
diff --git a/doc/src/declarative/advtutorial3.qdoc b/doc/src/declarative/advtutorial3.qdoc
index 1c50555..635054b 100644
--- a/doc/src/declarative/advtutorial3.qdoc
+++ b/doc/src/declarative/advtutorial3.qdoc
@@ -16,89 +16,25 @@ The main change was adding the following game logic functions:
As this is a tutorial about QML, not game design, these functions will not be discussed in detail. The game logic here was written in script, but it could have been written in C++ and had these functions exposed just as well (in fact, probably faster). The interfacing between these funcions and QML is of interest though. Of these functions, only handleClick and victoryCheck interface closely with the QML. Those functions are shown below (the rest are still in the code for this tutorial located at $QTDIR/examples/declarative/tutorials/samegame).
-\code
-function handleClick(x,y)
-{
- xIdx = Math.floor(x/gameCanvas.tileSize);
- yIdx = Math.floor(y/gameCanvas.tileSize);
- if(xIdx >= maxX || xIdx < 0 || yIdx >= maxY || yIdx < 0)
- return;
- if(board[index(xIdx, yIdx)] == null)
- return;
- //If it's a valid tile, remove it and all connected (does nothing if it's not connected)
- floodFill(xIdx,yIdx, -1);
- if(fillFound <= 0)
- return;
- gameCanvas.score += (fillFound - 1) * (fillFound - 1);
- shuffleDown();
- victoryCheck();
-}
-
-function victoryCheck()
-{
- //awards bonuses for no tiles left
- deservesBonus = true;
- for(xIdx=maxX-1; xIdx>=0; xIdx--)
- if(board[index(xIdx, maxY - 1)] != null)
- deservesBonus = false;
- if(deservesBonus)
- gameCanvas.score += 500;
- //Checks for game over
- if(deservesBonus || !(floodMoveCheck(0,maxY-1, -1)))
- dialog.show("Game Over. Your score is " + gameCanvas.score);
-}
-\endcode
+\snippet declarative/tutorials/samegame/samegame3/samegame.js 1
+\snippet declarative/tutorials/samegame/samegame3/samegame.js 2
You'll notice them referring to the 'gameCanvas' item. This is an item that has been added to the QML for easy interfacing. It is placed next to the background image and replaces the background as the item to create the blocks in. Its code is shown below:
-\code
- Item {
- id: gameCanvas
- property int score: 0
- property int tileSize: 40
-
- z: 20; anchors.centerIn: parent
- width: parent.width - (parent.width % tileSize);
- height: parent.height - (parent.height % tileSize);
-
- MouseRegion {
- id: gameMR
- anchors.fill: parent; onClicked: handleClick(mouse.x,mouse.y);
- }
- }
-\endcode
+
+\snippet declarative/tutorials/samegame/samegame3/samegame.qml 1
This item is the exact size of the board, contains a score property, and a mouse region for input. The blocks are now created as its children, and its size is used as the noe determining board size. Since it needs to bind its size to a multiple of tileSize, tileSize needs to be moved into a QML property and out of the script file. It can still be accessed from the script.
The mouse region simply calls handleClick(), which deals with the input events.Should those events cause the player to score, gameCanvas.score is updated. The score display text item has also been changed to bind its text property to gamecanvas.score. Note that if score was a global variable in the samegame.js file yo ucould not bind to it. You can only bind to QML properties.
victoryCheck() mostly just updates score. But it also pops up a dialog saying 'Game Over' when the game is over. In this example we wanted a pure-QML, animated dialog, and since the Fx primitives set doesn't contain one, we wrote our own. Below is the code for the Dialog element, note how it's designed so as to be quite usable imperatively from within the script file:
-\code
-import Qt 4.6
-
-Rectangle {
- id: page
- function forceClose() {
- page.closed();
- page.opacity = 0;
- }
- function show(txt) {
- MyText.text = txt;
- page.opacity = 1;
- }
- signal closed();
- color: "white"; border.width: 1; width: MyText.width + 20; height: 60;
- opacity: 0
- opacity: Behavior {
- NumberAnimation { duration: 1000 }
- }
- Text { id: MyText; anchors.centerIn: parent; text: "Hello World!" }
- MouseRegion { id: mr; anchors.fill: parent; onClicked: forceClose(); }
-}
-\endcode
+
+\snippet declarative/tutorials/samegame/samegame3/Dialog.qml 0
+
And this is how it's used in the main QML file:
-\code
- Dialog { id: dialog; anchors.centerIn: parent; z: 21 }
-\endcode
+
+\snippet declarative/tutorials/samegame/samegame3/samegame.qml 2
+
Combined with the line of code in victoryCheck, this causes a dialog to appear when the game is over, informing the user of that fact.
We now have a working game! The blocks can be clicked, the player can score, and the game can end (and then you start a new one). Below is a screenshot of what has been accomplished so far:
@@ -107,87 +43,11 @@ We now have a working game! The blocks can be clicked, the player can score, and
Here is the QML code as it is now for the main file:
-\code
-import Qt 4.6
-
-Rectangle {
- id: Screen
- width: 490; height: 720
-
- SystemPalette { id: activePalette; colorGroup: Qt.Active }
- Script { source: "samegame.js" }
-
- Item {
- width: parent.width; anchors.top: parent.top; anchors.bottom: ToolBar.top
-
- Image {
- id: background
- anchors.fill: parent; source: "pics/background.png"
- fillMode: "PreserveAspectCrop"
- }
-
- Item {
- id: gameCanvas
- property int score: 0
- property int tileSize: 40
-
- z: 20; anchors.centerIn: parent
- width: parent.width - (parent.width % tileSize);
- height: parent.height - (parent.height % tileSize);
-
- MouseRegion {
- id: gameMR
- anchors.fill: parent; onClicked: handleClick(mouse.x,mouse.y);
- }
- }
- }
-
- Dialog { id: dialog; anchors.centerIn: parent; z: 21 }
-
- Rectangle {
- id: ToolBar
- color: activePalette.window
- height: 32; width: parent.width
- anchors.bottom: Screen.bottom
-
- Button {
- id: btnA; text: "New Game"; onClicked: initBoard();
- anchors.left: parent.left; anchors.leftMargin: 3
- anchors.verticalCenter: parent.verticalCenter
- }
-
- Text {
- id: Score
- text: "Score: " + gameCanvas.score; font.bold: true
- anchors.right: parent.right; anchors.rightMargin: 3
- anchors.verticalCenter: parent.verticalCenter
- }
- }
-}
-\endcode
+\snippet declarative/tutorials/samegame/samegame3/samegame.qml 0
And the code for the block:
-\code
-import Qt 4.6
-
-Item {
- id:block
- property int type: 0
-
- Image { id: img
- source: {
- if(type == 0){
- "pics/redStone.png";
- } else if(type == 1) {
- "pics/blueStone.png";
- } else {
- "pics/greenStone.png";
- }
- }
- anchors.fill: parent
- }
-}
-\endcode
+
+\snippet declarative/tutorials/samegame/samegame3/Block.qml 0
The game works, but it's a little boring right now. Where's the smooth animated transitions? Where's the high scores? If you were a QML expert you could have written these in for the first iteration, but in this tutorial they've been saved until the next chapter - where your application becomes alive!
diff --git a/doc/src/declarative/advtutorial4.qdoc b/doc/src/declarative/advtutorial4.qdoc
index 5ad1ec3..291d2f2 100644
--- a/doc/src/declarative/advtutorial4.qdoc
+++ b/doc/src/declarative/advtutorial4.qdoc
@@ -10,6 +10,7 @@ If you compare the samegame3 directory with samegame4, you'll noticed that we've
\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 Follow element. By having the script set targetX and targetY, instead of x and y directly, we can set the x and y of the block to a follow. 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 Block.qml:
+
\code
property int targetX: 0
property int targetY: 0
@@ -20,32 +21,11 @@ The most vital animations are that the blocks move fluidly around the board. QML
We also have to change the samegame.js code, so that wherever it was setting the x or y it now sets targetX and 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 x Follow (but not the y follow) and only enable it after we've set the x in the createBlock function. The above snippet now becomes:
-\code
- property bool spawned: false
- property int targetX: 0
- property int targetY: 0
-
- x: SpringFollow { enabled: spawned; source: targetX; spring: 2; damping: 0.2 }
- y: SpringFollow { source: targetY; spring: 2; damping: 0.2 }
-\endcode
+\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 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:
-\code
- Image { id: img
- source: {
- if(type == 0){
- "pics/redStone.png";
- } else if(type == 1) {
- "pics/blueStone.png";
- } else {
- "pics/greenStone.png";
- }
- }
- opacity: 0
- opacity: Behavior { NumberAnimation { properties:"opacity"; duration: 200 } }
- anchors.fill: parent
- }
-\endcode
+
+\snippet declarative/tutorials/samegame/samegame4/content/BoomBlock.qml 2
Note that the '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 Block.qml:
\code
@@ -62,53 +42,25 @@ Note that the 'opacity: 0' makes it start out transparent. We could set the opac
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 floodFill function).
The least vital animations are a cool-looking particle effect when they get destroyed. First we create a Particles Element in the block, like so:
-\code
- Particles { id: particles
- width:1; height:1; anchors.centerIn: parent; opacity: 0
- lifeSpan: 700; lifeSpanDeviation: 600; count:0; streamIn: false
- angle: 0; angleDeviation: 360; velocity: 100; velocityDeviation:30
- source: {
- if(type == 0){
- "pics/redStar.png";
- } else if (type == 1) {
- "pics/blueStar.png";
- } else {
- "pics/greenStar.png";
- }
- }
- }
-\endcode
+
+\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 count is set to zero.
-We next extend the 'dying' state, which triggers the particles by setting the count to non-zero. The code for that state looks like this:
-\code
- State{ name: "DeathState"; when: dying == true
- PropertyChanges { target: particles; count: 50 }
- PropertyChanges { target: particles; opacity: 1 }
- PropertyChanges { target: particles; emitting: false } // i.e. emit only once
- PropertyChanges { target: img; opacity: 0 }
- }
-\endcode
-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.
+We next extend the 'dying' state, which triggers the particles by setting the count to non-zero. 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 Web-based High Scores
Another extension we might want for the game is some way of storing and retriveing high scores. In this tutorial we'll show you how to integrate a web enabled high score storage into your QML application. 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.
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, if the player entered their name we can send the data to the web service in the followign snippet out of the script file:
-\code
-function sendHighScore(name) {
- var postman = new XMLHttpRequest()
- var postData = "name="+name+"&score="+gameCanvas.score
- +"&gridSize="+maxX+"x"+maxY +"&time="+Math.floor(timer/1000);
- postman.open("POST", scoresURL, true);
- postman.onreadystatechange = function() {
- if (postman.readyState == postman.DONE) {
- dialog.show("Your score has been uploaded.");
- }
- }
- postman.send(postData);
-}
-\endcode
+
+\snippet declarative/tutorials/samegame/samegame4/content/samegame.js 1
This is the same 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.
diff --git a/doc/src/declarative/pics/declarative-adv-tutorial4.gif b/doc/src/declarative/pics/declarative-adv-tutorial4.gif
new file mode 100644
index 0000000..a67666d
--- /dev/null
+++ b/doc/src/declarative/pics/declarative-adv-tutorial4.gif
Binary files differ
diff --git a/doc/src/declarative/pics/declarative-adv-tutorial4.png b/doc/src/declarative/pics/declarative-adv-tutorial4.png
deleted file mode 100644
index 03c9f46..0000000
--- a/doc/src/declarative/pics/declarative-adv-tutorial4.png
+++ /dev/null
Binary files differ