Partage
  • Partager sur Facebook
  • Partager sur Twitter

[méthode] Encapsulation

    21 février 2022 à 20:45:09

    Bonjour,

    J'aurais besoin de conseils et d'avis concernant la méthode de stockage de mes objets.

    J'explique : à ce jour un fichier de niveau de mon jeu ne contient que des identifiants (uint32_t), une suite de nombre correspondants à quelle musique utiliser, quelle map, avec quel tileset, quel background etc. C'est juste un descripteur.

    Grosso modo, la plupart de mes objets n'embarque pas ses ressources, l'objectif de base était de limiter les copies d'une même ressource si elle sert plusieurs fois. Par exemple ressortir la même map en été et en hiver en ne changeant que le tileset, ou un jour / nuit en changeant le background.

    D'autres objets, au contraire, sont plus autonomes, par exemple un fichier sprite embarque ses paramètres d'animation et sa planche, ce qui me permet, en théorie, d'utiliser ce sprite dans 2 programmes simplement en ouvrant le fichier et sans avoir à penser à trimballer la planche avec.

    Comme je suis un peu maniaque et que j'aime la constance, j'aimerais me fixer sur l'une ou l'autre des méthodes.

    Travailler en référence permet de ne modifier une ressource qu'en un point pour tous les objets qui l'utilisent, par exemple corriger un tileset appliquera la correction pour toutes les maps basées dessus ou remplacer une musique par une autre et que ça passe dans tous les niveaux. Du coup, c'est un peu comme si un bmp basée sur une palette arrivait sans sa palette mais une valeur d'index dans une liste de palettes existant ailleurs.

    D'un autre côté, l'encapsulation va favoriser la mobilité du fichier, à ce jour, si je file mon fichier de tilemap à quelqu'un pour qu'il travaille dessus, il va arriver sans tileset et autant dire que ça ne sert à rien. Sans parler de l'exigence d'avoir des ID cohérents et dans le même système de fichier. Si je décide d'encapsuler la musique dans un fichier de niveau, j'aurais dans mon fichier de ressources autant de copies de cette musique qu'il y a de niveaux qui utilisent cette musique... Mais je peux envoyer ce fichier unique à quelqu'un qui peut le tester tout de suite, voir la map et entendre la zic de ce niveau...

    Bref, j'ai un peu de mal à me décider tellement je trouve les avantages / inconvénients de chaque bien équilibrés, c'est un peu comme choisir une structure faites de structures ou une structure pleine de pointeurs sur d'autre structures...

    J'espère presque qu'on me réponde : "Ne fais surtout pas ça, c'est encore plus diabolique qu'un goto !!" ^^

    Qu'en pensez vous ? Vous faites / feriez comment ? Je suis aussi ouvert à une autre alternative à laquelle je n'aurais pensée. Il est surprenant de voir à quel point mon génie m'empêche parfois de voir la simplicité...

    Merci.

    Salutations.

    • Partager sur Facebook
    • Partager sur Twitter

    Bonhomme !! | Jeu de plateforme : Prototype.

      21 février 2022 à 22:08:42

      Hello,

      en fait c'est pas vraiment d'encapsulation dont tu parles mais de composition et surtout du propriétaire des «références». Tu parles ensuite aussi de sérialisation/désérialisation.

      drx a écrit:

      [...]

      Comme je suis un peu maniaque et que j'aime la constance, j'aimerais me fixer sur l'une ou l'autre des méthodes.

      [...]

      Pourquoi l'une ou l'autre ?

      Si, par design, certains de tes objets ont des propriétés propres (comme pour les sprites qui a pour propriétés une planche, une animation, etc …) alors que d'autres sont des compositions d'objets (un niveau est composé par une musique, un tileset, etc …) il n'y a pas de raisons de vouloir fusionner leur structure.

      Ensuite si tu veux fournir des données à quelqu'un d'autre et bien tu sérialise ce qu'il faut. Si tu as des références tu les déréférences lors de la sauvegarde … La sérialisation n'a pas besoin de suivre la structure de l'objet sérialisé au bit près.

      • Partager sur Facebook
      • Partager sur Twitter
        22 février 2022 à 1:19:13

        Salut,

        Merci. Mais je ne parle pas de structure, mais bien de fichiers. Pour avoir un fichier unique de ressource, j'utilise depuis quelques années une formule d'encapsulage, assez simple d'ailleurs :

        typedef struct CEV_Capsule
        {/**structure containing data and associated informations **/
        
            uint32_t    id,   /**< hi byte is type, 3 lowest are unique id for this type */
                        size; /**< data size in bytes */
            void        *data;/**< raw data */
        }
        CEV_Capsule;

        Avec ça, je peux stocker tout un tas de fichiers au sein d'un même fichier dont l'en-tête me permet de retrouver mes petits quand je cherche un truc (avec les offsets). Un fichier de menu peut contenir ses paramètres puis une capsule de texte, une de police et des capsules d'images png ou gifs, puis j'encapsule le menu si j'ai besoin. Si ce menu me plait, je peux tout à fait utiliser ce fichier pour avoir exactement le même menu dans un autre jeu. Tant que je le base sur ma lib...

        Mais bon, à la rigueur un menu ou une map est objet plutôt unique dans le sens où le jeu ne gère qu'une instance à la fois.

        Prenons plus particulièrement une plateforme mobile, j'édite un asset de plateforme, c'est une image ou une animation et je stocke ça dans un fichier. Lorsque je charge ce fichier, j'ai les constantes d'une plateforme. Maintenant au sein d'un niveau, je vais avoir plusieurs plateformes avec leur propre instance, différentes positions, vitesses et un pointeur vers ses constantes, pour ne pas avoir 50 fois la même structure quand les plateformes ont la même tête :

        typedef struct S_CEV_PlateformCst
        {
            uint32_t ID;        //unique ID
            bool isPxlPerfect;  //pix perfect plateform, will generate Amask at load
            SDL_Texture* pic;   //Texture if single picture / gif
            CEV_Amask* mask;    //Alpha mask if used
            SDL_Rect display,   //picture display dimension
                     hitBox;    //plateform hitbox if no Amask
            SP_Anim* anim;      //animation if so
            CEV_GifAnim* gif;   //gif if so
        }
        CEV_PlateformCst;
        
        //pause is at end of movement, request is incremented when pause is done
        typedef struct S_CEV_PlateformInst
        {
            //parameters
            bool isElevator;    //plateform is elevator / accepts external requests
        
            uint32_t
                    ID,         //unique ID
                    timeSet,    //time to travel full cycle
                    timeSync,   //start sync as offset to 0 ms
                    timePause,  //time to pause when destination is reached
                    timeStart,  //begin of sync move on segment
                    timeStop,   //end of sync move on segment
                    timeRef,    //memo to detect modulo back to 0
                    numOfFloors,//number of positions available
                    distFull,   //full distance travel (from pt to pt)
                    posSync[CEV_PLATEFORME_MAX_POS]; //time/pos sync values
        
            SDL_Point floorPos[CEV_PLATEFORME_MAX_POS]; //floors positions
        
            //status
            bool isAtPos;       //destination is reached
        
            int posIndexNxt,    //next position index
                posIndexAct,    //memo to detect changes in request
                posIndexReq;    //requested index if elevator (extern)
        
            SDL_Point   actPos,  //actual position in world by top left rect
                        vect;    //actual velocity vector (to be added to player's)
        
            //ressources
            CEV_PlateformCst* cst;  //to its constants
            SP_Sprite* sprite;      //its sprite if is sprite
            CEV_Edge  posReached;   //reached position RE
        }
        CEV_Plateform;


        Tout ne se retrouve pas dans le fichier, seulement ce qui n'est pas recalculable, par exemple le fichier contient le fait de savoir si c'est du pixel perfect ou non, le masque est créé au chargement s'il le faut, il n'est pas dans le fichier. Le bmp / png / gif ou l'animation y est.

        Maintenant dans mon éditeur de niveau, je créé des plateformes pour lesquelles je génère un fichier (par plateforme) pour stocker ses paramètres uniques (positions dans la map, de où à ou, temps de pause...) et un Id pour identifier l'asset de référence. Et bien sûr un ID pour cette plateforme pour la retrouver si j'ai des boutons qui l'utilisent comme ascenseur...

        Bin là, j'ai un fichier inexploitable seul parce qu'il fait référence à un fichier que je dois savoir retrouver si je veux afficher la plateforme. Je pourrais dans le fichier d'une plateforme, embarquer les constantes, mais ce sera moins memory friendly parce j'aurais autant d'instances de "constantes" que de plateformes.

        M'enfin, bon. Le résultat de tout ça c'est qu'il semble que ça dépend quoi...

        Par exemple, un soft comme Unity fonctionnerait comment ? Il créé un fichier d'asset unique puis le référence pour chaque instance basées dessus ? Genre un fichier d'asset "cube1 of 100*100*100", puis des fichiers du type "cube1" at pos X, Y, Z par instance ?

        Salutations.

        -
        Edité par drx 22 février 2022 à 1:23:25

        • Partager sur Facebook
        • Partager sur Twitter

        Bonhomme !! | Jeu de plateforme : Prototype.

          22 février 2022 à 3:23:46

          drx a écrit:

          Salut,

          Merci. Mais je ne parle pas de structure, mais bien de fichiers. Pour avoir un fichier unique de ressource

          Mais pourquoi faire, un seul fichier? J'ai l'impression que c'est une complication gratuite.

          • Partager sur Facebook
          • Partager sur Twitter
            22 février 2022 à 9:47:52

            Salut,

            Non, ce n'est pas une complication, c'est fait depuis 10 ans. C'est même plus simple qu'un Cmake, je liste les fichiers et tout est encapsulé et mis à la queue leu leu dans le même fichier avec un simple clic.

            Parce qu'un seul fichier est plus propre qu'un bordel de dossiers d'images, de sons, de textes et j'en passe. C'est plus simple pour les corrections, on remplace un fichier par un autre. C'est plus simple à automatiser parce que je n'ai pas à jongler avec les noms de fichiers quand je code un jeu. Ou pour la même raison qu'on fourni un exécutable ou un auto extractible et pas un lot de fichiers texte à compiler soi-même... Pour la majorité des gens, j'entends.

            En gros je compile mes ressources comme je compile mon programme. La question n'est pas vraiment comment faire mais comment organiser.

            Et puis mon objectif est de coder des trucs à mettre en place proprement si possible, pas de trouver une bonne raison de ne pas le faire. Sinon, je ne me casse pas la tête à faire un plateformer en C alors que j'aurais un prototype en 15 jours sous Unity.

            En fait, sur certains objets, j'ai un peu le même problème que le MP3. Tu as remarqué que parfois il affiche la pochette et qu'elle disparait quand tu le files à quelqu'un ou que tu les déplaces ? C'est parce que l'image est ailleurs que dans le fichier MP3, parfois dans le dossier, parfois dans un dossier qui liste les images de pochettes. Mais tu as aussi l'option de l'embarquer, le prix à payer étant que le fichier est gonflé de la taille de l'image que tu y mets, mais tu peux refiler ton MP3 avec sa pochette à n'importe qui sans avoir à retrouver une image quelque part... Mais comme toutes les chansons d'un même album ont la même pochette, tu te trimballes techniquement autant de fois la même image qu'il y a de chansons.

            Du coup, en évoquant cet exemple, je me dis que je peux faire les 2, il suffirait d'avoir un booléen pour dire si c'est un objet qui embarque ses ressources ou s'il se réfère à d'autres au sein du système de fichier. Reste que les load() / save() ne pourront pas être les mêmes dans l'éditeur et dans le jeu parce que l'objet référencé ne se trouvera pas au même endroit et que le mode d'accès ne sera pas le même.

            Piste à creuser...

            Merci.

            -
            Edité par drx 22 février 2022 à 9:51:14

            • Partager sur Facebook
            • Partager sur Twitter

            Bonhomme !! | Jeu de plateforme : Prototype.

              22 février 2022 à 11:50:10

              Bon, tu as développé un gros truc depuis pas mal d'années tu ne vas pas tout refaire …

              Néanmoins ce que tu décris aurait (et là je m'avance) sans douté été plus simple si tu avais utilisé une db embarquée comme sqlite. Là tu gères plein d'id (de clés) pour retrouver dans des blocs de ton fichier (des tables de la base) des données qui sont liées entre elles (bd relationnelle).

              Avec sqlite, tu peux utiliser l'amalgation pour bénéficier de toute la «flexibilité» d'une db relationnelle embarquée en ajoutant qu'un seulk et unique source C à ton projet. L'avantage est que c'est largement cross platform (contrairement aux outils de gestions de ressources spécifiques à plus ou moins chaque OS, ou à l'utilisation du linker qui reste lourde).

              • Partager sur Facebook
              • Partager sur Twitter
                22 février 2022 à 15:24:16

                Salut,

                Merci pour le conseil.

                En fait, je tiens à tout développer, en quelque sorte le voyage m'intéresse plus que la destination. Pour mon éditeur, j'avais regardé GTk pour finalement implémenter mon propre système de menus, il a son propre explorateur de dossiers, tout est fait maison.

                Mes seules "aides" sont la SDL (+ mixer + image + ttf) en grande partie cachés sous ma lib de surcouche, et la lib standard. Et je suis ne suis pas loin de me débarasser de SDL_ttf pour avoir mon propre module... Bref, je m'épargne juste d'avoir à gérer l'interfaçage avec le hard et/ou l'OS. Et aussi parfois la lib logitechLCD pour exploiter l'écran de mon clavier, encore une fois pour lui laisser gérer l'interface matérielle.

                Je ne veux pas avoir fait un jeu, je veux faire un jeu, maintenant que j'ai un moteur et un prototype, je fais un soft pour créer du contenu pour ce moteur, et se pose donc la question d'organiser les ressources et éventuellement revoir ma méthode.

                Bref, au risque de me répéter, je ne cherche pas un truc forcément facile à faire, ou un truc qui le fera à ma place. Par contre je suis très intérressé par comprendre comment fonctionne ledit sqlite pour faire le mien, alors j'irai jeter un oeil.

                Merci.

                • Partager sur Facebook
                • Partager sur Twitter

                Bonhomme !! | Jeu de plateforme : Prototype.

                [méthode] Encapsulation

                × 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