Sur cette page

Tutoriel avancé sur QML 3 - Mise en œuvre de la logique du jeu

Créer un jeu jouable

Maintenant que nous disposons de tous les composants du jeu, nous pouvons ajouter la logique de jeu qui détermine la manière dont le joueur interagit avec les blocs et joue jusqu’à ce qu’il gagne ou perde.

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 le QML et non sur la conception de jeux, nous n'aborderons ci-dessous que les fonctions ` handleClick() ` et ` victoryCheck() `, car elles s'interfacent directement avec les types QML. Notez que bien que la logique de jeu soit ici écrite en JavaScript, elle aurait pu être écrite en C++ puis exposée au QML.

Activation de l’interaction par clic de souris

Pour faciliter l’interaction entre le code JavaScript et les types QML, nous avons ajouté un élément appelé « gameCanvas » à « samegame.qml ». Il 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 cet é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 exactement la taille du plateau et dispose d’une propriété « score » ainsi que d’un « MouseArea » pour gérer les clics de souris. Les blocs sont désormais créés en tant qu’enfants de cet élément, et ses dimensions sont utilisées pour déterminer la taille du plateau 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 ` vers ` samegame.qml ` en tant que propriété QML. Notez qu’il reste accessible depuis le script.

Lorsqu’on clique dessus, l’ MouseArea appelle la fonction 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 y associer de propriété. Vous ne pouvez associer de propriété 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);
}

Cela met à jour la valeur de ` 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 de Dialog.qml. Notez qu’il est conçu pour être utilisé de manière impérative depuis le fichier de 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 attribuons à la boîte de dialogue une valeur de « z » égale à 100 pour nous assurer qu’elle s’affiche par-dessus nos autres composants. La valeur par défaut de « z » pour un élément est 0.

Une touche de couleur

Ce 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 afin de créer aléatoirement un type de bloc différent (rouge, vert ou bleu) à chaque fois qu’elle est appelée. La fonction Block.qml a également été modifiée 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 désormais un jeu qui fonctionne ! On peut cliquer sur les blocs, le joueur peut marquer des points et la partie peut se terminer (après quoi on peut en commencer une nouvelle). Voici une capture d’écran de ce qui a été réalisé jusqu’à présent :

Jeu fonctionnel avec des blocs colorés partiellement éliminés

Voici à quoi ressemble désormais 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 les intégrer dès la première itération, mais dans ce tutoriel, ils ont été mis de côté jusqu’au chapitre suivant – où votre application prendra vie !

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