翻譯|使用教程|編輯:鮑佳佳|2021-03-26 10:50:30.540|閱讀 565 次
概述:前三章講了游戲前期搭建,現(xiàn)在我們要做兩件事來活躍游戲:給積木做動畫,并添加一個高分系統(tǒng)。
# 界面/圖表報表/文檔/IDE等千款熱門軟控件火熱銷售中 >>
相關(guān)鏈接:
Qt是一個跨平臺框架,通常用作圖形工具包,它不僅創(chuàng)建CLI應(yīng)用程序中非常有用。而且它也可以在三種主要的臺式機操作系統(tǒng)以及移動操作系統(tǒng)(如Symbian,Nokia Belle,Meego Harmattan,MeeGo或BB10)以及嵌入式設(shè)備,Android(Necessitas)和iOS的端口上運行。現(xiàn)在我們?yōu)槟闾峁┝嗣赓M的試用版。
Qt組件推薦:
現(xiàn)在我們要做兩件事來活躍游戲:給積木做動畫,并添加一個高分系統(tǒng)。
我們還清理了應(yīng)用程序文件的目錄結(jié)構(gòu)。我們現(xiàn)在有很多文件,所以所有samegame.qml以外的JavaScript和QML文件都被移到了一個名為 "content "的新子目錄中。
為了迎接新的塊狀動畫,Block.qml文件現(xiàn)在改名為BoomBlock.qml。
動畫塊運動
首先我們將對塊進(jìn)行動畫,使它們以流體的方式移動。QML有很多方法來添加流體運動,在這種情況下,我們將使用Behavior類型來添加一個SpringAnimation。在BoomBlock.qml中,我們將SpringAnimation行為應(yīng)用到x和y屬性中,這樣塊體將以類似彈簧的方式跟隨并向指定的位置移動(其值將由samegame.js設(shè)置)。以下是添加到BoomBlock.qml中的代碼。
property bool spawned: false Behavior on x { enabled: spawned; SpringAnimation{ spring: 2; damping: 0.2 } } Behavior on y { SpringAnimation{ spring: 2; damping: 0.2 } }
spring和damping值可以改變,以修改動畫的類似彈簧的作用。
enabled: spawned設(shè)置指的是在samegame.js中從createBlock()中設(shè)置的spawned值。這保證了只有在createBlock()將塊設(shè)置到正確的位置后,才會啟用x上的SpringAnimation。否則,當(dāng)游戲開始時,塊會從角落(0,0)滑出,而不是成行地從頂部落下。試著注釋出enabled: spawned,然后自己看看)。
動畫塊不透明度變化
接下來,我們將添加一個平滑的退出動畫。為此,我們將使用一個Behavior類型,它允許我們在發(fā)生屬性變化時指定一個默認(rèn)的動畫。在這種情況下,當(dāng)一個Block的不透明度變化時,我們將對不透明度值進(jìn)行動畫處理,使其逐漸淡入和淡出,而不是在完全可見和不可見之間突然變化。要做到這一點,我們將在 BoomBlock.qml 中的 Image 類型的不透明度屬性上應(yīng)用一個 Behavior。
Image { id: img anchors.fill: parent source: { if (type == 0) return "../../shared/pics/redStone.png"; else if (type == 1) return "../../shared/pics/blueStone.png"; else return "../../shared/pics/greenStone.png"; } opacity: 0 Behavior on opacity { NumberAnimation { properties:"opacity"; duration: 200 } } }
請注意不透明度:0,這意味著塊在第一次創(chuàng)建時是透明的。我們可以在創(chuàng)建和銷毀塊時在samegame.js中設(shè)置不透明度,但我們將使用狀態(tài),因為這對我們將要添加的下一個動畫很有用。最初,我們將這些狀態(tài)添加到BoomBlock.qml的根類型中。
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 } } ]
現(xiàn)在,塊會自動淡入,因為我們在實現(xiàn)塊動畫時已經(jīng)將 spawned 設(shè)置為 true。為了淡出,我們將死亡設(shè)置為true,而不是在塊被破壞時將不透明度設(shè)置為0(在floodFill()函數(shù)中)。
添加粒子效果
最后,我們將在積木被破壞時為它們添加一個看起來很酷的粒子效果。要做到這一點,我們首先在BoomBlock.qml中添加一個粒子系統(tǒng),像這樣。
ParticleSystem { id: sys anchors.centerIn: parent ImageParticle { // ![0] source: { if (type == 0) return "../../shared/pics/redStar.png"; else if (type == 1) return "../../shared/pics/blueStar.png"; else return "../../shared/pics/greenStar.png"; } rotationVelocityVariation: 360 // ![0] } Emitter { id: particles anchors.centerIn: parent emitRate: 0 lifeSpan: 700 velocity: AngleDirection {angleVariation: 360; magnitude: 80; magnitudeVariation: 40} size: 16 } }
要想完全理解這一點,你應(yīng)該閱讀《使用Qt快速粒子系統(tǒng)》,但需要注意的是,emitRate被設(shè)置為零,這樣粒子就不會正常發(fā)射了。另外,我們還擴展了垂死狀態(tài),它通過調(diào)用粒子類型上的burst()方法來創(chuàng)建粒子的爆發(fā)。現(xiàn)在狀態(tài)的代碼是這樣的。
states: [ State { name: "AliveState" when: spawned == true && dying == false PropertyChanges { target: img; opacity: 1 } }, State { name: "DeathState" when: dying == true StateChangeScript { script: particles.burst(50); } PropertyChanges { target: img; opacity: 0 } StateChangeScript { script: block.destroy(1000); } } ]
現(xiàn)在,游戲的動畫效果非常漂亮,為玩家的所有動作添加了微妙(或不那么微妙)的動畫。最終的效果如下圖所示,用不同的一組圖片來演示基本的主題。
這里的主題變化只是通過替換塊圖像產(chǎn)生的。這可以在運行時通過改變Image source屬性來完成,所以為了進(jìn)一步的挑戰(zhàn),你可以添加一個按鈕來切換不同圖片的主題。
保持高分表
我們可能要添加到游戲中的另一個功能是一種存儲和檢索高分的方法。
要做到這一點,我們將在游戲結(jié)束時顯示一個對話框來請求玩家的名字并將其添加到高分表中。這需要對Dialog.qml進(jìn)行一些修改。除了Text類型之外,現(xiàn)在它還有一個TextInput子項用于接收鍵盤文本輸入。
Rectangle { id: container ... TextInput { id: textInput anchors { verticalCenter: parent.verticalCenter; left: dialogText.right } width: 80 text: "" onAccepted: container.hide() // close dialog when Enter is pressed } ... }
我們還將添加一個showWithInput()函數(shù)。只有調(diào)用這個函數(shù)而不是show(),文本輸入才會可見。當(dāng)對話框關(guān)閉時,它會發(fā)出一個closed()信號,其他類型的對話框可以通過inputText屬性檢索用戶輸入的文本。
Rectangle { id: container property string inputText: textInput.text signal closed function show(text) { dialogText.text = text; container.opacity = 1; textInput.opacity = 0; } function showWithInput(text) { show(text); textInput.opacity = 1; textInput.focus = true; textInput.text = "" } function hide() { textInput.focus = false; container.opacity = 0; container.closed(); } ... }
現(xiàn)在可以在samegame.qml中使用該對話框。
Dialog { id: nameInputDialog anchors.centerIn: parent z: 100 onClosed: { if (nameInputDialog.inputText != "") SameGame.saveHighScore(nameInputDialog.inputText); } }
當(dāng)對話框發(fā)出關(guān)閉信號時,我們調(diào)用samegame.js中新的saveHighScore()函數(shù),將高分存儲在本地的SQL數(shù)據(jù)庫中,如果可能的話還可以將分?jǐn)?shù)發(fā)送到在線數(shù)據(jù)庫中。
在samegame.js中的victoryCheck()函數(shù)中,nameInputDialog被激活。
function victoryCheck() { ... //Check whether game has finished if (deservesBonus || !(floodMoveCheck(0, maxRow - 1, -1))) { gameDuration = new Date() - gameDuration; nameInputDialog.showWithInput("You won! Please enter your name: "); } }
離線存儲高分
現(xiàn)在我們需要實現(xiàn)實際保存高分表的功能。
這里是samegame.js中的saveHighScore()函數(shù)。
function saveHighScore(name) { if (scoresURL != "") sendHighScore(name); var db = Sql.LocalStorage.openDatabaseSync("SameGameScores", "1.0", "Local SameGame High Scores", 100); var dataStr = "INSERT INTO Scores VALUES(?, ?, ?, ?)"; var data = [name, gameCanvas.score, maxColumn + "x" + maxRow, Math.floor(gameDuration / 1000)]; db.transaction(function(tx) { tx.executeSql('CREATE TABLE IF NOT EXISTS Scores(name TEXT, score NUMBER, gridSize TEXT, time NUMBER)'); tx.executeSql(dataStr, data); var rs = tx.executeSql('SELECT * FROM Scores WHERE gridSize = "12x17" ORDER BY score desc LIMIT 10'); var r = "\nHIGH SCORES for a standard sized grid\n\n" for (var i = 0; i < rs.rows.length; i++) { r += (i + 1) + ". " + rs.rows.item(i).name + ' got ' + rs.rows.item(i).score + ' points in ' + rs.rows.item(i).time + ' seconds.\n'; } dialog.show(r); }); }
首先,我們調(diào)用sendHighScore()(在下面的章節(jié)中解釋),如果可以將高分發(fā)送到在線數(shù)據(jù)庫。
然后,我們使用本地存儲API來維護(hù)這個應(yīng)用程序特有的持久化SQL數(shù)據(jù)庫。我們使用openDatabaseSync()為高分創(chuàng)建一個離線存儲數(shù)據(jù)庫,并準(zhǔn)備好我們要用來保存高分的數(shù)據(jù)和SQL查詢。離線存儲API使用SQL查詢來進(jìn)行數(shù)據(jù)操作和檢索,在db.transaction()調(diào)用中,我們使用三個SQL查詢來初始化數(shù)據(jù)庫(如果需要的話),然后添加到和檢索高分。為了使用返回的數(shù)據(jù),我們將其變成一個字符串,每行返回一行,并顯示一個包含該字符串的對話框。
這是本地存儲和顯示高分的一種方式,但肯定不是唯一的方式。一個更復(fù)雜的選擇是創(chuàng)建一個高分對話框組件,并將結(jié)果傳遞給它進(jìn)行處理和顯示(而不是重復(fù)使用Dialog)。這將允許一個更有主題的對話框,可以更好地展示高分。如果你的QML是一個C++應(yīng)用的UI,你也可以將分?jǐn)?shù)傳遞給一個C++函數(shù),以多種方式將其存儲在本地,包括不用SQL的簡單格式或另一個SQL數(shù)據(jù)庫中。
在線存儲高分
你已經(jīng)看到了如何在本地存儲高分,但也很容易在你的QML應(yīng)用程序中集成一個支持網(wǎng)絡(luò)的高分存儲。我們對她的實現(xiàn)非常簡單:將高分?jǐn)?shù)據(jù)發(fā)布到某個服務(wù)器上運行的php腳本中,然后該服務(wù)器將其存儲并顯示給訪問者。你也可以從同一個服務(wù)器上請求一個XML或QML文件,其中包含并顯示分?jǐn)?shù),但這超出了本教程的范圍。我們在這里使用的php腳本可以在examples目錄下找到。
如果玩家輸入了他們的名字,我們可以將數(shù)據(jù)發(fā)送到網(wǎng)絡(luò)服務(wù)中,我們
如果玩家輸入一個名字,我們就用samegame.js中的這段代碼將數(shù)據(jù)發(fā)送到服務(wù)。
function sendHighScore(name) { var postman = new XMLHttpRequest() var postData = "name=" + name + "&score=" + gameCanvas.score + "&gridSize=" + maxColumn + "x" + maxRow + "&time=" + Math.floor(gameDuration / 1000); postman.open("POST", scoresURL, true); postman.setRequestHeader("Content-Type", "application/x-www-form-urlencoded"); postman.onreadystatechange = function() { if (postman.readyState == postman.DONE) { dialog.show("Your score has been uploaded."); } } postman.send(postData); }
這段代碼中的XMLHttpRequest與標(biāo)準(zhǔn)瀏覽器JavaScript中的XMLHttpRequest()是一樣的,可以用同樣的方式從web服務(wù)中動態(tài)獲取XML或QML來顯示高分。在這種情況下,我們不用擔(dān)心響應(yīng)--我們只是將高分?jǐn)?shù)據(jù)發(fā)布到web服務(wù)器上。如果它返回了一個QML文件(或一個QML文件的URL),你可以用與塊相同的方式來實例化它。
#### 未完待續(xù)持續(xù)更新
====================================================
想要了解或購買Qt正版授權(quán)的朋友,歡迎
Qt技術(shù)交流群現(xiàn)已開通,QQ搜索群號“765444821”或者掃描下方二維碼即可加入
本站文章除注明轉(zhuǎn)載外,均為本站原創(chuàng)或翻譯。歡迎任何形式的轉(zhuǎn)載,但請務(wù)必注明出處、不得修改原文相關(guān)鏈接,如果存在內(nèi)容上的異議請郵件反饋至chenjj@fc6vip.cn
文章轉(zhuǎn)載自: