Section Sciences de la vie
Programmation Orientée Objet
II. Dessin et affichage d'images
La prochaine étape est de dessiner les éléments pour notre jeu.
Couleur de fond
La couleur de fond peut se régler en passant un paramètre à la fonction clear de sf::RenderWindow.
Les couleurs dans SFML sont représentées par la classe sf::Color (cliquez pour accéder à la documentation). Elles sont codées en RGBA, c'est-à-dire quatre valeurs entières entre 0 et 255 pour rouge, vert, bleu et transparence (red, green, blue, alpha). Pour initialiser un objet de type sf::Color avec des valeurs données, on peut utiliser son constructeur en écrivant par exemple
sf::Color rouge(255,0,0);sf::Color vert(0,255,0);sf::Color bleu_transparent(0,0,255,100);
Colorons le fond de notre fenêtre en gris clair. Pour cela, on modifie l'appel à à la fonction clear de sf::RenderWindow par
window.clear(sf::Color(214,214,214));
Rectangles: dessin des raquettes (paddles)
Pour dessiner des rectangles, on utilise la classe sf::RectangleShape (cliquez pour accéder à la documentation). Un exemple d'utilisation est donné ci-dessous:
sf::RectangleShape rectangle; rectangle.setSize(sf::Vector2f(100, 50)); // Taille (100x50) rectangle.setOutlineColor(sf::Color(0,255,0)); //Contour rectangle.setOutlineThickness(5); //Taille du contour rectangle.setPosition(10, 20); //Position sur l'écran
Comme dans beaucoup de librairies graphiques, le axes du système de coordonnées ne suivant pas les conventions habituelles. Ainsi, dans SFML, l'axe vertical pointe vers le bas.
L'envoi du dessin à l'écran se fait en passant le rectangle à la fonction draw de la sf::RenderWindow en question au moment du dessin dans la boucle de rendu. C'est exactement ce qui se passee à la ligne 19 de notre code :
#include <SFML/Graphics.hpp> int main() { sf::RenderWindow window(sf::VideoMode(800, 600), "Titre"); sf::RectangleShape rectangle; rectangle.setSize(sf::Vector2f(100, 50)); // Taille (100x50) rectangle.setPosition(10, 20); //Position sur l'écran"); while (window.isOpen()) { sf::Event event; while (window.pollEvent(event)){ // Les événements seront gérés plus tard ici } window.clear(); window.draw(rectangle); window.display(); } return EXIT_SUCCESS; }
Dessinons les "paddles" des joueurs, c'est-à-dire les deux "raquettes", représentées par des rectangles blancs.. Il est bien sûr logique de vouloir créer une classe Paddle que nous instancierons ensuite deux fois, utilisant ainsi les avantages de la programmation orientée objet!

