Partage
  • Partager sur Facebook
  • Partager sur Twitter

Comment structurer un jeu en c++

    16 juillet 2013 à 22:07:49

    Le problème des singleton en MT c'est l'initialisation. Et les solutions (qui laisse toute liberté à l'utilisateur) à ce problème sont relativement rares (je ne crois pas encore en avoir vu une "clé en main") et les "pseudo" solutions qu'on peut trouver ici et là on tendance à être "lourde" (même si ce n'est pas critique en général) et incorrecte. En C++11, le problème disparaît (et l'algorithme utilisé par les compilateurs pour ça est loin d'être un enfant de coeur).

     Le problème du singleton est que les utilisateurs (débutant ?) ont tendances à oublier que singleton ce n'est pas juste "être unique", mais "être unique et fournir un point d'accès global". Et il se trouve qu'on voit beaucoup de singleton qui n'existe que pour le caractère "unique" : ce n'est pas pour ça que ce pattern est fait. (cf. définition du pattern, Gamma et Al.).

    Et cette définition du pattern le rend donc par nature très proche des variables global en C++, la seul différence est une politique d'initialisation / destruction plus flexible. Et si les utilisateurs pensent souvent à la première, la second passe en général à la trappe (alors que la question se pose sérieusement, surtout quand on fait interagir plusieurs singleton).

    Pour ce qui est de la conception, j'ai suivit en partie le sujet, et je crois que le début ne traite pas de la même chose que la fin. Au début ce dont parlait lmghs (et les liens qu'il cite) ce sont des éléments de conception sur le système de jeu en soi, pas sur le reste, alors que l'ECS discuté après traite de l'interaction de l'ensemble des composants d'une application. Les deux sujets ont un intérêt, mais sont différents. Pour illustrer ceci, si vous regardez les exemples dans les liens cité par lmghs, il n'est pas question de graphisme, de son et autre, juste de ce qui "fait" le fond du jeu.

    J'avais repris un des codes d'exemple pour y intégrer un affichage graphique, on tombe sur un problème qui est un casse-tête si on essaie de le traiter en même temps que le premier, mais plus facilement si on à penser le premier pour être orthogonal au reste.

    -
    Edité par Freedom 16 juillet 2013 à 22:16:48

    • Partager sur Facebook
    • Partager sur Twitter
    FaQ : Fr | En 1 2 | C++11 | Template || Blog : Deloget | C++|Boost--Dev | C++Next | GotW || Installer Boost
      16 juillet 2013 à 22:16:31

      Pour le système qui essaye d'éviter l'héritage. (components, world, système et entité.)

      Je sais pas si en c++ il y a moyen de vérifier par exemple si une entité à une position ou pas, je pense pas qu'on puisse retirer comme ça des variables aux classes et se serait pas une bonne idée.

      Je pense que une variable booléenne serait plus appropriée dans ce cas pour savoir si une arme est dans l'inventaire ou pas par exemple et donc si elle doit être affichée ou pas. (Bref j'utilise pas mal de variables booléennes pour savoir si je dois afficher les entités ou pas, ça m'évite ainsi de devoir les recréer et les supprimer à chaque fois, à la mort d'un monstre par exemple, je ne l'affiche pas quand il meurt et je le réaffiche ailleurs quand il réapparait.)

      C'est un système de ce genre que j'utilise qui est pour moi le meilleur système mais on ne peut pas se passer complètement de l'héritage quand même c'est même mieux dans le cas de l'IA par exemple de faire de l'héritage  :

      Si le monstre doit prendre une décision différente suivant le type de monstre, suffit d'avoir une variable type dans la classe monstre qui est une énumération, et donc d'avoir aussi une énumération qui contient tout les types de monstres de la classe Monstre, ainsi si on rajoute un nouveau type de monstre, ou d'objet, je n'ai qu'a rajouté une variable dans l'énumération.

      Et dans l'IA on fait donc un getType() pour savoir quel type de monstre c'est et là on applique l'IA. :)

      Voila là je reviens au problème cité plus haut.

      -
      Edité par StarShadow 16 juillet 2013 à 22:23:34

      • Partager sur Facebook
      • Partager sur Twitter
        16 juillet 2013 à 22:23:31

        Le système que tu proposes ici est quand même très rigide : si je veux ajouter un type de monstre, alors je dois recompiler ? Je trouve ceci très contraignant par rapport à rajouter une description des caractéristiques de base d'un type de monstre dans un fichier de ressource (chose qu'un game designer peut faire via une interface que tu lui proposerais).

        • Partager sur Facebook
        • Partager sur Twitter
        FaQ : Fr | En 1 2 | C++11 | Template || Blog : Deloget | C++|Boost--Dev | C++Next | GotW || Installer Boost
          16 juillet 2013 à 22:33:31

          Le problème des singleton en MT c'est l'initialisation. Et les solutions (qui laisse toute liberté à l'utilisateur) à ce problème sont relativement rares (je ne crois pas encore en avoir vu une "clé en main") et les "pseudo" solutions qu'on peut trouver ici et là on tendance à être "lourde" (même si ce n'est pas critique en général) et incorrecte. En C++11, le problème disparaît (et l'algorithme utilisé par les compilateurs pour ça est loin d'être un enfant de coeur).


          Ha ça si on considère qu'un singleton est juste une variable globale unique alors là c'est sûr que c'est faux.

          Mais je vois pas en quoi le c++ viendrait régler le problème là dedans, que ce soit en c++ 2000 ou en c++11 il faut faire interagir les singletons entre eux, sinon, ça ne va pas.

          Ce sont bien plus que de simples éléments unique mais des éléments avec des méthodes statiques qui doivent permettre à chaque singleton de pouvoir interagir entre eux.

          • Partager sur Facebook
          • Partager sur Twitter
            16 juillet 2013 à 22:37:06

            Le système que tu proposes ici est quand même très rigide : si je veux ajouter un type de monstre, alors je dois recompiler ? Je trouve ceci très contraignant par rapport à rajouter une description des caractéristiques de base d'un type de monstre dans un fichier de ressource (chose qu'un game designer peut faire via une interface que tu lui proposerais).


            Ecoute je peux toujours faire une interface avec l'éditeur de map qui permettrait de le faire, ça, c'est pas un soucis!

            Pour le moment, je n'en ai pas vraiment besoin, vu que je fais juste des tests, mais tu as raisons plus tard je devrais faire ça. (Editeur de monstre, éditeur de quêtes, etc..., pour les non programmeurs.)

            -
            Edité par StarShadow 16 juillet 2013 à 22:37:46

            • Partager sur Facebook
            • Partager sur Twitter
              17 juillet 2013 à 0:26:22

              Pour le singleton. Il y a deux problèmes. Le premier problème est un problème technique que l'on retrouve en particulier en C++98/03 -- le C++11 apporte enfin une solution de ce côté là. Il est impossible de garantir une construction paresseuse qui soit thread-safe en C++98/03. Ou alors il faut payer super cher à l'intérieur de la fonction getInstance() (ou assimilée) en lockant tout accès par un mutex de portée maximale (le double locking pattern qui était utilisé dans ACE a fini par être démontré comme ne marchant pas). Du coup, on se retrouve vite avec des singletons qui sont construits au niveau du main avant la création du moindre thread. Comment dire que l'on perd probablement le seul intérêt des singletons : la construction paresseuse.

              Le second problème, qui est un problème de fond. Un singleton n'est rien d'autre qu'une variable globale, avec un hack en plus qui interdit à une autre variable globale du même type d'exister (ce qui concrètement, a très peu d'intérêt). Sauf que vu que cela a été publié dans le GoF, les gens ont fermé les yeux sur la vraie nature de ces objets : des variables globales.
              Certes, il est facile de fermer les yeux en disant: "nawak! la construction de l'objet n'a rien à voir: elle n'arrive qu'à la première utilisation [passons un instant sur l'aspect que la construction paresseuse MT-safe est impossible en C++98/03, ou très très chère].". OK. je sais que cela fait mal, mais ouvrons les yeux un instant.
              Pourquoi est-ce qu'elles sont critiquées ces variables globales ? -> parce qu'elles sont accessibles partout, qu'elles rajoutent du couplage entre plein d'éléments, que ce couplage est difficile à suivre, et que cela met un foutoir pas possible pour écrire des tests unitaires.
              Tout ça, ce sont les raisons pour lesquelles on critique les variables globales. Le rapport avec les singletons ? C'est que cela a exactement les mêmes propriétées que celles que l'on critique chez les variables globales. Car ce n'est que ça : les singletons sont des variables globales qui se sont habillées pour que l'on puisse les sortir en sociétés: "z'avez vu, je sais faire des singletons, c'est cool, ça fait plein de choses".
              Ouais. Mais non.

              Pour le NVI. Oui c'est vrai, c'est propre au C++. Comme le RAII est propre au C++. J'espère juste que tu ne te prives pas du RAII en C++, car c'est le seul moyen que l'on ait pour écrire du code robuste, facile à maintenir et qui ne fuit pas. Aller, il existe des équivalents dans les autres langages -> le "dispose pattern", voire using en C# et les try-using-clauses depuis Java 7 (je crois).
              Pour le NVI, c'est pareil, c'est un moyen C++ de disposer de contrats (au sens de programmation par contrat) sur des interfaces. Eiffel fait ça nativement. Java avec une phase de préprocessing.

              Pointeurs vs références. C'est dans la même thématique de comment éviter les erreurs de programmation. Il y a plusieurs moyens à notre disposition. Du plus tardif (et moins efficace), jusqu'au plus en amont (et plus efficace) -> tests d'intégration et de validation, tests unitaires, Prog par Contrat (aka PpC), et tout ce qui peut être vérifié à la compilation ou par construction du programme. Dans cette dernière case de "vérifié par le compilateur", on a constructeurs vs fonctions init(), static_assert, sqrt(abs(whatever)), les références à la place des pointeurs, ...

              Bref, ptr vs ref, ce n'est pas une question de gout. C'est une question de maintenance. Et même le monde du jeu (longtemps réfractaire aux avancées du C++) est au courant -> http://www.altdevblogaday.com/2011/12/24/static-code-analysis/

              • 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 juillet 2013 à 1:36:38

                @lmghs: Pour le singleton MT en C++03, il me semble qu'il existe des techniques moins coûteuses qu'un lock complet en utilisant du CAS, ceci dit j'ai déjà vu la technique cité, mais pas encore croisé de singleton qui le propose directement (d'un autre côté je ne recherche pas spécialement les singleton), et c'est hardware-dépendant aussi (à priori on a pas nécessairement accès à un CAS atomique).

                @StarShadow: Un éditeur qui va ajouter des valeurs à une enum !?

                • Partager sur Facebook
                • Partager sur Twitter
                FaQ : Fr | En 1 2 | C++11 | Template || Blog : Deloget | C++|Boost--Dev | C++Next | GotW || Installer Boost
                  17 juillet 2013 à 1:57:36

                  C'est ça, tout le soucis, c'est d'avoir accès à du CAS & cie. Et cette chose n'est pas standard, dans le C++98/03. A ce compte là, on a des compilos qui font des trucs non standards comme VC++ v8 (?) qui détourne volatile pour conférer des propriétés d'atomicité d'accès.

                  (Tiens, j'avais zappé que j'avais ce lien dans ma besace -> http://goparallel.sourceforge.net/creating-a-thread-safe-c-singleton-instance-with-tbb/ ; Et j'aime bien ce lien, pour l'aspect design -> http://programmer.97things.oreilly.com/wiki/index.php/Resist_the_Temptation_of_the_Singleton_Pattern ; je vous épargne les 11 autres liens que j'ai sur le sujet)

                  • 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 juillet 2013 à 7:55:15

                    Lol tant qu'on y es je vais faire une éditeur de monstres qui va créer de nouvelles sous classes. *-*

                    Non je plaisante, je pense que pas que ce soit possible de faire ça en dehors de code::block, pour ça qu'on est obligé de proposer une structure commune au  gamedesigner pour les monstres (Nom, description, race, listes des type de monstre possible, etc...) et pour les quêtes.

                    Les enums je les utilise juste en programmation avec l'héritage pour récupérer le type de l'entité sans devoir faire un typeinfo.

                    Si vous n'utiliser pas de singleton vous risquez d'en chier pour les interactions entre les classes, vous aller devoir passer une instance de vos objets à chaque classe qui l'utilise hors que moi je n'ai juste besoin que de passer un mutex (qui est une variable globale avec la SFML.) que je passe à mes singleton et que je lock et unlock dans les fonctions qui posent problème. (Principalement celles qui modifie les données d'une structure, ou bien qui les reçoivent et les envoient sur le réseau.)

                    J'ai lu que les mutex étaient suffisant dans la plupart des cas, mais pour un système qui mets à jour les données et qui les affichent dans 2 thread différents là je peux vous dire, mieux vaut n'utiliser qu'un seul thread sinon ça risque d'être trop lent, et de ne pas toujours s'exécuter dans le bonne ordre (ça, ça dépend plutôt de l’ordonnanceur.)

                    Du coup par moment j'avais un écran qui clignotait.

                    Du coup je suis revenu à un système mono-threadé et ça passe sans problème.

                    Et dans mon moteur graphique je n'ai qu'à faire ça quand je veux envoyer un message au serveur par exemple :

                    Network::sendMessage("Mon message.");

                    Pas besoin donc de passer un objet de type Network à mon RenderComponent (qui se charge aussi de gérer les évènements.), mon client est instancier dans le main grâce au singleton avec Network::getClient ou je lui passe l'adresse ip et le port et plus besoin de rien faire après.

                    Mais il faut savoir que il ne faut pas utiliser que des systèmes en singleton car ça c'est mal.

                    Par exemple le système de chargements de ressources, moi, j'ai simplement fais une variable d'instance dans une classe World (et non plus un singleton.) qui charge les ressources et les efface à chaque chargement de map, car, je n'ai pas besoin de le faire ailleurs.

                    Donc bon ça dépend surtout ça de ou vous devez utiliser vos systèmes, mais si il faut les utiliser dans beaucoup de classe, un singleton est la meilleure solution.

                    Mais pour les singletons multi-threadé il faudra à mon avis attendre un peu encore car je ne connais pas de standard qui marche vraiment, j'essaye de bidouiller et de trouver des solutions comme ça, j'en ai trouvé une pour mon moteur réseau qui utilise un thread pour recevoir les données mais pas pour celui qui mets à jour les données à afficher.

                     PS : Le RAII c'est un système qui te permet de libérer les ressources correctement même si le programme génère un exception pour éviter d'avoir des fuites de mémoire, c'est l'équivalent de la clause finally en java.

                     Exemple de singleton :

                    //Le main.
                    
                    int main (int argc, char* argv[]) {
                    
                          Mutex globalMutex;
                    
                          Network::startCli(4567, 4569, Configuration::serverAddress, &globalMutex);
                    
                    }
                    
                    //Network = mon singleton.
                    
                    bool Network::startCli (int portTCP, int portUDP, IpAddress address, Mutex *globalMutex) {
                        Network::globalMutex = globalMutex;
                        return cli.startCli(portTCP, portUDP, address, globalMutex);
                    }
                    
                    void Network::sendTCPMessage (string message) {
                       Packet packet;
                       packet<<message;
                       cli.sendTCPMessage(packet);   
                    }
                    void Network::sendUDPMessage(string message) {
                       Packet packet;
                       packet<<message;
                       cli.sendUDPMessage(packet);
                    }
                    
                    //Et enfin le client qui se charge de recevoir et d'envoyer les message.
                    
                    SrkClient::SrkClient () : thread (&SrkClient::run, this) {
                        running = false;
                        selectedCanal = 0;
                        remotePortUDP = 4568;
                        this->globalMutex = globalMutex;
                    
                    }
                    
                    void SrkClient::sendTCPMessage(Packet &packet) {
                        globalMutex->lock();
                        if(clientTCP.send(packet) != Socket::Done)
                            cout<<"Erreur!"<<endl;
                        globalMutex->unlock();
                    }
                    void SrkClient::sendUDPMessage(Packet &packet) {
                        globalMutex->lock();
                        if(clientUDP.send(packet, Configuration::serverAddress, remotePortUDP) != Socket::Done)
                            cout<<"Erreur!"<<endl;
                        globalMutex->unlock();
                    }
                    
                    void SrkClient::run() {
                        Packet packet;
                        running = true;  
                        short unsigned int port;
                        IpAddress address;
                        selector.add(clientTCP);
                        selector.add(clientUDP);
                    
                        while (running) {
                            if (selector.wait(seconds(10))) {
                                globalMutex->lock();
                                if (selector.isReady(clientTCP)) {
                                    packet.clear();
                                    if(clientTCP.receive(packet) == Socket::Done) {
                    
                                        string message;
                                        packet>>message;
                                        Network::addResponse(message);
                    
                                    }
                                }
                                if (selector.isReady(clientUDP)) {
                                    packet.clear();
                                    if (clientUDP.receive(packet, address, port) == Socket::Done) {
                    
                                        if (address == Configuration::serverAddress) {
                                            string message;
                                            packet>>message;
                                            Network::addResponse(message);
                                            remotePortUDP = (remotePortUDP != port) ? port : remotePortUDP;
                                        } else {
                                            cout<<"This message don't provide from the server."<<endl;
                                        }
                    
                                    }
                                }
                                globalMutex->unlock();
                            }
                        }
                    }

                    Seul inconvénient, il faut passer le mutex dans chaque classe, mais je pense que il y a moyen d'éviter ça en déclarant le mutex en variable globale. (Faudrait que je récupère mon nouveau PC pour tester un peu ça.)
                    Bref ça c'est ma solution et elle marche. ;)

                    PS : j'ai pas mis tout le code hein cela fera l'objet d'un de mes tutoriels mais, c'est juste pour vous montrer le principe.

                    PS 2 : ce bout de code ici utilise juste le c++0x (pas le c++11 que je n'ai réussi à faire marcher que en 64 bits.) et comme librairie, la SFML.

                    -
                    Edité par StarShadow 17 juillet 2013 à 8:34:26

                    • Partager sur Facebook
                    • Partager sur Twitter
                      17 juillet 2013 à 8:33:50

                      Les mutex c'est un autre problème : c'est la solution du pauvre pour faire du MT. Beaucoup de cours ont tendance à s'arrêter à ça sans insister sur les patterns qui marchent vraiment -- en tout cas, cela avait été mon cas il y a bien longtemps, et je ne suis pas sûr que cela ait changé.

                      Typiquement, les patterns à bases de queues de messages qui servent à échanger des copies d'informations entre tâches (pas au sens thread, mais au sens objet actif/Active Object).

                      Les queues de messages, ça scale, et c'est bien plus rapide que des mutex qui poussent à locker notre appli dans tous les sens et faire perdre tout intérêt à passer au MT.

                      Je renvoie à la série d'articles d'Herb Sutter sur le multithreading.

                      • 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 juillet 2013 à 8:40:26

                        Les mutex c'est un autre problème : c'est la solution du pauvre pour faire du MT. Beaucoup de cours ont tendance à s'arrêter à ça sans insister sur les patterns qui marchent vraiment -- en tout cas, cela avait été mon cas il y a bien longtemps, et je ne suis pas sûr que cela ait changé.

                        Ouais comme les cours de la SFML qui s'arrêtent aux mutex, mais je ne dis pas que y'a pas moyen de faire mieux au niveau rapidité, le créateur de la SFML le dis même mais selon lui les mutex suffisent dans la plupart des cas et sont plus simple d'utilisation.

                        Donc j'ai sauté sur l'occasion. ^^

                        PS : maintenant il faut savoir placer aussi les lock et unlock aux bons endroits car faire trop de lock et unlock c'est pas bon non plus.

                        -
                        Edité par StarShadow 17 juillet 2013 à 8:51:19

                        • Partager sur Facebook
                        • Partager sur Twitter
                          17 juillet 2013 à 8:55:56

                          StarShadow a écrit:

                          Si vous n'utiliser pas de singleton vous risquez d'en chier pour les interactions entre les classes, vous aller devoir passer une instance de vos objets à chaque classe qui l'utilise hors que moi je n'ai juste besoin que de passer un mutex (qui est une variable globale avec la SFML.) que je passe à mes singleton et que je lock et unlock dans les fonctions qui posent problème. (Principalement celles qui modifie les données d'une structure, ou bien qui les reçoivent et les envoient sur le réseau.)

                          Il y a finalement peu de cas où le Singleton est absolument nécessaire. Et généralement tu devrais avoir peu de classes l'utilisant. Par ailleurs, pourquoi passer le mutex aux objets ? Autant le(s) mettre dans le singleton comme ça tu peux gérer les synchros nécessaires en interne.

                          StarShadow a écrit:

                          J'ai lu que les mutex étaient suffisant dans la plupart des cas, mais pour un système qui mets à jour les données et qui les affichent dans 2 thread différents là je peux vous dire, mieux vaut n'utiliser qu'un seul thread sinon ça risque d'être trop lent, et de ne pas toujours s'exécuter dans le bonne ordre (ça, ça dépend plutôt de l’ordonnanceur.)

                          Va jeter un oeil au fonctionnement des modèles mémoires sur les processeur et tu verras que l'ordonnanceur globalement c'est pas le plus gênant quand tu fais du parallélisme :lol: . Un simple programme comme celui-ci : 

                          [x] <- 1   | [y] <- 1
                          EAX <- [y] | EBX <- [x]

                          peut vraiment donner des trucs pas clairs quand on sait pas comment ça marche.

                          StarShadow a écrit:

                          Du coup par moment j'avais un écran qui clignotait.

                          Du coup je suis revenu à un système mono-threadé et ça passe sans problème.

                          Dommage de se passer du parallélisme pour un problème de synchro quand même.

                          StarShadow a écrit:

                          Et dans mon moteur graphique je n'ai qu'à faire ça quand je veux envoyer un message au serveur par exemple :

                          Network::sendMessage("Mon message.");

                          Par curiosité, pourquoi un moteur graphique peut avoir besoin d'envoyer un message sur le réseau ? Ce n'est trop son rôle normalement.

                          StarShadow a écrit:

                          PS : Le RAII c'est un système qui te permet de libérer les ressources correctement même si le programme génère un exception pour éviter d'avoir des fuites de mémoire, c'est l'équivalent de la clause finally en java.

                          Le RAII ce n'est pas que ça. C'est surtout un bon moyen de donner une portée bien définie à chaque ressource même si on l'alloue dynamiquement. La libération même en cas d'exception est presque un agréable imprévu :lol: . Par ailleurs, son intérêt ne se limite pas aux ressources dynamiques, la création d'un point d'accès à une ressource physique type imprimante, fichier, réseau, etc ... est, par RAII, automatiquement relâchée à la destruction.
                          • Partager sur Facebook
                          • Partager sur Twitter

                          Posez vos questions ou discutez informatique, sur le Discord NaN | Tuto : Preuve de programmes C

                            17 juillet 2013 à 9:19:07

                            Il y a finalement peu de cas où le Singleton est absolument nécessaire. Et généralement tu devrais avoir peu de classes l'utilisant. Par ailleurs, pourquoi passer le mutex aux objets ? Autant le(s) mettre dans le singleton comme ça tu peux gérer les synchros nécessaires en interne.

                            Oui il n'y a pas beaucoup de cas ou un singleton est vraiment nécessaire, je passe le mutex aux objets car pas vraiment le choix, ici c'est mon objet SrkClient qui contient le thread donc je suis obligé de bloquer et de débloquer le mutex quand je reçois des données.

                            Mais bon je vais utiliser une variable global par la suite, ainsi, je devrai plus passez le mutex. :)

                            Va jeter un oeil au fonctionnement des modèles mémoires sur les processeur et tu verras que l'ordonnanceur globalement c'est pas le plus gênant quand tu fais du parallélisme  . Un simple programme comme celui-ci : 
                            1
                            2
                            	
                            [x] <- 1   | [y] <- 1
                            EAX <- [y] | EBX <- [x]
                            
                            peut vraiment donner des trucs pas clairs quand on sait pas comment ça marche.
                            Dommage de se passer du parallélisme pour un problème de synchro quand même.

                            Ok, mais il faut que je refasse des tests après alors, j'avais surement un problème avec la synchronisation que je gérais mal, de plus et maintenant que lmghs le dis, je ne sais pas si les mutex sont assez rapide pour faire ce genre de chose, mais j'ai optimisé mon algorithme depuis (et c'est une structure et un algorithme spécial vu que je suis en 3D iso donc ma grille est un ensemble de losanges qui sont en quinconque), je dois retester, avant j'arrivais à avoir un résultat qui marchait quasiment donc (juste que ça clignotait à certains moment) donc je pense que ça doit être faisable de faire ça en parallèle avec juste des mutex.

                            Par curiosité, pourquoi un moteur graphique peut avoir besoin d'envoyer un message sur le réseau ? Ce n'est trop son rôle normalement.

                            Non c'est le moteur réseau qui envoie les messages sur le réseau mais, dans mon render component, quand je traite un évènement, par exemple, quand je clic avec la souris, il faut que j'envoie un message au serveur et c'est mon serveur qui se charge de calculer le trajet. (Sans passer par les cases ou il y a des objets pour ne pas passez à travers les objets.) et ainsi la caméra de mon rendercomponent côté client suit le trajet de la souris.

                             Donc je suis obligé d'avoir accès à mon singleton dans mon rendercomponent pour envoyé le message au serveur via le moteur réseau.





                            -
                            Edité par StarShadow 17 juillet 2013 à 9:22:30

                            • Partager sur Facebook
                            • Partager sur Twitter
                              17 juillet 2013 à 15:19:56

                              C'est la couche graphique qui manipule les entrées utilisateurs ?

                              C'est le serveur qui calcul les trajets ?

                              Huh ?

                              • Partager sur Facebook
                              • Partager sur Twitter
                                17 juillet 2013 à 15:35:33

                                Oui. :)

                                Bon j'aurai pu faire un moteur spécialement pour la couche qui gère les entrée utilisateurs et une autre pour la couche graphique mais  ici je gère tout dans la même classe,c'est plus simple.

                                -
                                Edité par StarShadow 17 juillet 2013 à 15:37:17

                                • Partager sur Facebook
                                • Partager sur Twitter
                                  17 juillet 2013 à 17:23:24

                                  StarShadow, juste une question rien que pour toi :

                                  A quoi sert l'héritage public pour toi ? Dans quels cas l'utilises tu ? Une classe Carre héritant d'une classe Rectangle, est-ce logique pour toi ?

                                  -
                                  Edité par Cytosol 17 juillet 2013 à 17:31:32

                                  • Partager sur Facebook
                                  • Partager sur Twitter
                                    17 juillet 2013 à 18:06:33

                                    Cytosol a écrit:

                                    StarShadow, juste une question rien que pour toi :

                                    A quoi sert l'héritage public pour toi ? Dans quels cas l'utilises tu ? Une classe Carre héritant d'une classe Rectangle, est-ce logique pour toi ?

                                    -
                                    Edité par Cytosol il y a 34 minutes


                                    :D
                                    • Partager sur Facebook
                                    • Partager sur Twitter
                                      19 juillet 2013 à 13:15:05

                                      L'héritage publique ne sert que si tu as plusieurs type d'objets qui ont des variables et fonctions membres communes, par exemple si la carré et le rectangle ont une propriété commune qui est un id, dans ce cas là tu peux utiliser l'héritage publique, sinon, ça ne sert à rien.

                                      Bon je crois que je vais me lancer au c++11 dès que j'aurai récupérer mon PC.

                                      Vous me conseillerai quoi à la place des mutex sinon comme autre solution pour optimiser et rendre mes systèmes threadsafe ? (Network n'est pas vraiment un singleton car il ne m'empêche pas de pouvoir créer plusieurs objets clients, c'est juste une classe avec que des variables et fonctions membres statiques qui me permettent de communiquer avec n'importe quel autre composant ou système quand il a besoin d’interagir avec mon moteur réseau.)

                                      C'est un peu comme si je ferai une classe World qui contiendrait tout les systèmes et composants à part que ici je fais plusieurs classes de ce type et pas qu'une seule classe qui contient tout les sytèmes et composants. (Je trouve ça mieux, personnellement.)

                                       PS : bien que je pourrai peut être essayer d'améliorer ça pour qu'on ne puisse créer qu'un seul objet client, car, je ne vois pas l'intérêt d'en avoir plusieurs par programme.

                                      Mais je n'ai pas encore trouvé de solution pour faire ça avec 2 classes, si je met le constructeur de mon client en privé je pourrait plus l'instancier dans ma classe Network, l'amitié est peut être est une solution dans ce cas là.

                                      -
                                      Edité par StarShadow 19 juillet 2013 à 13:39:45

                                      • Partager sur Facebook
                                      • Partager sur Twitter
                                        19 juillet 2013 à 13:27:47

                                        StarShadow a écrit:

                                        Vous me conseillerai quoi à la place des mutex sinon comme autre solution pour optimiser et rendre mes systèmes threadsafe ? (Network n'est pas vraiment un singleton car il ne m'empêche pas de pouvoir créer plusieurs objets clients, c'est juste une classe avec que des variables et fonctions membres statiques qui me permettent de communiquer avec n'importe quel autre composant quand il a besoin d’interagir avec mon moteur réseau.)

                                        Les mutex sont le parfait inverse de l'optimisation, c'est l'opération qu'on ne veut jamais avoir à faire. Des algos concurrents, lock-free/wait-free, avec mutex mais le moins possible, il en existe toute une tripotée. Comme toujours : on le gère au cas par cas, il y a pas de recettes magiques. Mais typiquement pour la réception de messages sur le réseau, une liste de messages lock-free ça passe bien si les messages sont au bout de pointeurs.

                                        Après il restera toujours le problème de comment faire agir des threads travailleurs de concert mais là ça dépend complètement de la structure de ton programme. Le but général c'est d'avoir le moins d'opérations concurrentes possibles ce qui implique d'avoir un design pensé pour que les systèmes n'aient jamais à se taper dessus.

                                        • Partager sur Facebook
                                        • Partager sur Twitter

                                        Posez vos questions ou discutez informatique, sur le Discord NaN | Tuto : Preuve de programmes C

                                          19 juillet 2013 à 13:48:05

                                          Il n'y a pas un truc à utiliser à la place des mutex, mais des principes de programmation concurrente à connaitre. Après on peut choisir la bonne archi pour chaque système -> queues, atomic, mutex, etc

                                          Comme je le disais, plein de lecture par là: http://herbsutter.com/category/effective-concurrency/

                                          Quant à l'héritage public, vu que syntaxiquement il permet la substituabilité, alors sémantiquement il doit obligatoirement correspondre à cela. Le seul cas où il est acceptable de dériver publiquement un carré depuis un rectangle, c'est s'ils sont immuables et totalement dépourvus de comportement.

                                          Voici 10 pages de lecture sur le sujet:

                                          - http://www.developpez.net/forums/d607686/general-developpement/langages-general/heritage-classes-carre-rectangle/

                                          - http://www.developpez.net/forums/d640636/c-cpp/cpp/poo-suivez-principe-substitution-liskov/

                                          • 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.
                                            19 juillet 2013 à 14:02:39

                                            Ok, on m'a souvent conseillé un système monothread plutôt que un système multi-thread, moi, je n'utilise que 2 threads côté client et le fait de lock et unlock la socket a chaque réception et envoi de message du client au serveur et d'ajouter tout les messages reçus dans une liste pour qu'ils soient ensuite traité par un autre thread ne me pose pas vraiment de problèmes au niveau performance et le code est moins crade que de faire tout avec la boucle de rendu.

                                            Maintenant d'un autre côté on me dit que se serait dommage de se priver du multi-thread donc je pense que si j'arrive à avoir un design bien pensé, qui ne mets pas trop en concurrence mes différentes variable, il y a moyen de faire ça avec plusieurs threads.

                                            Et puis oui je pense que tu as raison, ça dépend fortement du design, après tout, coder est un art. :)

                                            • Partager sur Facebook
                                            • Partager sur Twitter
                                              19 juillet 2013 à 14:06:24

                                              StarShadow a écrit:

                                              Ok, on m'a souvent conseillé un système monothread plutôt que un système multi-thread [...]

                                              Maintenant d'un autre côté on me dit que se serait dommage de se priver du multi-thread donc je pense que si j'arrive à avoir un design bien pensé, qui ne mets pas trop en concurrence mes différentes variable, il y a moyen de faire ça avec plusieurs threads.

                                              Aujourd'hui l'augmentation des performances des machines, c'est l'augmentation du nombre de cœurs et l'ajout d'unités de calcul vectorielles, les applications séquentielles ne vont plus beaucoup (voire plus du tout) gagner en performances avec les nouvelles générations de processeurs, l'ère est au multi-cœurs ^^ .

                                              • Partager sur Facebook
                                              • Partager sur Twitter

                                              Posez vos questions ou discutez informatique, sur le Discord NaN | Tuto : Preuve de programmes C

                                                19 juillet 2013 à 17:33:17

                                                Ok super alors je me lance là dedans. :)
                                                • Partager sur Facebook
                                                • Partager sur Twitter
                                                Anonyme
                                                  28 juillet 2013 à 1:06:28

                                                  StarShadow a écrit:

                                                  L'héritage publique ne sert que si tu as plusieurs type d'objets qui ont des variables et fonctions membres communes, par exemple si la carré et le rectangle ont une propriété commune qui est un id, dans ce cas là tu peux utiliser l'héritage publique, sinon, ça ne sert à rien.

                                                  Bravo, tu es tombé dans le piège posé par Cytosol.
                                                  Tu réfléchis en terme de données, pas en terme de services, donc tu n'as pas compris l'essentiel de l'Objet. Pire encore avec cette histoire de carré et de rectangle où tu brises allégrement le LSP.


                                                  Bonne lecture : http://cpp.developpez.com/faq/cpp/?page=heritage#heritage_lsp

                                                  • Partager sur Facebook
                                                  • Partager sur Twitter
                                                    28 juillet 2013 à 10:19:07

                                                    Je crois qu'il faisait référence à une classe de genre "Identified" qui assure le fait que chaque instance possède un identifiant unique. Classe dont hériteraient Carré et Rectangle.

                                                    Dans l'absolu, je ne suis pas contre le raisonnement, mais en pratique ça pose plus de problèmes que ça n'en résout lorsque le système d'identifiant n'est pas autonome (e.g un système de chargement qui va créer des instances en imposant les identifiants pour garantir l'état global de l'application après la lecture d'un fichier de sauvegarde).

                                                    • Partager sur Facebook
                                                    • Partager sur Twitter
                                                      29 juillet 2013 à 20:15:34

                                                      Boarf, moi c'est comme ça que je fais, et, ça ne m'a jamais posé de problème, oui, faut savoir mettre ça en pratique..

                                                      Vous êtes vicieux hein vous avec vos pièges.

                                                      • Partager sur Facebook
                                                      • Partager sur Twitter

                                                      Comment structurer un jeu en c++

                                                      × 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