IdentifiantMot de passe
Loading...
Mot de passe oublié ?Je m'inscris ! (gratuit)
Navigation

Inscrivez-vous gratuitement
pour pouvoir participer, suivre les réponses en temps réel, voter pour les messages, poser vos propres questions et recevoir la newsletter

SFML Discussion :

Comportement bizarre d'un setter


Sujet :

SFML

  1. #1
    Membre éprouvé Avatar de Charvalos
    Homme Profil pro
    Développeur Web
    Inscrit en
    Juin 2010
    Messages
    353
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Âge : 31
    Localisation : Suisse

    Informations professionnelles :
    Activité : Développeur Web

    Informations forums :
    Inscription : Juin 2010
    Messages : 353
    Points : 1 264
    Points
    1 264
    Par défaut Comportement bizarre d'un setter
    Bonjour à tous !

    Je suis en train de gentiment apprendre la bibliothèque SFML et pour cela, je m'amuse à créer un casse-briques. Pour le moment, j'arrive à faire bouger le paddle, la balle et gérer les collisions avec les bords de la fenêtre mais il m'arrive quelque chose d'assez bizarre.

    Quand je la balle touche le bord du bas, je fais un "reset" de la balle et du paddle en les remettant à leur emplacement d'origine. Cela fonctionne mais le problème vient du fait que la "vitesse" (le déplacement combiné sur l'axe X et l'axe Y) ne semble pas se remettre correctement.

    Tout d'abord, voici le code :

    Boule.cpp

    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    36
    37
    38
    39
    40
    41
    42
    43
    44
    45
    46
    47
    48
    49
    50
    51
    52
    53
    54
    55
    56
    57
    58
    59
    60
    61
    62
    63
    64
    65
    66
    67
    68
    69
    70
    71
    72
    73
    74
    75
    76
    77
    78
    79
    80
    81
    82
    83
    84
    85
    86
    87
    88
    89
    90
    91
    92
    93
    94
    95
    96
    97
    98
    99
    100
    101
    102
    103
    104
    105
    106
    107
    108
    109
    #include "Boule.h"
    #include <Graphics.hpp>
     
    using namespace std;
     
    /**
    Définition :    Constructeur d'une boule
    Paramètres :    radius -> rayon de la boule
                    posX -> position de la boule sur l'axe des X
                    posY -> position de la boule sur l'axe des Y
                    defaultSpeedX -> Vitesse par défaut sur l'axe X
                    defaultSpeedY -> Vitesse par défaut sur l'axe Y
    **/
    Boule::Boule(int radius, int posX, int posY, int defaultSpeedX, int defaultSpeedY)
    {
        this->circle.setRadius(radius);
        this->circle.setPosition(posX, posY);
        this->setSpeed(defaultSpeedX, defaultSpeedY);
    }
     
    /**
    Définition :    Permet d'afficher une boule à l'écran
    Paramètres :    target -> fenêtre dans laquelle afficher l'objet
    **/
    void Boule::display(sf::RenderTarget &target)
    {
        target.draw(this->circle);
    }
     
    /**
    Définition :    Permet de démarrer le déplacement de la balle
    **/
    void Boule::start()
    {
        this->circle.move(this->speed);
    }
     
    /**
    Définition :    Permet de faire bouger la balle
    **/
    void Boule::update()
    {
        this->circle.move(this->speed);
    }
     
    /**
    Définition :    Permet de détecter si la balle touche un bord. Tant qu'elle touche un bord différent du bas ou qu'elle n'en touche pas, la fonction renvoie false
                    et indique quel bord est touché
    Paramètres :    &window -> Fenêtre dans laquelle se trouve la balle
                    &side -> Indique quel bord a été touché
    Retour     :    false -> Aucun bord ou les bords haut, droit & gauche sont touchés
                    true -> Bord du bas est touché
    **/
    bool Boule::wallCollision(sf::Window &window, string &side)
    {
        int circleX = this->circle.getPosition().x;
        int circleY = this->circle.getPosition().y;
     
        int windowX = window.getSize().x;
        int windowY = window.getSize().y;
     
        //Aucun bord touché
        if((circleX > 0 && circleY > 0) && (circleX < windowX && circleY < windowY))
            return false;
        //Gauche
        else if(circleX <= 0 && circleY > 0)
        {
            side = "left";
            return false;
        }
        //Haut
        else if(circleX > 0  && circleY <= 0)
        {
            side = "top";
            return false;
        }
        //Bas
        else if(circleX < windowX && circleY >= windowY)
            return true;
        //Droite
        else
        {
            side = "right";
            return false;
        }
    }
     
    /**
    Définition :    Réinitialise la boule à sa place de départ
    Paramètres :    posX -> Position sur l'axe X
                    posY -> Position sur l'axe Y
                    defaultSpeedX -> Vitesse par défaut de la balle sur l'axe des X
                    defaultSpeedY -> Vitesse par défaut de la balle sur l'axe des Y
    **/
    void Boule::reset(int posX, int posY, int defaultSpeedX, int defaultSpeedY)
    {
        this->circle.setPosition(posX, posY);
        this->setSpeed(defaultSpeedX, defaultSpeedY);
    }
     
    /**
    Définition :    Permet d'ajuster la vitesse de la balle
    Paramètres :    speedX -> "Vitesse" sur l'axe des X
                    speedY -> "Vitesse" sur l'axe des Y
    **/
    void Boule::setSpeed(int speedX, int speedY)
    {
        this->speed = sf::Vector2f(speedX, speedY);
    }
    main.cpp

    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    36
    37
    38
    39
    40
    41
    42
    43
    44
    45
    46
    47
    48
    49
    50
    51
    52
    53
    54
    55
    56
    57
    58
    59
    60
    61
    62
    63
    64
    65
    66
    67
    68
    69
    70
    71
    72
    73
    74
    75
    76
    77
    78
    79
    80
    81
    82
    83
    84
    85
    86
    87
    88
    89
    90
    91
    92
    93
    94
    95
    96
    97
    98
    99
    100
    101
    102
    103
    104
    105
    106
    107
    108
    109
    110
    111
    112
    113
    114
    115
    116
    117
    118
    119
    120
    121
    122
    123
    124
    125
    126
    127
    128
    129
    130
    131
    132
    133
    134
    135
    136
    137
    138
    139
    140
    141
    142
    143
    144
    145
    146
    147
    148
    #include <SFML/Window.hpp>
    #include <SFML/Graphics.hpp>
    #include <iostream>
    #include "Rectangle.h"
    #include "Boule.h"
     
    using namespace std;
     
    int main()
    {
        //Variables associées au briques
        const int NB_BRIQUES = 10, NB_LIGNES = 10, POS_DEPART_X = 75, POS_DEPART_Y = 50, BRIQUE_WIDTH = 60, BRIQUE_HEIGHT = 15;
        int posX = POS_DEPART_X, posY = POS_DEPART_Y;
     
        //Paramètres pour le paddle et la balle
        const int PADDLE_POS_X = 350, PADDLE_POS_Y = 560, PADDLE_WIDTH = 100, PADDLE_HEIGHT = 10;
        const int BALLE_RADIUS = 7, BALLE_POS_X = 392, BALLE_POS_Y = 545, BALLE_DEFAULT_SPEED_X = -5, BALLE_DEFAULT_SPEED_Y = -5;
     
        //Paramètres de la fenêtre
        const int WINDOW_WIDTH = 800, WINDOW_HEIGHT = 600, WINDOW_BIT_PIXEL = 32, WINDOW_LIMIT_FPS = 60;
        const string WINDOW_TITLE = "Casse-brique";
     
        bool gameRunning = false, wallCollision = false;
        string sideWallCollision = "";
        int newMousePosX = 0, paddlePosX = 0, speed = 0;
        int i, j;
     
        //Création des différents objets
        Rectangle paddle(PADDLE_WIDTH, PADDLE_HEIGHT, PADDLE_POS_X, PADDLE_POS_Y);
        Rectangle briques[NB_BRIQUES][NB_LIGNES];
        Boule balle(BALLE_RADIUS, BALLE_POS_X, BALLE_POS_Y, BALLE_DEFAULT_SPEED_X, BALLE_DEFAULT_SPEED_Y);
     
        //Création d'une fenêtre de taille 800x600
        sf::RenderWindow App(sf::VideoMode(WINDOW_WIDTH, WINDOW_HEIGHT, WINDOW_BIT_PIXEL), WINDOW_TITLE);
        App.setFramerateLimit(WINDOW_LIMIT_FPS);
     
        //Création d'un mur de briques
        for(i = 0; i < NB_LIGNES; i++)
        {
            for(j = 0; j < NB_BRIQUES; j++)
            {
                briques[i][j] = Rectangle(BRIQUE_WIDTH, BRIQUE_HEIGHT, posX, posY);
                posX += 65;
            }
     
            //Réinitialistion de la position en X
            posX = POS_DEPART_X;
            //On décale la prochaine ligne de 20 pixels sur l'axe des Y (15 étant la height d'une brique + 5 de marge entre chaque ligne)
            posY += 20;
        }
     
        //Programme ouvert
        while(App.isOpen())
        {
            sf::Event Event;
     
            while(App.pollEvent(Event))
            {
                //Si on clique sur la croix rouge ou qu'on presse sur la touche "Esc" du clavier, l'application se ferme
                if(Event.type == sf::Event::Closed || sf::Keyboard::isKeyPressed(sf::Keyboard::Escape))
                    App.close();
     
                //Lance le jeu dès qu'on clique sur le bouton gauche de la souris
                if(sf::Mouse::isButtonPressed(sf::Mouse::Left))
                {
                    balle.start();
                    gameRunning = true;
                    App.setMouseCursorVisible(false);
                }
     
                //Avec le clic droit, on réinitialise la partie
                if(sf::Mouse::isButtonPressed(sf::Mouse::Right))
                {
                    balle.reset(BALLE_POS_X, BALLE_POS_Y, BALLE_DEFAULT_SPEED_X, BALLE_DEFAULT_SPEED_Y);
                    paddle.reset(PADDLE_POS_X, PADDLE_POS_Y, PADDLE_WIDTH, PADDLE_HEIGHT);
                    gameRunning = false;
                    App.setMouseCursorVisible(true);
                }
     
                //Bouger la souris permet de bouger le paddle
                if(Event.type == sf::Event::MouseMoved && gameRunning)
                {
                    paddlePosX = paddle.getPosX();
                    newMousePosX = Event.mouseMove.x;
                    speed = newMousePosX - paddlePosX;
     
                    //Permet de savoir de quel côté il faut bouger le paddleb
                    if(newMousePosX > paddlePosX)
                        paddle.moveRight(App, speed);
                    else
                        paddle.moveLeft(speed);
                }
            }
     
            //Définition d'un fond
            App.clear(sf::Color(214,214,214));
     
            //Affichage des différents éléments
            paddle.display(App);
            balle.display(App);
     
            for(i = 0; i < NB_LIGNES; i++)
            {
                for(j = 0; j < NB_BRIQUES; j++)
                    briques[i][j].display(App);
            }
     
            //Si la partie est en cours
            if(gameRunning)
            {
                wallCollision = balle.wallCollision(App, sideWallCollision);
     
                //Tant qu'on ne touche pas le bord du bas, on regarde quel bord on touche afin de définir de quel côté doit partir la balle. Sinon, on remet l'emplacement d'origine
                if(!wallCollision)
                {
                    //Test pour savoir quel bord a été touché, sinon, la balle avance
                    if(sideWallCollision == "left")
                    {
                        balle.setSpeed(5, -5);
                        balle.update();
                    }
                    else if(sideWallCollision == "right")
                    {
                        balle.setSpeed(-5, 5);
                        balle.update();
                    }
                    else if(sideWallCollision == "top")
                    {
                        balle.setSpeed(5, 5);
                        balle.update();
                    }
                    else
                        balle.update();
                }
                else
                {
                    balle.reset(BALLE_POS_X, BALLE_POS_Y, BALLE_DEFAULT_SPEED_X, BALLE_DEFAULT_SPEED_Y);
                    paddle.reset(PADDLE_POS_X, PADDLE_POS_Y, PADDLE_WIDTH, PADDLE_HEIGHT);
                    App.setMouseCursorVisible(true);
                    gameRunning = false;
                }
            }
     
            App.display();
        }
     
        return EXIT_SUCCESS;
    }
    Mon problème vient avec la fonction setSpeed. Quand je l'appelle au moment où la balle touche les murs droite, gauche et du haut, cela fonctionne et la balle change de direction (ligne 117 à 130). Par contre, quand j'appelle sa fonction reset(), cela n'a pas l'air de fonctionner car quand je clique à nouveau sur le clic gauche de la souris, la balle bouge mais a gardé en mémoire sa dernière vitesse. En gros, si je réinitialise :

    - Alors qu'elle bouge vers la droite (après avoir touché le bord gauche), elle repart vers la droite après l'appelle de la fonction reset()
    - Alors qu'elle bouge vers le bas, elle repart vers le bas après l'appelle de la fonction reset()
    - Etc.

    Comme si l'appel de la fonction setSpeed() dans la fonction reset() ne fonctionnait pas alors que le même appel dans le constructeur fonctionne car si je modifie les valeurs des constantes au début du programme, la vitesse de base change. Je n'ai aucun message d'erreur donc j'avoue que là, je ne vois pas trop d'où peut venir le problème.

    Merci d'avance pour votre aide !

    P.S : les valeurs de déplacement sont des valeurs de test, pour le moment et les collisions avec le paddle et les briques ne sont pas encore gérées. Et il se peut que j'utilise beaucoup de this mais c'est plus parlant pour moi.
    "Non, je ne dois rien à personne
    Et je ne méprise personne".


    Je ne réponds pas aux message techniques par MP !

  2. #2
    Expert confirmé
    Inscrit en
    Mars 2005
    Messages
    1 431
    Détails du profil
    Informations forums :
    Inscription : Mars 2005
    Messages : 1 431
    Points : 4 182
    Points
    4 182
    Par défaut
    Tu ne réinitialises jamais sideWallCollision au sein de la boucle de jeu. La variable conserve donc la valeur obtenue lors d'un appel précédent.

    La signature de ta fonction bool Boule::wallCollision(sf::Window &, std::string &) est d'ailleurs particulièrement alambiquée. On retourne false, mais il faut tout de même tester side (pourquoi une std::string d'ailleurs ?) pour savoir si une collision s'est produite !? Vire-moi ce second paramètre et retourne un simple code entier (int ou enum, peu importe). Par exemple :

    • 0 : pas de collision ;
    • 1, 2, 3, 4 : collision avec le bord gauche, supérieur, droit, inférieur ;
    • 5 : collision avec le paddle ;
    • 6..n : collision avec une brique de type x.


    Si tu as besoin de complexifier le système par la suite, tu pourras modifier la méthode pour retourner un objet décrivant plus précisément la collision. Attention notamment aux collisions dans les angles : on peut considérer qu'il s'agit de plusieurs évènements simultanés !


    D'autre part, tu ne contrôles pas correctement ta vitesse : lorsque la balle rencontre un mur, tu dois inverser uniquement la composante orthogonale à ce mur (x pour un bord vertical, y pour le bord supérieur) et laisser l'autre inchangée.

  3. #3
    Membre éprouvé Avatar de Charvalos
    Homme Profil pro
    Développeur Web
    Inscrit en
    Juin 2010
    Messages
    353
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Âge : 31
    Localisation : Suisse

    Informations professionnelles :
    Activité : Développeur Web

    Informations forums :
    Inscription : Juin 2010
    Messages : 353
    Points : 1 264
    Points
    1 264
    Par défaut
    Ah oui, je n'avais pas fait attention à ça ! Cela fonctionne maintenant. Merci beaucoup !

    Pour la fonction wallCollision, c'est la solution qui m'a paru le plus simple. En fait, cela me permet de savoir quel bord a été touché et le problème que j'avais, c'est que je si retournais un simple true, il m'était impossible de savoir quel bord a été touché et par conséquent, de quel côté il faut renvoyer la balle et le test me paraît plus parlant en faisant sideWallCollision == "left" que wallCollision == 1. C'est peut-être le nom qui est mal choisi.

    Pour la vitesse, c'était uniquement des valeurs de test.
    "Non, je ne dois rien à personne
    Et je ne méprise personne".


    Je ne réponds pas aux message techniques par MP !

  4. #4
    Expert confirmé
    Inscrit en
    Mars 2005
    Messages
    1 431
    Détails du profil
    Informations forums :
    Inscription : Mars 2005
    Messages : 1 431
    Points : 4 182
    Points
    4 182
    Par défaut
    Déclare tes variables le plus tard possible. Ça limitera automatiquement leur utilisation au contexte où elles sont pertinentes et tu éviteras la plupart de ces erreurs d'inattention. N'hésite pas à les redéclarer à chaque tour de boucle, tu peux faire confiance au compilateur qui saura quoi faire.


    Citation Envoyé par Charvalos Voir le message
    [...]le test me paraît plus parlant en faisant sideWallCollision == "left" que wallCollision == 1
    Utilise une énumération, c'est fait pour cela : apporter de la sémantique à des valeurs numériques absconses.

  5. #5
    Expert éminent sénior

    Femme Profil pro
    Ingénieur développement logiciels
    Inscrit en
    Juin 2007
    Messages
    5 188
    Détails du profil
    Informations personnelles :
    Sexe : Femme
    Localisation : France, Essonne (Île de France)

    Informations professionnelles :
    Activité : Ingénieur développement logiciels

    Informations forums :
    Inscription : Juin 2007
    Messages : 5 188
    Points : 17 136
    Points
    17 136
    Par défaut
    J'approuve totalement les énumérations. Priviliégie les class enum, plus propres.

    Autre conseil, si tu places tes constantes (telles que NB_BRIQUES) dans une structure/classe dédiée (ou plusieurs), tu serais à trois fois rien d'avoir des fichiers de descriptions de niveaux.
    Ca te permettrait entre autre de détacher ton code des données. Ca t'éviterait de compiler à chaque modification des données, de permettre des modes de jeu.
    Mes principes de bases du codeur qui veut pouvoir dormir:
    • Une variable de moins est une source d'erreur en moins.
    • Un pointeur de moins est une montagne d'erreurs en moins.
    • Un copier-coller, ça doit se justifier... Deux, c'est un de trop.
    • jamais signifie "sauf si j'ai passé trois jours à prouver que je peux".
    • La plus sotte des questions est celle qu'on ne pose pas.
    Pour faire des graphes, essayez yEd.
    le ter nel est le titre porté par un de mes personnages de jeu de rôle

  6. #6
    Membre éprouvé Avatar de Charvalos
    Homme Profil pro
    Développeur Web
    Inscrit en
    Juin 2010
    Messages
    353
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Âge : 31
    Localisation : Suisse

    Informations professionnelles :
    Activité : Développeur Web

    Informations forums :
    Inscription : Juin 2010
    Messages : 353
    Points : 1 264
    Points
    1 264
    Par défaut
    Citation Envoyé par Matt_Houston Voir le message
    Déclare tes variables le plus tard possible. Ça limitera automatiquement leur utilisation au contexte où elles sont pertinentes et tu éviteras la plupart de ces erreurs d'inattention. N'hésite pas à les redéclarer à chaque tour de boucle, tu peux faire confiance au compilateur qui saura quoi faire.


    Utilise une énumération, c'est fait pour cela : apporter de la sémantique à des valeurs numériques absconses.
    Merci, je regarderai ce côté-là.

    Citation Envoyé par ternel Voir le message
    Petit conseil, si tu places tes constantes (telles que NB_BRIQUES) dans une structure/classe dédiée (ou plusieurs), tu serais à trois fois rien d'avoir des fichiers de descriptions de niveaux.
    Ca te permettrait entre autre de détacher ton code des données. Ca t'éviterait de compiler à chaque modification des données, de permettre des modes de jeu.
    Merci de l'astuce. Pour le moment, la gestion des niveaux, ce n'est pas encore pour tout de suite. ^^
    "Non, je ne dois rien à personne
    Et je ne méprise personne".


    Je ne réponds pas aux message techniques par MP !

  7. #7
    Expert éminent sénior

    Femme Profil pro
    Ingénieur développement logiciels
    Inscrit en
    Juin 2007
    Messages
    5 188
    Détails du profil
    Informations personnelles :
    Sexe : Femme
    Localisation : France, Essonne (Île de France)

    Informations professionnelles :
    Activité : Ingénieur développement logiciels

    Informations forums :
    Inscription : Juin 2007
    Messages : 5 188
    Points : 17 136
    Points
    17 136
    Par défaut
    Au contraire, le plus tôt tu sépareras les parties du programme, le moins tu auras de soucis.
    De même, le plus tôt tu réfléchis aux fonctionnalités à inclure, le moins tu devras tout recommencer parce que tu n'as pas pris en compte un point de variation.
    Mes principes de bases du codeur qui veut pouvoir dormir:
    • Une variable de moins est une source d'erreur en moins.
    • Un pointeur de moins est une montagne d'erreurs en moins.
    • Un copier-coller, ça doit se justifier... Deux, c'est un de trop.
    • jamais signifie "sauf si j'ai passé trois jours à prouver que je peux".
    • La plus sotte des questions est celle qu'on ne pose pas.
    Pour faire des graphes, essayez yEd.
    le ter nel est le titre porté par un de mes personnages de jeu de rôle

  8. #8
    Membre éprouvé Avatar de Charvalos
    Homme Profil pro
    Développeur Web
    Inscrit en
    Juin 2010
    Messages
    353
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Âge : 31
    Localisation : Suisse

    Informations professionnelles :
    Activité : Développeur Web

    Informations forums :
    Inscription : Juin 2010
    Messages : 353
    Points : 1 264
    Points
    1 264
    Par défaut
    Je suis tout à fait d'accord mais je n'ai pas prévu d'en faire un gros truc. Mais j'aimerais bien savoir comment tu fais ça ? Tu fais, par exemple, un fichier constant.h ou level_1.h et dedans, tu y intègres les constantes ?

    Et merci pour le type enum. J'ai regardé et c'est vrai que c'est bien pratique. Par contre, est-ce que je l'utilise correctement ? (Je pense vu que cela fonctionne et que j'ai pas d'erreur de compilation).

    Boule.cpp
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    int Boule::wallCollision(sf::Window &window)
    {
        int circleX = this->circle.getPosition().x;
        int circleY = this->circle.getPosition().y;
     
        int windowX = window.getSize().x;
        int windowY = window.getSize().y;
     
        //Aucun bord touché
        if((circleX > 0 && circleY > 0) && (circleX < windowX && circleY < windowY))
            return 0;
        //Gauche
        else if(circleX <= 0 && circleY > 0)
            return 1;
        //Haut
        else if(circleX > 0  && circleY <= 0)
            return 3;
        //Bas
        else if(circleX < windowX && circleY >= windowY)
            return 4;
        //Droite
        else
            return 2;
    }
    main.cpp

    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
           //Si la partie est en cours
            if(gameRunning)
            {
                enum side {left = 1, right = 2, top = 3, bottom = 4};
                int sideWall;
     
                sideWall = balle.wallCollision(App);
     
                if(sideWall == left)
                {
                    balle.setSpeed(5, -5);
                    balle.update();
                }
                else if(sideWall == right)
                {
                    balle.setSpeed(-5, 5);
                    balle.update();
                }
                else if(sideWall == top)
                {
                    balle.setSpeed(5, 5);
                    balle.update();
                }
                else if(sideWall == bottom)
                {
                    balle.reset(BALLE_POS_X, BALLE_POS_Y, BALLE_DEFAULT_SPEED_X, BALLE_DEFAULT_SPEED_Y);
                    paddle.reset(PADDLE_POS_X, PADDLE_POS_Y, PADDLE_WIDTH, PADDLE_HEIGHT);
                    App.setMouseCursorVisible(true);
                    gameRunning = false;
                }
                else
                    balle.update();
            }
    "Non, je ne dois rien à personne
    Et je ne méprise personne".


    Je ne réponds pas aux message techniques par MP !

  9. #9
    Expert éminent sénior
    Avatar de koala01
    Homme Profil pro
    aucun
    Inscrit en
    Octobre 2004
    Messages
    11 612
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Âge : 52
    Localisation : Belgique

    Informations professionnelles :
    Activité : aucun

    Informations forums :
    Inscription : Octobre 2004
    Messages : 11 612
    Points : 30 612
    Points
    30 612
    Par défaut
    Salut,
    Citation Envoyé par Charvalos Voir le message
    Je suis tout à fait d'accord mais je n'ai pas prévu d'en faire un gros truc. Mais j'aimerais bien savoir comment tu fais ça ? Tu fais, par exemple, un fichier constant.h ou level_1.h et dedans, tu y intègres les constantes ?
    Cela va même beaucoup plus loin que cela, à vrai dire...

    Car, si il existe des constantes de compilation (des truc du genre de double const pi = 3.145926;), il y a énormément de cas dans lesquels nous ne considérons "quelque chose" comme une constante qu'à cause... de l'utilisation que nous pouvons en faire. Ainsi, dans un code proche de
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    void foo(std::string const & str){
        /* je peux manipuler à ma guise str, tant que je n'essaye pas de la modifier */
    }
    int main(){
        /* un petit message pour faire savoir à l'utilisateur ce que l'on attend de lui (c'est toujours sympa) */
        std::cout<<"veuillez introduire n'importe quel texte : ";
        std::string recup; // ce n'est pas une constante !!!
        std::getline(std::cin, recup);
        foo(recup); // mais elle sera considérée comme une constante dans foo
    }
    Du coup, si tu regroupe les données (qui sont des constantes dans ton code d'origine) dans une (ou plusieurs) structure(s) proche(s) de
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    /* toutes tes données constantes servent à la configuration*/
    struct Config{
        /* les dimensions ne sont jamais négatives : un type non signé est bien plus adéquat */
        size_t width;
        size_t height;
        size_t level; // petit ajout : c'est bon de connaitre le numéro du niveau ;)
        size_t nbBricks; // le nombre de briques
        size_t lines; // le nombre de lignes
        size_t startX; // correspond à POS_DEPART_X
        size_t stratY; // correspond à POS_DEPART_Y
    };
    Tu pourrais les "englober" (par exemple dans une classe LevelConfig), qui ne permettrait de les récupérer que... sous une forme constante, et qui déléguerait le chargement de ces structures à des classes dédiées
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
     
    class Loader{
    public:
        static Config load(filename);
     
    };
    struct  LevelConfig{
    public:
       /* Le fait qu'une donnée est considérée constante ne change en rien un fait majeur :
        * elle doit bel et bien être construite à un moment donnée.
        *
        * On profite de son constructeur de copie dans la liste d'initialisation, car elle ne sera
        * constante qu'une fois que nous serons dans le code du constructeur de la classe
        */
       LevelConfig(std::string const & filename) : config{Loader::load(filename)}{}
       Config  config;
    };
    Une fois que tu en es là, il ne te reste "plus" qu'à introduire une variable de type LevelConfig dans une portée dans laquelle elle sera recréée pour chaque niveau, par exemple, sous une forme proche de
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    int main(){
        std::string const basename{"level"};
        size_t levle{1};
        bool quit{fase};
       /* la boucle principale  */
       do{
           /* on crée le nom du fichier correspondant au niveau */
           std::string filename{basename};
           filenae.append(std::to_string(level)
                    .append(".txt");
           LevelConfig const config{filename}; // elle sera créé, mais tout son contenu sera considéré comme constant
           /* on accède à config.config sans problème, tant qu'on n'essaye pas 
            * de modifier les valeurs
            */
           while(condition){
                /* la boucle d'exécution */
     
           }
     
       }    while(! quit);
    }
    Bien sur, toute cette "logistique" devrait sans doute prendre place dans une classe dédiée (comme "Game"), de manière à pouvoir te contenter d'un code aussi simple que
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    int main(){
        Game g; 
        g.run(); // les boucles sont incluses ici ;)
        return 0;
    }
    Mais l'idée reste sensiblement la même
    Et merci pour le type enum. J'ai regardé et c'est vrai que c'est bien pratique. Par contre, est-ce que je l'utilise correctement ? (Je pense vu que cela fonctionne et que j'ai pas d'erreur de compilation).

    Boule.cpp
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    int Boule::wallCollision(sf::Window &window)
    {
        int circleX = this->circle.getPosition().x;
        int circleY = this->circle.getPosition().y;
     
        int windowX = window.getSize().x;
        int windowY = window.getSize().y;
     
        //Aucun bord touché
        if((circleX > 0 && circleY > 0) && (circleX < windowX && circleY < windowY))
            return 0;
        //Gauche
        else if(circleX <= 0 && circleY > 0)
            return 1;
        //Haut
        else if(circleX > 0  && circleY <= 0)
            return 3;
        //Bas
        else if(circleX < windowX && circleY >= windowY)
            return 4;
        //Droite
        else
            return 2;
    }
    petite info au passage : à part dans des cas très particulier, l'utilisation de this est totalement inutile dans les fonctions membres d'une classe : si on ne précise pas, par exemple, que l'on veut faire appel à la fonction membre ou à la donnée membre (qui devrait alors être publique) d'un paramètre sous la forme de parametre.fonctionMembre(), le compilateur considère par défaut que l'on veut parler d'une fonction membre ou d'une donnée membre qui appartient forcément à l'objet courant
    main.cpp

    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
           //Si la partie est en cours
            if(gameRunning)
            {
                enum side {left = 1, right = 2, top = 3, bottom = 4};
                int sideWall;
     
                sideWall = balle.wallCollision(App);
     
                if(sideWall == left)
                {
                    balle.setSpeed(5, -5);
                    balle.update();
                }
                else if(sideWall == right)
                {
                    balle.setSpeed(-5, 5);
                    balle.update();
                }
                else if(sideWall == top)
                {
                    balle.setSpeed(5, 5);
                    balle.update();
                }
                else if(sideWall == bottom)
                {
                    balle.reset(BALLE_POS_X, BALLE_POS_Y, BALLE_DEFAULT_SPEED_X, BALLE_DEFAULT_SPEED_Y);
                    paddle.reset(PADDLE_POS_X, PADDLE_POS_Y, PADDLE_WIDTH, PADDLE_HEIGHT);
                    App.setMouseCursorVisible(true);
                    gameRunning = false;
                }
                else
                    balle.update();
            }
    En fait, au lieu d'utiliser un if ... else, tu devrait utiliser un test à choix multiple, sous une forme qui serait plutôt proche de
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    if(gameRunning){
        switch(sizeWall){
            case left :
                /* que faire si on est en collision avec le mur de gauche */
                break;
            case right :
                /* que faire si on est en collision avec le mur de droit */
                break;
            case top :
                /* que faire si on est en collision avec le mur du haut */
                break;
            case bottom :
                /* que faire si on est en collision avec le mur du bas*/
                break;
        }
    }
    qui présente un énorme avantage : si ton compilateur est correctement réglé, il sera en mesure d'émettre un avertissement si, d'aventure, le cas d'une valeur énumérée n'était jamais traité (soit de manière explicite à l'aide d'un case valeur, soit de manière implicite au travers du cas particulier default).

    C'est d'ailleurs pour cette raison que l'on insiste autant que faire se peut sur l'absolue nécessité de préférer les énumérations à toute autre possibilité plus ou moins similaire, la "moins mauvaise" étant la définition de constantes proches de const int left=1, la pire étant l'utilisation de la macro #define sous la forme de #define left 1Note au passage que tu n'est pas forcément obligé de fournir une valeur spécifique pour chaque valeur énumérée: Si tu n'indique pas de valeur spécifique pour une valeur énumérée, la toute première prendra la valeur 0 par défaut (à moins que tu ne définisse une valeur différente) et toutes les suivantes prendront la valeur de celle qui les précèdent +1.
    Ainsi, si tu avais une énumération proche de
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    enum Wall {
        none, // aucun
        left,
        top,
        right,
        bottom
    };
    none vaudrait d'office 0 (c'est la première, et on n'a pas donné de valeur pour elle), left vaudrait d'office 0 (valeur de celle qui précède) +1 (= ... 1, tiens donc), top vaudrait d'office 2, right vaudrait d'office 3 et bottom vaudrait d'office 4. Même si, l'un dans l'autre, on s'en foutrait pas mal car le sens que l'on trouve dans les termes none, left, right, top et bottom est bien plus intéressant que la valeur qu'ils représentent

    En outre, bien qu'il n'y ait "rien de grave" au fait de définir un type de donnée (qu'il s'agisse d'une classe, d'une structure, d'une énumération ou d'une union) à l'intérieur d'une fonction (comme tu l'as si bien fait remarquer : le compilateur ne s'en est pas plaint ), et bien que cela puisse même être très intéressant dans certains circonstances particulières, il faut toujours garder en tête que les types de données que nous créons sont destinés à représenter des notions qui apparaissent sous la forme de noms dans notre analyse (alors que les fonctions correspondent à des notions qui apparaissent sous la forme de verbe dans celle-ci), et que ces "notions" ont souvent une utilité qui s'étend bien au-delà de la possibilité de n'être utilisées que dans une seule et unique fonction.

    Or, les règles concernant la portée d'une définition restent totalement applicables à la définition des types de données : si tu définis un type de données personnalisé à l'intérieur d'une portée quelconque (en gros, entre une accolade ouvrante { et une accolade fermante }), il ne sera accessible qu'à l'intérieur de cette portée.

    Aussi, en définissant ton énumération à l'intérieur de ta fonction main() (même pire, d'ailleurs: à l'intérieur d'un test qui se trouve dans une boucle de ta fonction main), tu réduis forcément la portée à l'intérieur de laquelle elle est accessible à l'espace qui se trouve entre la fin de la définition de ton énumération (le point-virgule) et... l'accolade fermante du if(blabla).

    Je l'ai dit : cela peut être intéressant dans certains cas, mais, tant qu'à faire, il se peut que tu souhaites disposer de cette énumération à une "plus large échèle" .
    A méditer: La solution la plus simple est toujours la moins compliquée
    Ce qui se conçoit bien s'énonce clairement, et les mots pour le dire vous viennent aisément. Nicolas Boileau
    Compiler Gcc sous windows avec MinGW
    Coder efficacement en C++ : dans les bacs le 17 février 2014
    mon tout nouveau blog

  10. #10
    Membre éprouvé Avatar de Charvalos
    Homme Profil pro
    Développeur Web
    Inscrit en
    Juin 2010
    Messages
    353
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Âge : 31
    Localisation : Suisse

    Informations professionnelles :
    Activité : Développeur Web

    Informations forums :
    Inscription : Juin 2010
    Messages : 353
    Points : 1 264
    Points
    1 264
    Par défaut
    Merci beaucoup ! Je regarderai ça de plus près quand j'aurai fini mes gestions de collisions.

    Citation Envoyé par koala01 Voir le message
    petite info au passage : à part dans des cas très particulier, l'utilisation de this est totalement inutile dans les fonctions membres d'une classe : si on ne précise pas, par exemple, que l'on veut faire appel à la fonction membre ou à la donnée membre (qui devrait alors être publique) d'un paramètre sous la forme de parametre.fonctionMembre(), le compilateur considère par défaut que l'on veut parler d'une fonction membre ou d'une donnée membre qui appartient forcément à l'objet courant
    C'est ce que j'avais cru comprendre. Comme je l'ai dit, c'est une habitude que je tiens du PHP et pour moi, cela me paraît plus lisible. Est-ce qu'il y a une diminution des performances en indiquant le this à chaque fois ?

    Citation Envoyé par koala01 Voir le message
    En fait, au lieu d'utiliser un if ... else, tu devrait utiliser un test à choix multiple, sous une forme qui serait plutôt proche de
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    if(gameRunning){
        switch(sizeWall){
            case left :
                /* que faire si on est en collision avec le mur de gauche */
                break;
            case right :
                /* que faire si on est en collision avec le mur de droit */
                break;
            case top :
                /* que faire si on est en collision avec le mur du haut */
                break;
            case bottom :
                /* que faire si on est en collision avec le mur du bas*/
                break;
        }
    }
    qui présente un énorme avantage : si ton compilateur est correctement réglé, il sera en mesure d'émettre un avertissement si, d'aventure, le cas d'une valeur énumérée n'était jamais traité (soit de manière explicite à l'aide d'un case valeur, soit de manière implicite au travers du cas particulier default).
    J'ai testé avec un switch (j'aurais dû y penser plus tôt ) et bizarrement, la balle n'avance plus toute seule alors que si je fais un if, elle avance bien toute seule une fois qu'on a cliqué sur le bouton gauche de la souris.

    Citation Envoyé par koala01 Voir le message
    C'est d'ailleurs pour cette raison que l'on insiste autant que faire se peut sur l'absolue nécessité de préférer les énumérations à toute autre possibilité plus ou moins similaire, la "moins mauvaise" étant la définition de constantes proches de const int left=1, la pire étant l'utilisation de la macro #define sous la forme de #define left 1Note au passage que tu n'est pas forcément obligé de fournir une valeur spécifique pour chaque valeur énumérée: Si tu n'indique pas de valeur spécifique pour une valeur énumérée, la toute première prendra la valeur 0 par défaut (à moins que tu ne définisse une valeur différente) et toutes les suivantes prendront la valeur de celle qui les précèdent +1.
    Ainsi, si tu avais une énumération proche de
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    enum Wall {
        none, // aucun
        left,
        top,
        right,
        bottom
    };
    none vaudrait d'office 0 (c'est la première, et on n'a pas donné de valeur pour elle), left vaudrait d'office 0 (valeur de celle qui précède) +1 (= ... 1, tiens donc), top vaudrait d'office 2, right vaudrait d'office 3 et bottom vaudrait d'office 4. Même si, l'un dans l'autre, on s'en foutrait pas mal car le sens que l'on trouve dans les termes none, left, right, top et bottom est bien plus intéressant que la valeur qu'ils représentent
    Merci de l'info mais je le savais déjà. J'ai fait mes recherches sur internet avant.
    "Non, je ne dois rien à personne
    Et je ne méprise personne".


    Je ne réponds pas aux message techniques par MP !