Pour cela, créez dans le dossier src/Paddle/ un fichier Paddle.cpp et un fichier Paddle.hpp.
Créez dans ces fichiers une classe Paddle avec les attributs et méthodes suivantes:
- Un attribut (privé) sf::RectangleShape rectangle représentant la forme graphique du paddle.
- Un constructeur Paddle(double y) permettant d'initialiser le paddle à une taille et position de base (au milieu de la fenêtre horizontalement et à distance y du haut de la fenêtre, la couleur sf::Color(0,0,0) pourra être donnée au contour).
- Une méthode publique void draw(sf::RenderTarget& target) permettant de dessiner le rectangle sur la fenêtre passée en paramètre.
La conception proposée ci-dessus n'est dans l'absolu pas bonne. Plus tard, il vous faudra utiliser une relation d'héritage pour toutes les classes qui sont "affichables" à l'écran.
Instanciez deux Paddle dans main.cpp aux positions voulues et dessinez-les. Votre fichier main.cpp pourrait alors ressembler à cela:
#include <SFML/Graphics.hpp> #include "Paddle/Paddle.hpp" int main() { sf::RenderWindow window(sf::VideoMode(800, 600), "InfoSV Pong"); //Paddles Paddle top(10); Paddle bottom(580); while (window.isOpen()) { sf::Event event; while (window.pollEvent(event)){ // Les événements seront gérés plus tard ici } window.clear(sf::Color(214,214,214)); top.draw(window); bottom.draw(window); window.display(); } return EXIT_SUCCESS; }
Vous pouvez télécharger une solution de cette étape.
Cercles
Pour dessiner des cercle, on utilise la classe sf::CircleShape (cliquez pour accéder à la documentation), de manière similaire à sf::RectangleShape Nous vous renvoyons donc à la documentation de SFML.
Images (sprites)
Pour afficher des images, on utilise deux classes: sf::Texture (qui représente une image comme ressource en mémoire) et sf::Sprite (qui représente un objet ayant une texture, qui pourra être dessiné à l'écran).
Cette distinction est notamment faite pour des raisons d'optimisation: si la même image est affichée plusieurs fois, il serait stupide de la charger plusieurs fois en mémoire.
Ces deux classes peuvent s'utiliser de la façon suivante:
//création d'une texture à partie d'un fichier sf::Texture texture; texture.loadFromFile("chemin_depuis_le dossier_build/texture.png"); //Création d'un sprite à partir de cette texture sf::Sprite sprite(texture); sprite.setPosition(100, 25);
Puis on envoie le dessin à l'écran comme on le faisait pour un sf::RectangleShape (voir ci-dessus), c'est-à-dire en passant le sprite à la fonction draw de la sf::RenderWindow en question au moment du dessin dans la boucle de rendu.
Dessinons la balle du jeu à partir de l'image d'une molécule d'eau.

Faites un clic droit sur l'image ci-dessus, sélectionne "Enregistrer l'image sous" et enregistrez-la dans votre dossier res sous le nom ball.png.
Comme pour les Paddle, il est pratique et opportun de créer une classe Ball (dans le dossier src/Ball), ayant comme attributs:
- Une texture texture et un sprite sprite.
- Un constructeur Ball() initialisant la position du sprite au milieu de l'écran en lui associant la texture du fichier ../res/ball.png.
- Une fonction void draw(sf::RenderTarget& target) chargée de dessiner le sprite dans la fenêtre target comme pour les Paddles.
Implémentez cette classe, puis instanciez un objet de type Ball dans votre application. Appelez la fonction de dessin dans la boucle de rendu.
Vous devriez arriver au résultat suivant (toujours avec la cible pong):

Vous pouvez télécharger une solution de cette étape.
Texte
Pour afficher du texte, on utilise les classes sf::Font (qui représente une police d'écriture) et sf::Text (qui représente un texte d'une police d'écriture donnée). La première n'est pas contenue dans la seconde pour les mêmes raisons que précédemment.
Un exemple d'utilisation est le suivant:
//Chargement police sf::Font font; font.loadFromFile("arial.ttf"); //Texte avec police "font" de taille 30 sf::Text text("Blabla", font, 30); text.setStyle(sf::Text::Bold); //En gras text.setFillColor(sf::Color::Red); //Couleur //Changer le texte en cours de route. text.setString("Blublu");
Puis on envoie le texte à l'écran comme on le faisait pour un sf::RectangleShape (voir ci-dessus), c'est-à-dire en passant le texte à la fonction draw de la sf::RenderWindow en question au moment du dessin dans la boucle de rendu.
Ajoutons l'affichage des scores à notre application.
A nouveau, nous allons adopter une conception orientée objet et créer une classe Score (dans le dossier src/Score), dotée des attributs suivant:
- Un int value représentant la valeur du score.
- Un sf::Text texte représentant le texte à afficher, c'est-à-dire la valeur du score.
- Un constructeurScore(double x, double y, sf::Font& font) chargée de positionner le texte au point (x,y) avec la police font et d'initialiser le score à zéro.
- Une méthode void draw(sf::RenderTarget& target) chargée de dessiner le texte dans la fenêtre target comme précédemment.
- Une méthode void increment() augmentant le score de 1 et mettant à jour la chaîne de l'attribut texte.
A nouveau, cette conception n'est dans l'absolu pas idéale. Vous verrez bientôt au cours que toutes les classes "dessinables" devraient être regroupées sous une relation d'héritage.
Implémentez cette classe. Pour convertir un int en string, vous pouvez utiliser le code suivant
int i(2); // exemple d'entier initialisé à 2 std::stringstream oss; oss<<i; std::string s(oss.str());
après avoir inclus la librairie standard sstream (i.e. #include <sstream>).
ou plus simplement:int i(2); std::string s(std::to_string(i)); // ici, s contient "2"
Intégrons maintenant cette classe à notre programme principal.
- Créez un tableau de taille fixe de deux Score avant la boucle de rendu et initialisez-les (méthode init) aux deux positions de votre souhait et avec la police ../res/arial.ttf.
- Faites appel aux fonctions draw des Score dans la boucle de rendu pour les dessiner.
Vous devriez obtenir le résultat suivant:

Si vous voulez vous amuser un peu, vous pouvez essayer de rajouter le titre de notre jeu en transparence au fond comme ceci:

Vous pouvez télécharger une solution de cette étape.
Corentin Perret, Jamila Sam - 2013-2025