Ecole Polytechnique Fédérale de Lausanne
Section Sciences de la vie
Programmation Orientée Objet
Tutoriel librairie graphique (SFML)

V. Gestion des collisions: rebond et game over

A ce stade, vous avez déjà appris l'essentiel des notions qui vous seront nécessaires à la réalisation du projet de cette année. A ce titre cette partie, un peu plus difficile, peut-être considérée comme facultative. Nous la proposons pour compléter la programmation du jeu de Pong et introduire la problématique du test de collisions (que nous traiterons un peu différement dans le projet).

Toutes les formes dans SFML (rectangle, cercle ...en fait tout objet héritant de sf::Shape) ainsi que les sprites possèdent une fonction FloatRect getGlobalBounds() retournant un rectangle délimitant celle-ci. Dans le cas des sprites et des cercles, le rectangle (appelé bounding-box dans le jargon) est déterminé automatiquement.

Un objet de type sf::FloatRect représente un rectangle à coordonnées réelles. Cette classe possède une méthode bool intersects(const Floatrect&) retournant true si et seulement si les deux rectangles s'intersectent (avec la convention que seules les arêtes à gauche et en haut sont incluses dans le bord)

Un exemple d'utilisation est le suivant:

//Création d'un rectangle dont le coin en haut à gauche est en (10,10), de taille 200x100
sf::FloatRect r1(10,10,200,100);
//Création d'un rectangle dont le coin en haut à gauche est en (0,0), de taille 200x100
sf::FloatRect r2(0,0,200,100);
//Test d'intersection
std::cout<<r1.intersects(r2)<<std::endl;

En utilisant ces fonctions, on peut donc implémenter un système basique de collision en approximant toutes les formes par des rectangles.

Maintenant que la balle et les paddle bougent, il est temps de s'occuper de faire rebondir la balle sur ceux-ci et sur les murs, ainsi que de détecter quand la balle sort de la zone de jeu.

  1. Premièrement, les collisions avec les murs haut et bas (donc game over). Ajoutez une fonction int out() à la classe Ball avec le comportement suivant:

    • Si la balle est dans la zone rouge supérieure sur l'image ci-dessus, la fonction retourne 0.
    • Si la balle est dans la zone rouge inférieure sur l'image ci-dessus, la fonction retourne 1.
    • Sinon, la fonction retourne -1.
    Note
    Si vous voulez être plus élégants, vous pouvez également implémenter cela avec un enum.

    Pour implémenter cela, utilisez bien sûr des sf::FloatRect!

    Indications La zone rouge représentant la limite supérieure du terrain de jeu peut être matérialisée par le sf::FloatRect de caractéristiques (0,0,800,2) par exemple.

    Pour savoir si la balle intersecte cette zone (et donc est en dehors), il suffira donc d'utiliser ici la tournure sprite.getGlobalBounds().intersects(sf::FloatRect(0,0,800,2)))sprite est le sprite associé à la balle.

  2. Dans la boucle de rendu, utilisez cette fonction pour vérifier si la balle sort de la zone de jeu (en haut ou en bas). Si c'est le cas, incrémentez le score du joueur gagnant (en utilisant la fonction increment de Score) et remettez la balle en position initiale grâce à la fonction restart de Ball.

  3. Maintenant, les collisions avec les murs gauche et droite (sur lesquels la balle rebondit). Ajoutez une fonction void checkWallCollisions() à la classe Ball qui:

    • Vérifie si la balle est en contact avec le mur gauche un droite (vous pouvez utiliser des sf::FloatRect ou simplement contrôler la coordonnée horizontale de la balle).
      Indications la tournure sprite.getPosition().x<=0 permet par exemple de tester si la balle sort par le mur gauche.
    • Le cas échéant, fait rebondir la balle selon le dessin ci-après. Indication: il suffit de changer l'angle en π-l'angle actuel. Utilisez la fonction setDirectionfromAngle!
  4. Appelez cette fonction dans la fonction move de la classe Ball (qui on le rappelle est appelée dans la boucle de rendu). La balle devrait maintenant rebondir contre les murs gauche et droite (pour le tester, vous pouvez modifier l'angle initial de la balle à 0, c'est-à-dire mouvement horizontal).

  5. Finalement, les collisions avec les paddles! Ajoutez dans Ball une méthode void paddleBounce() changeant la direction de la balle pour obtenir un rebond comme sur l'image ci-dessous. Indication: comme avant, utilisez setDirectionfromAngle. Il suffit de prendre l'opposé de l'angle.
  6. Dans la boucle de rendu, ajoutez une condition vérifiant si l'un des paddles rentre en collision avec la balle (utilisez à nouveau les sf::FloatRect). Dans ce cas, appelez la fonction paddleBounce de Ball.
    Indications
    Le rectangle associé au Paddle est de type sf::RectangleShape. Il est donc possible d'invoquer rectangle.getGlobalBounds() qui retourne un sf::FloatRect (rectangle englobant le Paddle). La même méthode getGlobalBounds s'applique aussi au sprite associé à la balle. Pour tester s'il y a collision entre un Paddle et une Ball, il suffit donc de tester si les sf::FloatRect retournés par les méthodes getGlobalBounds (respectivement appliquées au sprite de Ball et au rectangle de Paddle) s'intersectent.

Vous pouvez télécharger une solution de cette étape (nous y avons laissé des petits messages de "debug" indiquant quand une collision est détectée et nous vous laissons le soin de les éliminer).

Tutorial SFML
Corentin Perret, Jamila Sam - 2013-2019