+ Répondre à la discussion
Cette discussion est résolue.

Discussions similaires

  1. Comportement bizarre de mes FPS
    Par Ekinoks dans le forum OpenGL
    Réponses: 7
    Dernier message: 22/08/2005, 15h14
  2. xsl:test .... avec comportement bizarre
    Par Blue LC dans le forum XMLRAD
    Réponses: 2
    Dernier message: 10/06/2005, 13h56
  3. [ACESS][MEMO][ISNULL]Comportement bizarre
    Par seb.49 dans le forum ASP
    Réponses: 2
    Dernier message: 09/06/2004, 10h44
  4. [HttpClient] comportement bizarre, saute des catch()...
    Par iubito dans le forum Développement Web en Java
    Réponses: 4
    Dernier message: 04/02/2004, 15h25
  5. [Sybase] Comportement bizarre d'une table
    Par sdozias dans le forum Sybase
    Réponses: 4
    Dernier message: 03/02/2004, 10h39

Partager

Partager
  • Envoyer la discussion sur Viadeo
  • Envoyer la discussion sur Twitter
  • Envoyer la discussion sur Google
  • Envoyer la discussion sur Facebook
  • Envoyer la discussion sur Digg
  • Envoyer la discussion sur Delicious
  • Envoyer la discussion sur MySpace
  • Envoyer la discussion sur Yahoo