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 :

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