0% ont trouvé ce document utile (0 vote)
34 vues6 pages

Exercice 02

Ce document présente un exercice de programmation de jeux vidéo utilisant SFML, où les étudiants doivent modifier un projet de casse-brique pour ajouter des fonctionnalités telles que l'affichage de briques et la gestion des collisions. Il décrit les classes principales impliquées, notamment GameObject, Ball, Brick, Game et Palette, et fournit des instructions détaillées sur les modifications à apporter à chaque classe. Les étudiants doivent également gérer les collisions et tester leur jeu avant de soumettre leur projet sur la plateforme LÉA.

Transféré par

owlwhyyy
Copyright
© © All Rights Reserved
Nous prenons très au sérieux les droits relatifs au contenu. Si vous pensez qu’il s’agit de votre contenu, signalez une atteinte au droit d’auteur ici.
Formats disponibles
Téléchargez aux formats PDF, TXT ou lisez en ligne sur Scribd
0% ont trouvé ce document utile (0 vote)
34 vues6 pages

Exercice 02

Ce document présente un exercice de programmation de jeux vidéo utilisant SFML, où les étudiants doivent modifier un projet de casse-brique pour ajouter des fonctionnalités telles que l'affichage de briques et la gestion des collisions. Il décrit les classes principales impliquées, notamment GameObject, Ball, Brick, Game et Palette, et fournit des instructions détaillées sur les modifications à apporter à chaque classe. Les étudiants doivent également gérer les collisions et tester leur jeu avant de soumettre leur projet sur la plateforme LÉA.

Transféré par

owlwhyyy
Copyright
© © All Rights Reserved
Nous prenons très au sérieux les droits relatifs au contenu. Si vous pensez qu’il s’agit de votre contenu, signalez une atteinte au droit d’auteur ici.
Formats disponibles
Téléchargez aux formats PDF, TXT ou lisez en ligne sur Scribd

CEGEP DE S AINTE -FOY – PROGRAMMATION DE JEUX VIDEO 1

EXERCICE02 – INTRODUCTION A SFML

Préparé par Pierre Poulin, Daniel Huot

1 Travail à effectuer
Ouvrez le projet de départ fourni avec cet énoncé. Il devrait déjà contenir plusieurs fichiers permettant de
lancer une application fenêtrée qui, une fois cet exercice terminé, vous permettra de jouer à un jeu de
casse-brique.. Nous modifierons cette application en vue de lui ajouter des fonctionnalités nécessaires pour
afficher des briques, une balle ainsi que pour gérer les déplacements de la balle et les collisions avec les
briques.

Vous pouvez d’abord compiler et exécuter le projet pour vous assurer qu’il fonctionne correctement.

Important
L’énoncé demande de répondre à de courtes questions directement dans le code à des endroits précis.
N’oubliez pas d’inclure vos réponses, elles seront corrigées.

1.1 La classe GameObject

La classe GameObject représente un élément pouvant être affiché, activé et manipulé dans un jeu. Il s’agit
de la classe de base des briques, de la balle et de la palette de jeu.

Cette classe est assez commune dans plusieurs engins de jeu notamment dans Unity que vous utiliserez à
la prochaine session.

Prenez quelques instants pour vous familiariser avec elle et sa classe de base Sprite.

1.2 La classe Ball

La classe Ball hérite de GameObject. Elle hérite donc également des méthodes et attributs permettant de
manipuler ses instances dans un plan 2D. Pour afficher un Sprite à l’écran, il est nécessaire d’utiliser une
texture.

i. Jetez un œil sur l’asset graphique et est fournie, il s’agit cette fois d’une « spritesheet » que nous
allons charger en entier, pour ensuite découper une petite fenêtre afin d’obtenir l’image de la
balle.

ii. En vous basant sur le tutoriel SFML et les notes de cours, apportez les modifications requises à la
classe Ball pour faire déclarer et initialiser la texture dans le constructeur. N’oubliez pas qu’une
Texture se trouve dans l’espace de noms sf.
Note1 : Vous remarquez que le setTextureRect est donné, il s’agit de la « fenêtre » dans laquelle se

1
trouve la balle dans la sprite sheet
Note2 : En fonction de l’image que vous choisirez, vous aurez assurément à modifier l’échelle
(setScale) du sprite.

Astuce
Vous connaissez la BALL_SIZE_IN_SPRITE_SHEET, et vous connaissez le BALL_RADIUS attendu,
appliquez les facteurs d’agrandissement X et Y dans la méthode setScale. Il va de soi que votre
calcul doit fonctionner peu importe la grosseur de la balle qui a été découpée dans la spritesheet

iii. Complétez le code de la méthode update de manière à ce que la nouvelle position de la balle
soit assignée. .Utilisez la méthode move de Sprite (Ball hérite de GameObject qui hérite de
Sprite) utilisez les directions qui doivent être ajustées par la vitesse de la balle (BALL_SPEED).
iv. Complétez le code de la méthode respawn qui vise d’abord à positionner la balle aux
coordonnées reçues. Cette méthode déterminera ensuite une direction horizontale et verticale
réalistes pour la balle.

Utilisez la classe Random au besoin. Elle est très similaire à celle utilisée en C#. Il se peut que
visuellement vous ne constatiez pas une grande différence d’une exécution à l’autre. Ne vous en
faites pas : le générateur aléatoire n’est pas super.

Attention : Cette étape demande un peu de réflexion et de calcul. Idéalement, nous voulons que la
progression diagonale de la balle soit constante, unitaire et réaliste (pas trop verticale ou
horizontale) et ce, dans tous les cas :

Si vous êtes pris à cette étape, assignez une direction horizontale de 0.5f et une direction verticale
de -0.5f et revenez faire les calculs à la fin.

v. Par héritage, la méthode draw se trouve dans GameObject :

void GameObject::draw(sf::RenderWindow& window) const;

Expliquez en commentaires aux endroits prévus pourquoi l’utilisation de l’& ainsi que du modificateur
const sont essentiels ici.
vi. La méthode GameObject::getBoundingRectangle() retourne le résultat de
getGlobalBounds. Expliquez en commentaires en quoi consiste ce rectangle.
vii. Vérifiez que votre balle s’affiche et bouge à l’exécution.

2
1.3 La classe Brick

Cette classe symbolise le concept de brique à détruire par contact avec la balle.

i. La classe Brick hérite également de GameObject. Elle contient donc également toutes les
méthodes et attributs nécessaires pour son affichage. Or, contrairement à la balle qui ne sera
instanciée qu’une seule fois, il y aura plusieurs briques instanciées à l’écran en même temps.
Nous verrons un peu plus tard dans la session une façon appropriée d’éviter d’allouer et de charger
une texture à plusieurs reprises. Pour l’instant, proposez une façon de déclarer un attribut de type
sf::Texture qui évitera de charger 70 fois la texture en question.
ii. Repérez dans le fichier brick.h l’endroit où nous aurions normalement déclaré l’attribut texture
de type Texture. Cependant, si l’instruction se retrouve à cet endroit, nous n’adresserons pas le
problème mentionné ci-haut. Repensez aux principes de base de la programmation orientée objet
et écrivez l’instruction nécessaire pour la déclarer. Complétez au besoin le code nécessaire dans le
fichier brick.cpp.
iii. La définition du constructeur de la classe Brick contient les instructions suivantes :
Brick::Brick(int offsetX, int offsetY, const sf::Vector2i& rectInSpriteSheet)
: GameObject()
, offsetX(offX)
, offsetY(offY)
{

}

Expliquez en commentaires le rôle des instructions surlignées plus haut. Inspirez-vous des notes de
cours au besoin.

iv. Complétez le constructeur de la classe Brick afin d’initialiser la texture.

v. Testez que les briques s’affichent correctement.

3
1.4 La classe Game

La classe Game constitue le cœur de l’application. C’est elle qui gère la logique du jeu. C’est aussi elle qui
s’occupe de gérer la boucle principale du jeu.

i. Repérez la méthode run de la classe BaseGame et prenez quelques instants pour l’analyser.

On y crée la fenêtre principale utilisée pour le rendu du jeu. Elle contient aussi la boucle principale du jeu.
La boucle principale d’un jeu a toujours la même structure :

• Lecture des entrées du joueur


• Mise à jour du jeu
• Affichage du jeu.

La méthode run fait également appel à une méthode nommée handleEvents. Cette dernière méthode
est responsable de gérer les événements « Windows » associés à la fenêtre de rendu (ex. touche du clavier
enfoncée, déplacement ou clique de la souris, etc) de manière synchrone.

Il est possible aussi de gérer les entrées de manière asynchrone, c’est-à-dire de lire l’état d’un périphérique
au moment où l’on en a besoin sans se soucier des événements qui peuvent s’être déroulés depuis la
dernière lecture. Un bon exemple de cela est le fait de déplacer la palette dans le jeu. Nous allons la déplacer
si le joueur appuie sur la touche gauche (ou droite) au moment où l’on va lire le clavier. Nous ne sommes
pas intéressés à savoir si entre deux lectures le joueur a relâché la touche et l’a enfoncée de nouveau.

1.5 La classe Palette

i. Écrivez la définition des méthodes moveLeft (respectivement moveRight) en déplaçant la palette


avec un saut de 10 pixels (le STEP) vers la gauche (respectivement vers la droite).
ii. En vous basant sur votre exercice du dernier cours, écrivez le code nécessaire pour déplacer la
palette vers la gauche ou vers la droite lorsque l’utilisateur appuie sur les touches et . Faites-
en sorte que la palette ne bouge pas si l’on appuie simultanément sur les deux touches. Apportez
les modifications aux endroits appropriés dans le code. N’oubliez pas d’utiliser la struct Inputs..

Ajout d’un élément (asset) dans un projet.


La gestion des ressources dans un projet peut rapidement devenir un cauchemar lorsque le nombre
de ressources augmente.
Il est important que vos ressources soient regroupées au même endroit, possiblement sous un
dossier (ex : /Assets) de votre projet principal.
Il est aussi important que les ressources de votre projet soient correctement déployées à chaque
génération et ce, sans intervention manuelle car vous risqueriez de l’oublier en plus du temps perdu
à chaque fois pour faire l’opération.
Une solution élégante consiste à utiliser les événements de génération de Visual Studio pour
programmer la copie à votre place.

4
Il vous est en effet possible de venir indiquer une ligne de commande que vous voulez
systématiquement exécuter à chaque fois que vous générez votre projet. Ainsi, vous vous éviterez
bien du trouble. Notez que Visual Studio définit quelques macros intéressantes qui vous permettent
d’utiliser la même ligne de commande pour tous vos projets.

Il est important que cet événement de génération soit spécifié pour toutes les configurations.

Organisation de la solution
La solution dans laquelle vous avez travaillé contient 2 projets. Le premier projet (celui nommé
Game) est une bibliothèque à édition des liens statiques (.lib). Le second projet (celui nommé
Launcher) est un projet exécutable (.exe).

Pourquoi avoir séparé le code ainsi? Pour en faciliter … les tests unitaires. En effet, le « code » du
projet se trouvant séparé de l’exécutable, il est facile d’ajouter un projet de tests qui utilisera le
même code que l’exécutable. Le « code » ne sait alors jamais dans quelle situation il est exécuté
(.exe ou tests unitaires).

Une telle manière de procéder demande un peu de gymnastique de configuration de la solution qui
est plus ardue qu’en C#. Compte-tenu que ce n’est pas un objet d’apprentissage du cours, je vous
fournirai toujours le projet de base déjà configuré. Celles et ceux qui souhaitent savoir comment ça
fonctionne dans le détail sont invitées(és) à me le demander!

1.6 Gestion des collisions

Il va de soi dans ce type de jeu que la balle doit rebondir sur toutes les surfaces (bordures, briques et
palette) et que la logique d’affaire subséquente doit être implantée (perte de vie si la balle passe à côté de
la palette et brique qui disparaît au contact de la balle).
i. Regardez attentivement les méthodes de « rebondissement » dans la classe Ball, certaines sont
publiques (nécessitent de la logique en lien avec les briques, logique détenue par la Game),
certaines sont privées (la balle peut prendre ses propres décisions), nous allons donc exploiter
celles-ci. Dans la méthode update de Ball, gérez les collisions latérales et supérieure de la balle avec la
surface de jeu. N’oubliez pas que vous avez accès aux données publiques de la classe Game.
5
ii. Puisque tous nos objets d’affichage sont des GameObject, localisez la méthode collidesWith de la
classe GameObject. On constate que cette méthode travaille avec le GlobalBounds du sprite, c’est-
à-dire son rectangle englobant. Elle appelle ensuite une méthode intersects qui vérifie si 2 sprites
« empiètent » l’un sur l’autre, en d’autres mots : collisionnent ! Puisque nous sommes dans une
boucle de jeu, nous aurons un feedback assez rapide au moindre contact de 2 sprites

Toujours dans le update de Game, complétez la gestion de la collision entre la balle et les briques.
On voudra désactiver une brique au contact de balle et évidemment faire rebondir la balle !

Attention : Une brique désactivée « existe » toujours en mémoire, c’est juste qu’elle n’est pas
soumise à l’affichage, il faudra en tenir compte si vous ne voulez pas que la balle rebondisse sur
des briques « fantômes ».

iii. Il reste maintenant à gérer une balle qui passe à côté de la palette et sortira du jeu par le bas. On
constate que le la logique dans le update est basée sur la méthode isOutOfGame de la classe Ball
qui retourne systématiquement false pour le moment. En d’autres mots, la balle ne peut
actuellement pas respawner car elle est considérée comme étant toujours dans le jeu. Complétez
le isOutOfGame de la classe Ball. Vous connaissez la position verticale de la balle et la hauteur du
jeu, reste à mettre en place la logique booléenne de cette fonction.

iv. Testez le jeu en y jouant et tous ses cas limites

2 Modalités de remise
Remettez votre projet Visual Studio sur LÉA, dans la section travaux, à l’intérieur d’une archive Zip.
Supprimez tous les dossiers temporaires, à savoir les dossiers .vs, extern, Debug et Release. Ce dernier
ne sera pas toujours présent.

N’oubliez pas de supprimer les dossiers pour chaque projet présent dans la
solution.

Vous aimerez peut-être aussi