Auf dieser Seite

QML-Tutorial für Fortgeschrittene 3 – Implementierung der Spielelogik

Ein spielbares Spiel erstellen

Da wir nun über alle Spielkomponenten verfügen, können wir die Spiellogik hinzufügen, die festlegt, wie ein Spieler mit den Blöcken interagiert und das Spiel spielt, bis es gewonnen oder verloren ist.

Dazu haben wir die folgenden Funktionen zu „ samegame.js “ hinzugefügt:

  • handleClick(x,y)
  • floodFill(xIdx,yIdx,type)
  • shuffleDown()
  • victoryCheck()
  • floodMoveCheck(xIdx, yIdx, type)

Da es sich hierbei um ein Tutorial zu QML und nicht zum Spieldesign handelt, werden wir im Folgenden nur auf „ handleClick() “ und „ victoryCheck() “ eingehen, da diese direkt mit den QML-Typen interagieren. Beachten Sie, dass die Spielelogik hier zwar in JavaScript geschrieben ist, sie jedoch auch in C++ hätte geschrieben und dann für QML verfügbar gemacht werden können.

Mausklick-Interaktion aktivieren

Um die Anbindung des JavaScript-Codes an die QML-Typen zu vereinfachen, haben wir dem „ samegame.qml “ ein Element namens „ gameCanvas “ hinzugefügt. Es ersetzt den Hintergrund als das Element, das die Blöcke enthält. Außerdem akzeptiert es Mauseingaben des Benutzers. Hier ist der Code für das Element:

        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 genau die Größe des Spielbretts und verfügt über eine Eigenschaft „ score “ sowie eine „ MouseArea “ zur Verarbeitung von Mausklicks. Die Blöcke werden nun als seine untergeordneten Elemente erstellt, und seine Abmessungen werden zur Bestimmung der Spielfeldgröße herangezogen, sodass sich die Anwendung an die verfügbare Bildschirmgröße anpasst. Da seine Größe an ein Vielfaches von ` blockSize` gebunden ist, wurde ` blockSize ` aus ` samegame.js ` herausgenommen und als QML-Eigenschaft in ` samegame.qml ` verschoben. Beachten Sie, dass weiterhin über das Skript darauf zugegriffen werden kann.

Beim Anklicken ruft „ MouseArea “ die Funktion „ handleClick() “ in „ samegame.js “ auf, die ermittelt, ob der Klick des Spielers zum Entfernen von Blöcken führen soll, und „ gameCanvas.score “ bei Bedarf mit dem aktuellen Punktestand aktualisiert. 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();
}

Beachten Sie, dass Sie keine Bindung zu „ score “ herstellen könnten, wenn es sich dabei um eine globale Variable in der Datei „ samegame.js “ handeln würde. Sie können nur Bindungen zu QML-Eigenschaften herstellen.

Aktualisieren der Punktzahl

Wenn der Spieler auf einen Block klickt und damit „ handleClick() “ auslöst, ruft „ handleClick() “ außerdem „ victoryCheck() “ auf, um den Punktestand zu aktualisieren und zu prüfen, ob der Spieler das Spiel abgeschlossen hat. Hier ist der Code für „ 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);
}

Dadurch wird der Wert von „ gameCanvas.score “ aktualisiert und ein „Game Over“-Dialogfeld angezeigt, wenn das Spiel beendet ist.

Der „Game Over“-Dialog wird mithilfe eines Typs „ Dialog “ erstellt, der in „ Dialog.qml “ definiert ist. Hier ist der Code für „ Dialog.qml “. Beachten Sie, wie er so konzipiert ist, dass er imperativ aus der Skriptdatei heraus über die Funktionen und Signale 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 weisen dem Dialog den Wert „ z “ von 100 zu, um sicherzustellen, dass er über unseren anderen Komponenten angezeigt wird. Der Standardwert für „ z “ eines Elements ist 0.

Ein Hauch von Farbe

Es macht nicht viel Spaß, „Same Game“ zu spielen, wenn alle Blöcke dieselbe Farbe haben. Deshalb haben wir die Funktion „ createBlock() “ in „ samegame.js “ so angepasst, dass bei jedem Aufruf zufällig ein anderer Blocktyp (entweder rot, grün oder blau) erzeugt wird. Auch „ Block.qml “ wurde 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 lassen sich anklicken, der Spieler kann Punkte erzielen, und das Spiel kann enden (und dann kann man ein neues starten). Hier ist ein Screenshot dessen, was bisher erreicht wurde:

Funktionsfähiges Spiel mit farbigen Blöcken, das teilweise geleert wurde

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, ist aber im Moment noch etwas langweilig. Wo sind die flüssigen animierten Übergänge? Wo sind die Highscores? Wenn du ein QML-Experte wärst, hättest du diese Funktionen bereits in der ersten Iteration implementieren können, aber in diesem Tutorial wurden sie für das nächste Kapitel aufgehoben – dort wird deine Anwendung erst richtig lebendig!

Beispielprojekt auf code.qt.io

© 2026 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.