QML-Tutorial für Fortgeschrittene 3 - Implementieren der Spiellogik
Ein spielbares Spiel erstellen
Nun, da wir alle Spielkomponenten haben, können wir die Spiellogik hinzufügen, die bestimmt, wie ein Spieler mit den Blöcken interagiert und das Spiel spielt, bis es gewonnen oder verloren ist.
Zu diesem Zweck haben wir die folgenden Funktionen zu samegame.js
hinzugefügt:
handleClick(x,y)
floodFill(xIdx,yIdx,type)
shuffleDown()
victoryCheck()
floodMoveCheck(xIdx, yIdx, type)
Da dies ein Tutorium über QML und nicht über Spieldesign ist, werden wir im Folgenden nur handleClick()
und victoryCheck()
besprechen, da sie direkt mit den QML-Typen verbunden sind. Beachten Sie, dass die Spiellogik hier zwar in JavaScript geschrieben ist, aber auch in C++ hätte geschrieben und dann in QML dargestellt werden können.
Aktivieren der Mausklick-Interaktion
Um dem JavaScript-Code die Schnittstelle zu den QML-Typen zu erleichtern, haben wir ein Element namens gameCanvas
zu samegame.qml
hinzugefügt. Es nimmt auch Mauseingaben des Benutzers entgegen. Hier ist der Code des Elements:
Item { id: gameCanvas property int score: 0 property int blockSize: 40 width: parent.width - (parent.width % blockSize) height: parent.height - (parent.height % blockSize) anchors.centerIn: parent MouseArea { anchors.fill: parent onClicked: (mouse)=> SameGame.handleClick(mouse.x, mouse.y) } }
Das Element gameCanvas
hat die exakte Größe der Tafel und verfügt über die Eigenschaften score
und MouseArea, um Mausklicks zu verarbeiten. Die Blöcke werden nun als seine Kinder erstellt, und seine Abmessungen werden verwendet, um die Größe der Tafel zu bestimmen, damit die Anwendung auf die verfügbare Bildschirmgröße skaliert. Da seine Größe an ein Vielfaches von blockSize
gebunden ist, wurde blockSize
als QML-Eigenschaft aus samegame.js
in samegame.qml
verschoben. Beachten Sie, dass der Zugriff über das Skript weiterhin möglich ist.
Beim Anklicken ruft MouseArea handleClick()
in samegame.js
auf, das feststellt, ob der Klick des Spielers dazu führen soll, dass Blöcke entfernt werden, und aktualisiert gameCanvas.score
gegebenenfalls mit dem aktuellen Punktestand. Hier ist die Funktion handleClick()
:
function handleClick(xPos, yPos) { var column = Math.floor(xPos / gameCanvas.blockSize); var row = Math.floor(yPos / gameCanvas.blockSize); if (column >= maxColumn || column < 0 || row >= maxRow || row < 0) return; if (board[index(column, row)] == null) return; //If it's a valid block, remove it and all connected (does nothing if it's not connected) floodFill(column, row, -1); if (fillFound <= 0) return; gameCanvas.score += (fillFound - 1) * (fillFound - 1); shuffleDown(); victoryCheck(); }
Wenn score
eine globale Variable in der Datei samegame.js
wäre, könnten Sie keine Verbindung zu ihr herstellen. Sie können nur an QML-Eigenschaften binden.
Aktualisieren des Spielstands
Wenn der Spieler auf einen Block klickt und handleClick()
auslöst, ruft handleClick()
auch victoryCheck()
auf, um den Punktestand zu aktualisieren und zu prüfen, ob der Spieler das Spiel beendet hat. Hier ist der Code von victoryCheck()
:
function victoryCheck() { //Award bonus points if no blocks left var deservesBonus = true; for (var column = maxColumn - 1; column >= 0; column--) if (board[index(column, maxRow - 1)] != null) deservesBonus = false; if (deservesBonus) gameCanvas.score += 500; //Check whether game has finished if (deservesBonus || !(floodMoveCheck(0, maxRow - 1, -1))) dialog.show("Game Over. Your score is " + gameCanvas.score); }
Damit wird der Wert gameCanvas.score
aktualisiert und ein "Game Over"-Dialog angezeigt, wenn das Spiel beendet ist.
Das Dialogfeld "Game Over" wird mit dem Typ Dialog
erstellt, der in Dialog.qml
definiert ist. Hier ist der Code Dialog.qml
. Beachten Sie, dass er so konzipiert ist, dass er über die Funktionen und Signale in der Skriptdatei verwendet werden kann:
import QtQuick Rectangle { id: container function show(text) { dialogText.text = text; container.opacity = 1; } function hide() { container.opacity = 0; } width: dialogText.width + 20 height: dialogText.height + 20 opacity: 0 Text { id: dialogText anchors.centerIn: parent text: "" } MouseArea { anchors.fill: parent onClicked: hide(); } }
Und so wird er in der Hauptdatei samegame.qml
verwendet:
Dialog { id: dialog anchors.centerIn: parent z: 100 }
Wir geben dem Dialog einen z Wert von 100, um sicherzustellen, dass er über unseren anderen Komponenten angezeigt wird. Der Standardwert von z
für ein Element ist 0.
Ein Hauch von Farbe
Es macht keinen Spaß, Same Game zu spielen, wenn alle Blöcke dieselbe Farbe haben. Deshalb haben wir die Funktion createBlock()
in samegame.js
so geändert, dass sie bei jedem Aufruf zufällig einen anderen Blocktyp (entweder rot, grün oder blau) erzeugt. Block.qml
wurde auch so geändert, dass jeder Block je nach Typ ein anderes Bild enthält:
import QtQuick Item { id: block property int type: 0 Image { id: img anchors.fill: parent source: { if (type == 0) return "pics/redStone.png"; else if (type == 1) return "pics/blueStone.png"; else return "pics/greenStone.png"; } } }
Ein funktionierendes Spiel
Jetzt haben wir ein funktionierendes Spiel! Die Blöcke können angeklickt werden, der Spieler kann punkten, und das Spiel kann beendet werden (und dann kann man ein neues beginnen). Hier ist ein Bildschirmfoto von dem, was bisher erreicht worden ist:
So sieht samegame.qml
jetzt aus:
import QtQuick import "samegame.js" as SameGame Rectangle { id: screen width: 490; height: 720 SystemPalette { id: activePalette } Item { width: parent.width anchors { top: parent.top; bottom: toolBar.top } Image { id: background anchors.fill: parent source: "pics/background.jpg" fillMode: Image.PreserveAspectCrop } Item { id: gameCanvas property int score: 0 property int blockSize: 40 width: parent.width - (parent.width % blockSize) height: parent.height - (parent.height % blockSize) anchors.centerIn: parent MouseArea { anchors.fill: parent onClicked: (mouse)=> SameGame.handleClick(mouse.x, mouse.y) } } } Dialog { id: dialog anchors.centerIn: parent z: 100 } Rectangle { id: toolBar width: parent.width; height: 30 color: activePalette.window anchors.bottom: screen.bottom Button { anchors { left: parent.left; verticalCenter: parent.verticalCenter } text: "New Game" onClicked: SameGame.startNewGame() } Text { id: score anchors { right: parent.right; verticalCenter: parent.verticalCenter } text: "Score: Who knows?" } } }
Das Spiel funktioniert, aber es ist im Moment ein bisschen langweilig. Wo sind die flüssigen animierten Übergänge? Wo sind die hohen Punktzahlen? Wenn du ein QML-Experte wärst, hättest du diese Dinge schon in der ersten Iteration schreiben können, aber in diesem Tutorial wurden sie bis zum nächsten Kapitel aufgespart - wo deine Anwendung lebendig wird!
© 2025 The Qt Company Ltd. Documentation contributions included herein are the copyrights of their respective owners. The documentation provided herein is licensed under the terms of the GNU Free Documentation License version 1.3 as published by the Free Software Foundation. Qt and respective logos are trademarks of The Qt Company Ltd. in Finland and/or other countries worldwide. All other trademarks are property of their respective owners.