Logs
Consultez les logs.
OK
Liste des données
Consultez la liste des données.
OK
Loading...
Formulaire
Saisissez vos données.
Enregistrer
Annuler

Jeu de Super Mario Bros

Vues
202

Introduction


Bienvenue dans ce tutoriel consacré à la présentation du processus de création d'un Jeu de Super Mario Bros en C++ avec Qt 6 sous Windows. Nous utiliserons la bibliothèque de création d'interfaces homme machine Qt en C++ sous Windows. Nous utiliserons le compilateur (MinGW) pour compiler notre projet Qt. Qt fournit un environnement de développement intégré (Qt Creator) et un designer d'interfaces graphiques (Qt Designer), un assistant d'aide (Qt Assistant) et un moyen d'internationalisation de nos projets vers plusieurs langues de notre choix (Qt Translation). Nous sommes libres d'utiliser un éditeur de code de notre choix (Eclipse, VS Code ou Microsoft Visual Studio) pour éditer nos fichiers sources puis passer en ligne de commande pour construire, compiler et déboguer notre projet Qt, d'ailleurs, c'est cette solution que nous utiliserons dans notre projet pour plus de flexibilité, même si Qt fournit tout l'écosystème pour faire bien plus de choses que ça. Nous utiliserons le fichier projet (*.pro) pour construire notre projet Qt. Tout le long du développement, nous nous efforcerons à respecter les bonnes pratiques en ingénierie logiciel. Nous veillerons à respecter les principes SOLID pour faciliter la compréhension du code ainsi que sa maintenance et son évolution, les principes RAII pour faciliter la gestion de nos ressources en considérant l'acquisition d'une ressource comme une initialisation afin d'éviter les fuites de mémoire et profiter pleinement du destructeur automatique de classe lorsqu'un objet sort de sa portée, les principes DRY pour éviter la duplication de base de code et nous nous interdirons de créer des objets incomplets.

image.png


Fichier Projet


Le fichier projet (*.pro) est l'une des techniques utilisées par Qt pour construire un projet via la commande (qmake).


Déclaration de la cible


La cible du projet est un fichier exécutable (rdv_super_mario_bros.exe). Nous indiquons la cible à Qt en affectant le nom du fichier exécutable (rdv_super_mario_bros) à la variable (TARGET). Nous indiquons à Qt que la cible est un fichier exécutable en affectant la valeur (app) à la variable (TEMPLATE).

Déclaration des répertoires de construction


Par défaut, Qt génère chaque construction de projet dans un dossier (Debug) ou (Release). Les fichiers intermédiaires tels que les fichiers objet (*.o) et autres fichiers tels que les fichiers MOC (*.moc) ainsi les fichiers cibles (*.exe ou *.lib ou *.dll) sont placés dans ce dossier. La bonne nouvelle c'est que nous pouvons contrôler les répertoires de génération de ces fichiers. Nous indiquons à Qt de générer le fichier exécutable dans le répertoire binaire en affectant la valeur (bin) à la variable (DESTDIR). Nous indiquons à Qt de générer les fichiers objet (*.o) dans le répertoire build en affectant la valeur (build) à la variable (OBJECTS_DIR). Nous indiquons à Qt de générer les fichiers MOC (*.moc) dans le répertoire build en affectant la valeur (build) à la variable (MOC_DIR). Nous indiquons à Qt de générer les fichiers interface utilisateur UI (ui_*.h) dans le répertoire build en affectant la valeur (build) à la variable (UI_DIR). Nous indiquons à Qt de générer les fichiers ressource RCC (rcc_*.h) dans le répertoire build en affectant la valeur (build) à la variable (RCC_DIR).

Déclaration des ressources


Le compilateur de ressources (RCC) est utilisé pour intégrer des ressources dans une application Qt pendant le processus de construction. Il fonctionne en générant un fichier source C++ contenant des données spécifiées dans un fichier de ressources Qt (*.qrc). Nous indiquons l'utilisation d'une ressource à Qt en ajoutant le nom du fichier ressource (resources.qrc) à la variable (RESOURCES).

Déclaration des sources C++


Les sources C++ du projet contiennent les définitions de la logique métier à laquelle doit répondre le projet. Nous indiquons les sources C++ de notre projet à Qt en affectant l'ensemble des sources (main.cpp source.cpp) à la variable (SOURCES).

Déclaration des entêtes C++


Les en-têtes C++ du projet contiennent les déclarations de la logique métier à laquelle doit répondre le projet. Nous indiquons les en-têtes C++ de notre projet à Qt en affectant l'ensemble des sources (header1.h header2.h) à la variable (HEADERS). La déclaration d'un fichier d'en-tête C++ est obligatoire lorsqu'il contient la macro (Q_OBJECT) permettant de bénéficier du mécanisme des signaux et des slots.

Activation de la console


Le débogage permet d'afficher des informations sur les données internes du projet à l'écran afin de saisir très rapidement le sens réel des données. Qt étant orienté pour la création d'interfaces utilisateur, par défaut, l'affichage des données à l'écran est inhibé. Nous indiquons à Qt d'activer l'affichage des données à l'écran en ajoutant la valeur (console) à la variable (CONFIG). Nous affichons un message de débogage à l'écran en avec la macro (qDebug). Nous affichons un message d'erreur fatale avec la variable (qFatal).

Activation des standards C++


Les standards C++ sont devenus incontournable de nos jours si nous voulons bénéficier de toutes la puissance du langage C++ dans notre projet. Nous indiquons à Qt d'utiliser les standards C++ de décembre 2017 lors de la compilation de notre projet en ajoutant la valeur (c++17) à la variable (QMAKE_CXXFLAGS).

Note sur le compilateur des méta-objets


Le compilateur des méta-objets (MOC) lit un fichier d'en-tête C++. S'il trouve une ou plusieurs déclarations de classe contenant la macro (Q_OBJECT), il produit un fichier source C++ contenant le code méta-objet de ces classes. Le système de méta-objets de Qt fournit le mécanisme de signaux et de slots pour la communication inter-objets, les informations de type d'exécution et le système de propriétés dynamiques. 


Messages de débogage


Les messages de débogage servent à remonter des informations sur les données internes de l'application.


Messages de débogage


Nous utilisons la macro (qDebug) pour afficher les messages de débogages. Les messages de débogage de type (qDebug) laissent continuer l'exécution de l'application.

Messages d'erreur


Nous utilisons la macro (qFatal) pour afficher les messages d'erreurs à l'écran. Les messages de type (qFatal) arrêtent l'exécution de l'application. 


Ecran d'accueil


L'écran d'accueil permet d'afficher une image dans une fenêtre d'accueil (sans barre de titre) au lancement de l'application. Nous utilisons la classe (QSplashscreen) pour créer l'écran d'accueil. Nous utilisons la classe (QPixmap) pour charger l'image d'accueil. Nous utilisons dans la classe (QTimer), la méthode statique (singleShot) pour marquer un délai d'attente (200ms) pendant lequel l'écran d'accueil restera affiché. Nous nous servons de la méthode statique (singleShot) de la classe (QTimer) pour connecter le slot de fermeture (close) de l'écran d'accueil au signal associé à l'expiration du délai d'attente (timeout) de la classe (QTimer).


Fenêtre principale


La fenêtre principale correspond à l'interface graphique de notre application. La fenêtre principale est une classe qui dérive de la classe (QMainWindow). Une fenêtre principale (QMainWindow) est une fenêtre qui possède une barre de titre, une barre de menu, une barre d'outils, un composant graphique centrale et une barre d'états.


Affichage de la fenêtre principale


Nous nous servons de la méthode statique (singleShot) de la classe (QTimer) pour connecter le slot d'affichage (show) de la fenêtre principale au signal associé à l'expiration du délai d'attente (timeout) de classe (QTimer). Ainsi, la fermeture de l'écran d'accueil annonce l'affichage de la fenêtre principale.

Titre de l'application


Nous initialisons le titre de notre application avec la méthode (setWindowTitle) présente dans la classe (QMainWindow).

Icône de l'application


Nous initialisons l'icône de notre application avec la méthode (setWindowIcon) présente dans la classe (QApplication). 

image.png

