Partage
  • Partager sur Facebook
  • Partager sur Twitter

Tri des membres d'une classe (std::vector ou std::list)

à l'aiiiide !!!!!! :'(

Sujet résolu
    16 septembre 2007 à 7:46:30

    Salut à tous ! :)
    je suis actuellement en train de programmer un petit jeu sans prétentions en C++/SDL

    Malheureusement je suis confronté à un problème sur lequel je bloque grave ... :(

    J'ai créé un systeme qui charge une carte et l'affiche à l'écran.
    Sur cette carte se balladent actuellement quelques personnages controlés
    par ordinateur. Jusque là tout va bien, surtout qu'ils tiennent compte
    des collisions etc ... ;)

    Ces persos sont une instansation de la classe "Npc" que j'ai codé :

    1. Npc *m_npc;
    2. m_npc = new Npc[MAXNPCS];


    ainsi je créé un tableau de X Npc et j'accède à chaque Npc grâce à son
    indice : m_npc[0] pour le 1er Npc par exemple.

    Mon problème intervient lorsque je dessine mes persos à l'écran;
    en effet, je les dessine un par un avec une boucle for :

    1. for(int i = 0; i < MAXNPCS; i++) m_npc[i].draw();


    Ce qui fait que le perso avec le plus haut indice est toujours repeint en dernier.

    Ceci pose problème lorsque certains persos repeints ont une coordonnée
    Y inférieure aux autres Npcs repeints précédemment.

    En fait cela créé un problème de chevauchement (les jambes d'un perso + haut sur l'écran vont
    être peintent sur la tête d'un perso qui est plus bas si ce dernier a un indice inférieur dans le tableau m_npc).

    Je voudrai donc trier mon tableau m_npc par la valeur de sa propriété ".m_inf.m_y"
    (m_npc[x].m_inf.m_y donc, qui est un entier qui représente la position Y du npc X. PS : m_inf est une structure)
    pour qu'ainsi m_npc[0] ai le m_inf.m_y le plus petit et m_npc[n] ai le m_inf.m_y le plus grand.

    Je me doute qu'il serait dangereux de toucher directement à la structure du tableau m_npc, j'ai
    donc pensé à utiliser une std::list ou un std::vector qui contiendrai le pointeur de chaque indice
    du tableau et à trier cette liste (ou ce vector) grâce à sa fonction sort().
    Pour repeindre ensuite, il me suffirait de parcourir la liste (ou le vector), et d'utiliser chaque
    pointeur qu'il contient pour repeindre mes persos.

    L'autre problème c'est que je n'y connais absolument rien en list ou vector :(

    Pourriez vous m'aider svp ?
    • Partager sur Facebook
    • Partager sur Twitter
      16 septembre 2007 à 8:28:23

      Un petit cours sur les algorithmes de tri avec la stl: sur developpez.com ;)
      • Partager sur Facebook
      • Partager sur Twitter
        16 septembre 2007 à 8:50:02

        Si tu utilise le vector ou la liste tu peux facilement utiliser la fonction sort en incluant "<algorithm>"

        1. list<Npc> container; // ou vector<Npc> au choix
        2. sort( container.begin(), container.end() )


        désavantage : tu dois surcharger l'opérateur < en tenant compte de seulement valider qu'il soit devant... c'est pas très propre.

        tu peux aussi utiliser un "predicate" qui est une fonction pouvant valider 2 éléments d'un même conteneur de la STL (vector, list, etc)

        tu peux définir dans ta classe Npc une fonction estDerriere() qui valide si un personnage est devant un autre et ensuite définir une fonction qui va prendre 2 Npc en paramètre et retour si le premier est derriere le 2e

        1. // dans la classe Npc : Déclarée "static"
        2. class Npc
        3. {
        4.     //...
        5.     static bool estDerriere( const Npc & n1, const Npc & n2 );
        6. }
        7. // Définition
        8. bool Npc::estDerriere( const Npc & n1, const Npc & n2 )
        9. {
        10.     return n1.m_y < n2.m_y;
        11. }
        12. //utilisation
        13. vector<Npc> v;
        14. // insertions de Npcs dans v
        15. sort( v.begin(), v.end(), Npc::estDerriere );


        Voilà une utilisation auto-documentée de la fonction sort, on sait qu'on veut trier les NPCs par leur position. Et de plus tu n'as pas a faire une surcharge d'un opérateur d'une façon douteuse.
        • Partager sur Facebook
        • Partager sur Twitter
          16 septembre 2007 à 10:16:47

          Pour la liste, il faut préférer la fonction membre. La fonction libre d'<algorithm> est inéficace sur les listes.
          • Partager sur Facebook
          • Partager sur Twitter
          C++: Blog|FAQ C++ dvpz|FAQ fclc++|FAQ Comeau|FAQ C++lite|FAQ BS| Bons livres sur le C++| PS: Je ne réponds pas aux questions techniques par MP.
            16 septembre 2007 à 10:35:01

            Il est a noter que les fonctions "sort" de la STL (la fonction libre ou la méthode du conteneur) peuvent aussi etre surchargées par un foncteur :
            En effet, tu as 2 possibilités :
            - Soit tu ne mets pas de foncteur, et il faudra surcharger l'opérateur < de la donnée : ce qui sera impossible si tu as un tableau de pointeur !
            - Soit utiliser un foncteur :
            c'est a dire que tu fais ton tableau ou ta liste de pointeurs, et, pour les trier, tu passes un foncteur en parametre, qui sera une fonction a qui tu diras littéralement de comparer le p->y et p2->y (p,p2 pointeurs sur structures de tes bonhommes, qui contiennent un membre y, sur lequel tu veux trier par exemple)

            Petit exemple (tapé de tete)
            1. std::vector<truc*> tab;  // la ou tu mets tes pointeurs
            2. // disons que truc contient une donnée y sur laquelle tu veux trier
            3. struct mon_foncteur
            4. {
            5.   bool operator()(const truc* s1, const truc* s2) const
            6.   {
            7.     return (s1->y) < (s2->y);
            8.   }
            9. };
            10. sort(tab.begin(),tab.end(),mon_foncteur);  // voila, ça te trie tout ça :)


            • Partager sur Facebook
            • Partager sur Twitter

            Recueil de code C et C++  http://fvirtman.free.fr/recueil/index.html

              16 septembre 2007 à 18:40:50

              Merci pour vos réponses, je test ça ce soir et je vous tien au courant ...

              je pensais plutot à conserver un tableau et à utiliser un vector pour stocker les adresses de chaque pointeur vers les indices du tableau. Ceci dit il est vrai que si utiliser un vector est exactement la même chose qu'utiliser un tableau je pourrais peut être passer directement par un vector pour initialiser et gérer tous mes objets npc ... qu'est ce que vous en pensez ??

              Merci.
              • Partager sur Facebook
              • Partager sur Twitter
                17 septembre 2007 à 0:37:12

                J'en pense que vu la nature profondément "entité-ienne" d'un PNJ, il conviendrait mieux de les gérer via des pointeurs -> std::vector<NPC*>, (ou std::vector<boost::shared_ptr<NPC> >, ou boost::ptr_array<NPC> si tu veux te dédouanner d'une partie de la gestion de leur mémoire)

                PS: les divers sort() ne sont pas _surchargés_ par un foncteur, mais _paramétrés_ (comme dans "polymorphisme paramétrique") :p
                • Partager sur Facebook
                • Partager sur Twitter
                C++: Blog|FAQ C++ dvpz|FAQ fclc++|FAQ Comeau|FAQ C++lite|FAQ BS| Bons livres sur le C++| PS: Je ne réponds pas aux questions techniques par MP.
                  17 septembre 2007 à 1:04:21

                  Bon ben j'ai, grâce à votre aide, résolu mon problème.
                  Voilà la solution pour les intéréssés :

                  Il faut déclarer les Npcs non pas dans un tableau mais dans un vector :

                  1. vector<Npc *> m_npc;


                  Pour initialiser chaque Npc, je fait :

                  1. for(int i = 0; i < MAXNPCS; i++)
                  2. {
                  3.     Npc *npc_m = new Npc();
                  4.     m_npc.push_back(npc_m);
                  5.     m_npc[i]->initNpc();
                  6. }


                  Ensuite, dans la classe Npc, j'ai ajouté cette fonction membre :

                  déclaration dans npc.h:

                  1. static bool estDerriere(Npc *npc1, Npc *npc2);


                  dans npc.cpp :

                  1. bool Npc::estDerriere(Npc *npc1, Npc *npc2)
                  2. {
                  3.     return npc1->m_inf.m_y < npc2->m_inf.m_y;
                  4. }


                  Enfin, avant de dessiner les Npc à l'écran, j'appel :

                  1. sort(m_npc.begin(), m_npc.end(), Npc::estDerriere);


                  Ce qui à pour effet de trier mon vecteur !

                  Par contre j'ai une dernière question. Quand je fait un push_back(npc_m), l'objet pointé par npc_m (donc une instance de Npc) est "recopié" dans le vecteur ou celui-ci contient juste l'adresse du pointeur ? Parce que je voudrais savoir si il faut à un moment ou un autre que je vide npc_m avec un delete.

                  Voilà, merci à tous :)
                  • Partager sur Facebook
                  • Partager sur Twitter
                    17 septembre 2007 à 5:25:53

                    si tu conserve des pointeurs de Npc il faut que tu les deletes effectivement...
                    • Partager sur Facebook
                    • Partager sur Twitter
                      17 septembre 2007 à 8:02:48

                      MatteX tu pourrais préciser stp :D ?
                      Au vu de ce que j'ai écrit au dessus, dois-je désallouer de la mémoire et comment ? Merci.

                      PS : j'utilise maintenant

                      1. stable_sort(m_npc.begin(), m_npc.end(), Npc::estDerriere);

                      au lieu de

                      1. sort(m_npc.begin(), m_npc.end(), Npc::estDerriere);


                      car stable_sort permet de ne pas inverser les Npc dans le vector qui ont la meme position Y. En effet, utiliser sort créé un problème au niveau du repeint des npc (les npc qui ont le même Y (et qui se chevauchent en X) sont repeint un coup l'un sur l'autre et un coup l'autre sur l'un ... enfin c'est pas très beau :p )

                      Hum encore une question tant que j'y suis. Dois-je concidérer le personnage que va controler le joueur comme un Npc aussi ? (Parce que lui aussi il faut qu'il soit repeint par dessus ou en dessous des npc en fonction de son Y... il faut donc que les npc et le personnage joueur soient dans le meme vector non ??)

                      Merci ^^

                      • Partager sur Facebook
                      • Partager sur Twitter
                        17 septembre 2007 à 9:44:10

                        Je vois que tu fais un for ou tu remplis ton tableau de pointeur, avec un new a chaque fois.
                        Dans ce cas la, oui, il faudra faire un for avec des delete de la meme maniere.

                        Mais par contre, je te donne une astuce plus rapide : (je réponds peut etre a coté de la plaque, je ne suis pas réveillé)

                        Tu gardes en mémoire un tableau de joueur :

                        1. std::vector<Mec> tab;   // tableau d'objets statiques


                        Dans ce tableau : l'élément d'indice 0 est toujours ton bonhomme principale, qu'il doive etre dessiné avant ou apres : index fixe.
                        Et derriere, tu fais un deuxieme tableau :

                        1. std::vector<Mec*> affich_tab; // vide au début, sinon tu clear avant ce for
                        2. for(i=0;i<tab.size();i++)  // parcours
                        3. {
                        4.   affich_tab.push_back(&tab[i]);  
                        5.        // ajout de l'adresse d'un gars depuis le tableau tab.
                        6. }
                        7. sort(affich_tab.begin(),affich_tab.end(),tonfoncteur);


                        tu n'as ainsi pas de new a faire, et ton tableau pointe directement sur les éléments de l'autre. Et dans ce cas la, il faut SURTOUT pas faire de delete sur les éléments de affich_tab (puisque tu n'auras pas fait de new)
                        ça va vite, ça évite de la construction d'objets, ça fait juste un déréférencement en plus :)
                        • Partager sur Facebook
                        • Partager sur Twitter

                        Recueil de code C et C++  http://fvirtman.free.fr/recueil/index.html

                          17 septembre 2007 à 21:47:46

                          Merci pour ton astuce Fvirtman !
                          En faisant comme ça, tu déclare 2 vectors; celui qui contient les npc et celui qui contient les adresses de chaque index du premier vector. Et je ne comprend pas trop l'utilité (à part utiliser + de mémoire que si il n'y avait qu'un seul vector :p ). Faire un new suivi d'un delete c'est pas non plus chi*nt lol :D

                          Le seul truc c'est que je ne sait pas exactement quelle boucle je dois utiliser pour désallouer.
                          Serait-ce celle ci ? :

                          1. for(int i = 0; i < MAXNPCS; i++)
                          2. {
                          3. delete m_npc[i];
                          4. }


                          tout simplement ?

                          Et enfin, je repose ma dernière question :
                          Dois-je concidérer le personnage que va controler le joueur comme un Npc aussi ? (Parce que lui aussi il faut qu'il soit repeint par dessus ou en dessous des npc en fonction de son Y... il faut donc que les npc et le personnage joueur soient dans le meme vector non ??)

                          Merci pour tous vos messages ^^
                          • Partager sur Facebook
                          • Partager sur Twitter

                          Tri des membres d'une classe (std::vector ou std::list)

                          × Après avoir cliqué sur "Répondre" vous serez invité à vous connecter pour que votre message soit publié.
                          × Attention, ce sujet est très ancien. Le déterrer n'est pas forcément approprié. Nous te conseillons de créer un nouveau sujet pour poser ta question.
                          • Editeur
                          • Markdown