1. 程式人生 > >如何使用QML動態產生Component來完成我們的氣球遊戲 (1)

如何使用QML動態產生Component來完成我們的氣球遊戲 (1)

在這篇文章裡,我們將學習如何使用QML的Qt.createComponent來動態生成我們所需要的Component。這是一個有趣的練習。我希望大家能跟隨我一步一步地完成這個練習。最終使得大家對QML應用有更多的認識。這篇文章中我們也將使用我們的sensor來完成我們其中的一部分功能。在練習之前請大家先去按照安裝Ubuntu SDK來安裝好我們的環境。

1)使用Qt Creator建立一個最基本的應用

我們首先選擇一個“App with Simple UI"的模版來建立我們的最基本的應用。在建立應用的時候,由於應用的package名字中不能出現大寫的字母,所以我們選擇使用小寫的“balloon
"來作為我們的工程的名字:
緊接著,我們填入所需要的資訊來完成我們的應用。
把應用的大小設為如下的值:
    width: units.gu(50)
    height: units.gu(75)


執行我們的應用:
我們看到,這個應用沒有什麼太多的內容。我們可以嘗試點選按鈕,然後看見方框中的文字發生改變。

2)新增Balloon Component

我們按照如下的圖,用右鍵點選專案"balloon",並選擇“Add New"。
我們選擇建立一個叫做“Balloon.qml”的檔案。記住第一個字母為大寫的字母。
至此我們已經建立了一個名字叫做"Balloon.qml"的component,雖然現在它做不了什麼。為了測試我們剛剛已經建立好的Balloon Component,我們把我們的Balloon在main.qml中創建出來。現在我們來修改main.qml檔案:
import QtQuick 2.0
import Ubuntu.Components 1.1
import "components"

MainView {
    // objectName for functional testing purposes (autopilot-qt5)
    objectName: "mainView"

    // Note! applicationName needs to match the "name" field of the click manifest
    applicationName: "com.ubuntu.developer.liu-xiao-guo.balloon"

    //automaticOrientation: true

    // Removes the old toolbar and enables new features of the new header.
    useDeprecatedToolbar: false

    width: units.gu(50)
    height: units.gu(75)

    Page {
        title: i18n.tr("Balloon")

        Balloon {
            width: parent.width/3
            height: parent.height/3
            x: (parent.width - width) / 2
            y: (parent.height - height) /2
        }
    }
}

這裡我們把Balloon放到了我們主介面的正中央的位置。重新執行我們的程式,我們可以看到:
顯然,我們可以看到,我們的Balloon component裡沒有任何的東西。只是一個白色方框在那裡。但是,至少,我們可以看到確實Balloon已經能被正確地呼叫。在下面的章節中,我們來一步一步地完成我們的Balloon的功能。

3)完成Balloon模組

請到地址https://github.com/liu-xiao-guo/balloon下載應用,並拷貝應用的"images"目錄到我們已經建立好的專案中(別的檔案不要拷貝)。這樣使得我們的應用有一個叫做"images"的目錄,裡面有我們想要的圖片。

開啟我們的Balloon.qml檔案。我們知道Balloon component其實是一個Image的item。我們首先把它設計為如下:
import QtQuick 2.0

Image {
    id: balloon
    width: 100
    height: 250

    source: "images/red.svg"
}

我們執行程式,我們發現它的結果如下:
顯然,我們已經看到了我們所需要看到的氣球了。只是它的顏色是固定的紅色。我們想顯示不同的氣球。這時我們需要加入一個顏色的屬性。我們的Balloon.qml的程式碼如下:
Image {
    id: balloon
    width: 100
    height: 250
    property string color: "red"

    source: "images/" + color + ".svg"
}

預設的顏色我紅色(在沒有定義的情況下)。我們可以嘗試改變我們的main.qml檔案。加入color屬性:
        Balloon {
            color: "green"
            width: parent.width/3
            height: parent.height/3
            x: (parent.width - width) / 2
            y: (parent.height - height) /2
        }

再重新執行應用:
我們看見了一個綠色的氣球。顯然它的顏色屬性是起作用的。為了能夠使我們拖動滑鼠移動氣球,我們可以在Balloon.qml的Image中加入如下的程式碼:
    MouseArea {
        x: 0; y: parent.height/2
        width: parent.width
        height: parent.height/2

        drag.target: parent
        drag.axis: "XandYAxis"
    }