image.png

Actions de l'application


Nous créons une action avec la classe (QAction). Nous initialisons le libellé de l'action (QAction) avec la méthode (setText). Nous initialisons le raccourci clavier associé à l'action avec la méthode (setShortcut). Nous initialisons le message de la barre de statut associé à l'action avec la méthode (setStatusTip). Nous récupérons l'évènement provenant d'une action en connectant le signal (triggered) de l'action au slot (customSlot) de la fenêtre principale.

Menus de l'application


Nous récupérons la barre de menu (QMenuBar) avec la méthode (menuBar) de la classe (QMainWindow). Nous ajoutons un menu (QMenu) à la barre de menu (QMenuBar) avec la méthode (addMenu) de classe (QMenuBar) en précisant le texte libellé du menu (File). Nous créons un menu avec la classe (QAction). Nous ajoutons une action (QAction) à un menu (QMenu) avec la méthode (addAction) de la classe (QMenu).  

image.png

Centrage de la fenêtre principale


Le centrage de la fenêtre principale consiste à fixer les dimensions de la fenêtre principale et à positionner la fenêtre principale au centre de l'écran de l'ordinateur. Nous fixons les dimensions de la fenêtre principale (1280 x 720) avec la méthode (setFixedSize) de la classe (QMainWindow). Nous récupérons les dimensions de l'écran de l'ordinateur à partir de la méthode (geometry) de la classe (QScreen). Nous récupérons l'objet (QScreen) correspond à l'écran de l'ordinateur avec la méthode statique (primaryScreen) de la classe (QApplication). Nous calculons les positions de centrage de la fenêtre principale (Xc , Yc) à partir des dimensions de la fenêtre principale et de celles de l'écran de l'ordinateur. Nous déplaçons la fenêtre principale au centre de l'écran de l'ordinateur avec la méthode (move) de la classe (QMainWindow).

Fermeture de l'application


Nous réalisons la fermeture de l'application en connectant le signal (triggered) de l'action (quitAction) au slot (close) de la fenêtre principale (QMainWindow).  


Vue graphique principale


La vue graphique principale correspond au composant graphique central de la fenêtre principale. Elle dérive de la classe (QGraphicsView). Nous initialisons la vue graphique principale comme étant le composant graphique central de la fenêtre principale avec la méthode (setCentralWidget) de la classe (QMainWIndow). Nous initialisons les dimensions de la vue graphique principale (1280 x 720) avec la méthode (setFixedSize) de la classe (QGraphicsView). Nous désactivons la barre de défilement horizontal de la vue graphique principale avec la méthode (setHorizontalScrollBarPolicy) de la classe (QGraphicsView). Nous désactivons la barre de défilement horizontal de la vue graphique principale avec la méthode (setVerticalScrollBarPolicy) de la classe (QGraphicsView). Une vue graphique (QGraphicsView) est un containeur de scène graphique (QGraphicsScene). Nous initialisons une scène graphique dans la vue graphique principale avec la méthode (setScene) de la classe (QGraphicsView).


Scène graphique du titre



Création du décor


La scène graphique du titre dérive de la classe (QGraphicsScene) et est constituée d'un empilement de 3 images:

Une image de fond constitué d'une couleur de fond bleu ciel avec des nuages.
Une image de sol constitué d'un sol avec de la verdure.
Une image de titre constitué de l'image d'accueil de Super Mario Bros.

Nous créons une image pouvant être ajoutée à une scène graphique (QGraphicsScene) en utilisant la classe (QGraphicsPixmapItem). Nous ajoutons une image (QGraphicsPixmapItem) à une scène graphique avec la méthode (addItem) de la classe (QGraphicsScene). Nous définissons le rectangle de la scène avec la méthode (setSceneRect) de la classe (QGraphicsScene).

Animation de l'image de fond


Nous animons l'image de fond afin d'avoir un ciel avec une couleur de fond bleu ciel et des nuages en mouvement. Afin de pouvoir créer une image capable d'être animée:

