Sur cette page

QML Advanced Tutorial 3 - Implémenter la logique du jeu

Créer un jeu jouable

Maintenant que nous avons tous les composants du jeu, nous pouvons ajouter la logique du jeu qui dicte comment un joueur interagit avec les blocs et joue le jeu jusqu'à ce qu'il soit gagné ou perdu.

Pour ce faire, nous avons ajouté les fonctions suivantes à samegame.js:

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

Comme il s'agit d'un tutoriel sur QML, et non sur la conception de jeux, nous ne parlerons que de handleClick() et victoryCheck() ci-dessous, car ils s'interfacent directement avec les types QML. Notez que bien que la logique du jeu soit ici écrite en JavaScript, elle aurait pu être écrite en C++ puis exposée à QML.

Activation de l'interaction avec le clic de la souris

Pour faciliter l'interface du code JavaScript avec les types QML, nous avons ajouté à samegame.qml un élément appelé gameCanvas, qui remplace l'arrière-plan en tant qu'élément contenant les blocs. Il accepte également les entrées de souris de l'utilisateur. Voici le code de l'élément :

        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)
            }
        }

L'élément gameCanvas a la taille exacte du tableau et possède une propriété score et une propriété MouseArea pour gérer les clics de souris. Les blocs sont maintenant créés comme ses enfants, et ses dimensions sont utilisées pour déterminer la taille du tableau afin que l'application s'adapte à la taille de l'écran disponible. Comme sa taille est liée à un multiple de blockSize, blockSize a été déplacé de samegame.js à samegame.qml en tant que propriété QML. Notez qu'il est toujours possible d'y accéder à partir du script.

Lorsqu'il est cliqué, le site MouseArea appelle handleClick() dans samegame.js, qui détermine si le clic du joueur doit entraîner la suppression de blocs et met à jour gameCanvas.score avec le score actuel si nécessaire. Voici la fonction 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();
}

Notez que si score était une variable globale dans le fichier samegame.js, vous ne pourriez pas la lier. Vous ne pouvez vous lier qu'aux propriétés QML.

Mise à jour du score

Lorsque le joueur clique sur un bloc et déclenche handleClick(), handleClick() appelle également victoryCheck() pour mettre à jour le score et vérifier si le joueur a terminé le jeu. Voici le code de 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);
}

Ce code met à jour la valeur gameCanvas.score et affiche une boîte de dialogue "Game Over" si le jeu est terminé.

La boîte de dialogue Game Over est créée à l'aide d'un type Dialog défini dans Dialog.qml. Voici le code Dialog.qml. Remarquez qu'il est conçu pour être utilisé impérativement à partir du fichier script, via les fonctions et les signaux :

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();
    }
}

Et voici comment il est utilisé dans le fichier principal samegame.qml:

    Dialog {
        id: dialog
        anchors.centerIn: parent
        z: 100
    }

Nous donnons à la boîte de dialogue une valeur z de 100 pour qu'elle soit affichée au-dessus de nos autres composants. La valeur par défaut de z pour un élément est 0.

Une touche de couleur

Il n'est pas très amusant de jouer à Same Game si tous les blocs sont de la même couleur. Nous avons donc modifié la fonction createBlock() dans samegame.js pour créer aléatoirement un type de bloc différent (rouge, vert ou bleu) chaque fois qu'elle est appelée. Block.qml a également été modifié pour que chaque bloc contienne une image différente en fonction de son type :

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";
        }
    }
}
Un jeu qui fonctionne

Nous avons maintenant un jeu qui fonctionne ! Les blocs peuvent être cliqués, le joueur peut marquer des points et le jeu peut se terminer (et vous pouvez alors en commencer un nouveau). Voici une capture d'écran de ce qui a été accompli jusqu'à présent :

Voici à quoi ressemble samegame.qml:

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?"
        }
    }
}

Le jeu fonctionne, mais il est un peu ennuyeux pour l'instant. Où sont les transitions animées fluides ? Où sont les meilleurs scores ? Si vous étiez un expert en QML, vous auriez pu écrire ces éléments dès la première itération, mais dans ce tutoriel, ils ont été conservés jusqu'au prochain chapitre - où votre application devient vivante !

Exemple de projet @ 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.