再重新執行應用。我們嘗試用滑鼠點選氣球的下半部,並移動氣球。我們可以看到氣球隨著滑鼠的移動而移動。下面,我們想在點選氣球的上半部分時,氣球發生爆炸。為了這樣做,我們必須定義另外一個MouseArea來撲捉這個事件。當我們點選氣球的上半部時會發出一個爆破聲。
    MouseArea {
        x: 0; y: 0
        width: parent.width
        height: parent.height/2

        onClicked: {
            balloon.state = "exploded";
            player.play();
        }
    }

為了我們能夠聽到一個聲音,我們也同時定義了一個MediaPlayer。同時記得把剛下載好的程式中的"sounds"目錄考到我們的專案中,並處於和“images”相同的目錄中。
import QtMultimedia 5.0

Image {
    ...

    MediaPlayer {
        id: player
        source: "sounds/blast.wav"
    }

   ...
}

為了能夠使得我們的應用能夠在手機上播放出聲音,我們必須為它加入我們所需要的security policy。為此,我們必須修改專案的“balloon.apparmor”檔案:
有了“Audio”的policy,我們就可以在我們的手機上聽到一聲“砰”的聲音(在氣球爆炸的時候)。 這裡我們把Image的id定義為"balloon"。當點選氣球的上半部時,我們同時設定balloon的狀態為“exploded”狀態。我們知道,QML設計中可以設定component為不同的狀態。在不同的狀態中,可以定義component中各個item的不同屬性的值。預設的狀態為"",即空串。我們定義的狀態如下:
    states: [
        State {
            name: "exploded"

            StateChangeScript {
                script: {
 //                   particle.running = true;
                }
            }

            PropertyChanges {
                target: balloon
                visible: false
                source: "images/" + color + "_exploded.png"
            }

            PropertyChanges {
                target: balloon
                opacity: 0
            }

            PropertyChanges {
                target: balloon
                scale: 0
            }

 //           StateChangeScript { script: balloon.destroy(1000); }
        }
    ]

這時我們重新執行我們的應用,我們會發現我們可以點選球的上半部,並聽到一聲爆破聲。隨後球就消失了。我們的目的達到了,但是,還不是我們最終想要的。這是因為從氣球點選到消失,速度太快了。我們機會沒有看到任何的中間過程。為了我們能夠看到氣球是怎麼爆炸的,我們必須使用一個叫做 transition的。
    transitions: [
        Transition {
            to: "exploded"
            SequentialAnimation {

                // Disappear
                NumberAnimation { target: balloon; property: "opacity"
                    to: 0; duration: 800 }
                NumberAnimation { target: balloon; property: "scale"
                    to: 0; duration: 800 }

                PropertyAction { target: balloon; property: "source"
                    value:  {
                        if ( !surprise )
                            "images/" + color + "_exploded.png"
                        else
                            "images/flower.png";
                    }
                }

                NumberAnimation { target: balloon; property: "opacity"
                    to: 1; duration: 300 }
                NumberAnimation { target: balloon; property: "scale"
                    to: 1; duration: 300 }

                PauseAnimation {
                    duration: {
                        if (surprise)
                            2000
                        else
                            800
                    }
                }

                PropertyAction { target: balloon; property: "visible"
                    value: "false"}
            }
        }
    ]

有了這個我們可以看到氣球是逐漸消失的。我們在Image中也加入瞭如下的屬性。當這個氣球是一個surprise時,我們會顯示一朵花:
Image {
    id: balloon
    width: 100
    height: 250
    property string color: "red"
    property bool surprise: true
    ....
}

為了達到更加逼真的效果,我也為我們的Balloon加入了一個煙花的效果:
import QtQuick.Particles 2.0
...
Image {
    ...
ParticleSystem {
        id: particle
        anchors.fill: parent
        running: false

        Emitter {
            group: "stars"
            emitRate: 800
            lifeSpan: 2400
            size: 24
            sizeVariation: 8
            anchors.fill: parent
        }

        ImageParticle {
            anchors.fill: parent
            source: "qrc:///particleresources/star.png"
            alpha: 0
            alphaVariation: 0.2
            colorVariation: 1.0
        }

        Emitter {
            anchors.centerIn: parent
            emitRate: 400
            lifeSpan: 2400
            size: 20 // 48
            sizeVariation: 8
            velocity: AngleDirection {angleVariation: 180; magnitude: 60}
        }

        Turbulence {
            anchors.fill: parent
            strength: 2
        }
    }

...
}

並在state變化時讓它執行:
            StateChangeScript {
                script: {
                    particle.running = true;
                }
            }

執行應用,效果圖如下:     
至此所有的原始碼可以在如下的網址下載: bzr branch lp:~liu-xiao-guo/debiantrial/balloon1
由於篇幅的原因。我們將在下一篇文章中詳細介紹怎麼動態建立很多個Balloon的。大家敬請期待!