Nous créons une classe qui dérive de la classe (QObject) afin de modifier le propriété position de l'image et la classe (QGraphicsPixmapItem) afin d'ajouter l'image à une scène graphique.
Nous créons une animation avec la classe (QPropertyAnimation) associée à la position de l'image. Nous définissons un nombre illimité de boucles d'animation avec la méthode (setLoopCount). Nous définissons la durée de l'animation (150 000 ms) avec la méthode (setDuration). Nous définissons la position de départ de l'image (-1280 , 0) avec la méthode (setStartValue). Nous définissons la position de fin de l'image (0, 0) avec la méthode (setEndValue).

image.png


Gestion des utilisateurs


La gestion des utilisateurs nous permet d'identifier chaque joueur afin d'enregistrer le contexte de chaque session de jeu. Un utilisateur est identifié à travers son nom d'utilisateur et son mot de passe. L'utilisateur doit d'abord créer un compte et ensuite se connecter avant d'accéder au jeu.


Interface de gestion des utilisateurs


L'interface de gestion des utilisateurs est créée directement dans la vue graphique principale (QGraphicsView) pour faciliter la connexion de l'utilisateur à la scène graphique de jeu (QGraphicsScene). Nous réalisons cela au moment de la création de chaque composant graphique de l'interface de gestion des utilisateurs en indiquant que le parent est la vue graphique principale (QGraphicsView). L'interface de gestion des utilisateurs est constituée d'une combinaison des composants graphiques suivants : des libellés, des zones de texte, des boutons et une option.

Zone de texte


Une zone de texte permet de saisir un texte. Nous créons une zone de texte avec la classe (QLineEdit). Nous initialisons le nom de l'objet de la zone de texte avec la méthode (setObjectName). Nous initialisons l'infobulle de la zone de texte avec la méthode (setToolTip). Nous initialisons la géométrie de la zone de texte, c'est-à-dire, sa position et ses dimensions avec la méthode (setGeometry). Nous affichons une image dans un libellé avec la méthode (setPixmap). Nous masquons la zone de texte avec la méthode (close).

Zone de texte (mot de passe)


Lors de la saisie du mot de passe dans la zone de texte (QLineEdit) nous proposons une option (QRadioButton) qui nous permet de masquer ou d'afficher le mot de passe. Nous modifions le mode d'affichage du mot de passe avec la méthode (setEchoMode) de la classe (QLineEdit).

Libellé


Un libellé permet de mieux identifier une zone de texte. Nous créons un libellé avec la classe (QLabel). Nous modifions la police du libellé avec la méthode (setFont). Nous initialisons le texte du libellé avec la méthode (setText). Nous initialisons la géométrie du libellé, c'est-à-dire, sa position et ses dimensions avec la méthode (setGeometry). Nous initialisons le nom de l'objet du libellé avec la méthode (setObjectName). Nous masquons le libellé avec la méthode (close).

Option


Une option permet de réaliser un choix parmi plusieurs. Nous créons une option avec la classe (QRadioButton). Nous initialisons le texte de l'option avec la méthode (setText). Nous initialisons l'infobulle de l'option avec la méthode (setToolTip). Nous initialisons la géométrie de l'option, c'est-à-dire, sa position et ses dimensions avec la méthode (setGeometry). Nous connectons l'option à une action avec la méthode (connect). Nous masquons l'option avec la méthode (close).

Bouton


Un bouton permet de réaliser une action. Nous créons un bouton avec la classe (QPushButton). Nous initialisons le texte du bouton avec la méthode (setText). Nous initialisons l'infobulle du bouton avec la méthode (setToolTip). Nous initialisons la géométrie du bouton, c'est-à-dire, sa position et ses dimensions avec la méthode (setGeometry). Nous connectons le bouton à une action avec la méthode (connect). Nous masquons le bouton avec la méthode (close).

Police


Une police permet de modifier la mise en forme d'un texte. Nous créons une police avec la classe (QFont) en indiquant le nom de la police (MV Boli), la taille de la police (15) et le poids de la police (Bold). Nous modifions la taille d'une police avec la méthode (setPointSize).

image.png

Bouton connexion invité


Le bouton connexion invité (9) est utilisé en mode débogage pour permettre au développeur d'accéder directement au jeu sans saisir les identifiants de connexion. Dans ce mode, nous n'enregistrons pas le contexte de la session de jeu.

Bouton nouvel utilisateur


L'action sur le bouton nouvel utilisateur (8) ouvre une boîte de dialogue dans laquelle nous pouvons enregistrer un nouvel utilisateur.

Boîte de dialogue (saisie d'un nouvel utilisateur)


Une boîte de dialogue est une fenêtre modale de niveau supérieure qui désactive l'interaction de l'utilisateur avec la fenêtre principale. Nous créons la boîte de dialogue en créant une classe qui dérive de la classe (QDialog). Nous initialisons le titre de la boîte de dialogue avec la méthode (setWindowTitle). Nous fixons les dimensions minimales de la boîte de dialogue avec la méthode (setMinimumSize). Nous fixons les dimensions maximales de la boîte de dialogue avec la méthode (setMaximumSize).

image.png

Qt Designer


Qt Designer est un éditeur d'interfaces graphiques fourni par Qt. Il permet de créer des interfaces graphiques par simple glisser-déposer. D'ailleurs, nous avons utilisé Qt Designer pour concevoir l'interface graphique de la saisie d'un nouvel utilisateur. 

image.png

Nom de l'objet


Le nom de l'objet est défini par la méthode (setObjectName) de la classe (QObject). Le nom de l'objet permet de modifier la mise en forme d'un objet à partir de la feuille de styles (QSS) gérée par Qt. La syntaxe QSS est l'équivalent Qt de la syntaxe CSS côté HTML. La classe (QObject) est la mère de toutes les classes qui veulent bénéficier de la mise en forme QSS fourni par Qt.

Expression régulière


Une expression régulière permet d'établir un modèle de correspondance dans un texte. Nous utilisons une expression pour vérifier par exemple que le nom d'utilisateur correspond à une adresse email valide ou que le mot de passe correspond à un mot passe valide. Nous créons une expression régulière avec la classe (QRegularExpression). Nous créons un vérificateur de correspondance d'une expression régulière avec la classe (QRegularExpressionMatch). Nous récupérons le résultat de la correspondance avec la méthode (hasMatch) de la classe (QRegularExpressionMatch).

Expression régulière


Une expression régulière permet d'établir un modèle de correspondance dans un texte. Nous utilisons une expression pour vérifier par exemple que le nom d'utilisateur correspond à une adresse email valide ou que le mot de passe correspond à un mot passe valide. Nous créons une expression régulière avec la classe (QRegularExpression). Nous créons un vérificateur de correspondance d'une expression régulière avec la classe (QRegularExpressionMatch). Nous récupérons le résultat de la correspondance avec la méthode (hasMatch) de la classe (QRegularExpressionMatch).

Base de données


Une base de données permet de stocker des données de manière permanente. Nous utilisons la base de données pour sauvegarder par exemple les données de chaque utilisateur ainsi que le contexte de chaque session de jeu. 


Base de données


Une base de données permet de stocker des données de manière permanente. Nous utilisons la base de données pour sauvegarder par exemple les données de chaque utilisateur ainsi que le contexte de chaque session de jeu.


Base données MySQL


MySQL est un système de gestion de base de données relationnelles. MySQL est pris en charge par Qt.

Connexion à la base données


Nous créons une nouvelle connexion (QSqlDatabase) avec la méthode statique (addDatabase) de la classe (QSqlDatabase) en indiquant le nom du driver (QMYSQL) et un nom de la connexion (rdv_super_mario_bros). Nous initialisons l'adresse du serveur de données avec la méthode (setHostName) de la classe (QSqlDatabase). Nous initialisons le numéro de port du serveur de données avec la méthode (setPort) de la classe (QSqlDatabase). Nous initialisons le nom d'utilisateur du serveur de données avec la méthode (setUserName) de la classe (QSqlDatabase). Nous initialisons le mot de passe du serveur de données avec la méthode (setPassword) de la classe (QSqlDatabase). Nous initialisons le nom de la base de données à utiliser sur le serveur de données avec la méthode (setDatabaseName) de la classe (QSqlDatabase). Nous ouvrons la connexion au serveur de données avec la méthode (open) de la classe (QSqlDatabase).

Gestionnaire de requêtes SQL


Nous créons un gestionnaire de requêtes SQL avec la classe (QSqlQuery) en indiquant au constructeur la connexion (QSqlDatabase).

Injection SQL


Une injection SQL est une attaque SQL consistant à détourner le fonctionnant d'une requête SQL dans le but de se connecter à un système sans avoir l'autorisation. L'injection SQL est réalisée en injectant une requête de détournement SQL à base l'opérateur logique OR dans les champs nom d'utilisateur ou mot de passe.

Requêtes SQL préparées


Les requêtes SQL préparées sont le moyen le plus sûr de lutter contre les attaques par injection SQL. La préparation des requêtes SQL est une étape obligatoire dans la construction d'un système de sécurisé. Nous préparons nos requêtes SQL avec la méthode (prepare) de la classe (QSqlQuery). Nous lions les données à la requête SQL préparée avec la méthode (bindValue) de la classe (QSqlQuery). Nous exécutons la requête SQL avec la méthode (exec) de la classe (QSqlQuery).

Requêtes SQL d'insertion


Nous récupérons l'ID de la dernière insertion avec la méthode (lastInsertId) de la classe (QSqlQuery).

Requêtes SQL de sélection


Nous récupérons le gestionnaire des enregistrements (QSqlRecord) avec la méthode (record) de la classe (QSqlQuery). Nous récupérons le nombre de lignes d'enregistrements avec la méthode (size) de la classe (QSqlQuery). Nous récupérons le nombre de colonnes de chaque enregistrement avec la méthode (count) de la classe (QSqlRecord). Nous parcourons les enregistrements avec la méthode (next) de la classe (QSqlQuery). Nous récupérons la valeur de chaque colonne de l'enregistrement en cours avec la méthode (value) de la classe (QSqlQuery) en indiquant l'index de la colonne (iCol).


Gestion des effets sonores et des audio


Les effets sonores ajoutent une profondeur empathique et psychologique à notre jeu en accompagnant les actions, les moments forts, l'atmosphère et les personnages du jeu. L'application démarre par exemple avec un audio du jeu de Super Mario Bros. Des effets sonores différentes sont par exemple associés aux moments où le personnage de Super Mario Bros acquiert un pouvoir et grandit ou perd un pouvoir et rétrécit. Cela rend le jeu reconnaissable à travers les effets sonores et les audios qui l'accompagnent sans avoir vu l'interface du jeu.


Patron observateur


Le mécanisme des signaux et des slots, fourni par Qt est une implémentation très bien élaborée du patron observateur. Ce mécanisme permet de notifier un objet lorsqu'un changement d'état intervient dans un autre objet. Cela favorise l'élimination de couplage fort entre les objets.

Communication inter-objets


Nous connectons l'émetteur au récepteur avec la méthode (connect) de la classe (QObject) en indiquant le signal émis par l'émetteur (customSignal) et le slot reçu par le récepteur (customSlot). L'objet émetteur émet un signal (customSignal) avec le mot clé (emit) et l'objet récepteur capture le signal avec le slot (customSlot). La classe (QObject) est la mère de toutes les classes qui veulent bénéficier du mécanisme des signaux et des slots, fourni par Qt.

Gestion des effets sonores


Nous créons un gestionnaire d'effet sonore avec la classe (QSoundEffect). Nous initialisons le chemin de l'effet sonore avec la méthode (setSource). Nous indiquons de jouer l'effet sonore en une seule fois avec la méthode (setLoopCount) en indiquant 0 ou 1 comme nombre de boucle. Nous initialisons le volume en pourcentage avec la méthode (setVolume). Nous jouons l'effet sonore avec la méthode (play).

Gestion des audios


Nous créons un gestionnaire de son audio avec la classe (QMediaPlayer). Nous initialisons le chemin du son audio avec la méthode (setSource). Nous initialisons la sortie audio avec la méthode (setAudioOutput). Nous jouons le son audio avec la méthode (play). Nous arrêtons le son audio avec la méthode (stop). Nous créons un gestionnaire de sortie audio avec la classe (QAudioOutput).