[SDL] Hésitation de codage

Hésitation de codage [SDL] - C++ - Programmation

Marsh Posté le 08-07-2010 à 12:25:43    

Bonjour, je suis en équipe sur un projet de jeu point & click à la Machinarium, je dois le coder en C++ avec la SDL.
 
Bon je vais essayer d'être le plus clair possible ; ma question est la suivante :
 
"Comment programmer le plus aisément ?"
 
J'ai plusieurs solutions mais je ne sais pas comment m'y prendre :
 
1. Soit je crée une classe Game englobant toutes les classes d'un jeu point & click (Sprites, Personnage, Room, Dialogue, Item, j'en passe...) afin d'insérer un pointeur SDL_Surface attribut contenant l'écran où je blitterai toutes mes images au fur et à mesure...
 
2. Soit je crée les classes précédentes à part et indépendantes entre elles, mais le problème c'est qu'il me faut le pointeur SDL_Surface et donc à n'importe quelle fonction graphique je serai toujours obligé de l'envoyer en paramètre, ce qui peut être lourd et ennuyeux. En fait je me demande si ya pas mieux que de devoir créer 50 fonction sous la forme :
 

Code :
  1. Perso1.afficher(ecran);
  2. chaise.afficher(ecran);
  3. gui.afficher(ecran);
  4. textes.afficher(ecran);


 
Je verrais plus proprement un truc du genre :
 

Code :
  1. game.afficherPersos();
  2. game.afficherItems();
  3. game.afficherTextes();
  4. game.afficherObjets();


 
Ce qui me fait bien sûr penser à ça c'est pour maximiser à fond l'orientation objet du main.cpp et faire en sorte que la SDL soit gérer toute seule dans les méthodes.
 
Je sais pas si je suis clair dans mon explication, si vous voulez des précisions n'hésitez pas :)
 
Merci, à bientôt.


Message édité par Dolphin Dice le 08-07-2010 à 12:26:24

---------------
Apprenez à créer des jeux d'aventure avec AGS
Reply

Marsh Posté le 08-07-2010 à 12:25:43   

Reply

Marsh Posté le 09-07-2010 à 00:05:43    

Au lieu d'avoir un objet pour chaque concept du jeu (Sprite, Perso, etc...) et de le forcer à tout faire lui même (donc à savoir s'afficher, donc à avoir du code de blit de la SDL dans tout tes objets). Il y a une autre méthode, qui te fera avoir bien moins de duplication de code, en gros :

 
Code :
  1. // C'est pas comme le Sprite dont tu parle, il est utilisé par tout types d'affichage
  2. class Sprite {
  3.    SDL_Surface* texture;
  4.    int x, y;
  5.    int layer;
  6.    public:
  7.    void draw(SDL_Surface* surface) {
  8.        // peint la surface texture à la position (x, y)
  9.        SDL_BlitSurface(texture, blahblah..., surface, blahblah...);
  10.    }
  11.    // Ca c'est pour trier tout les sprites dans le bon ordre et dans un ordre
  12.    // efficace pour l'affichage...
  13.    bool operator<(const Sprite& that) {
  14.        // On met les plus petits layers (les plus au fond) en premier
  15.        if (layer >= that.layer) return false;
  16.        // On essaie ensuite de trier les textures dans l'ordre pour mieux utiliser le cache
  17.        if (texture >= that.texture) return false;
  18.        // On se fiche de x et y, donc on compare simplement l'identite
  19.        // pour avoir un ordre complet
  20.        if (this >= &that) return false;
  21.    }
  22. };
  23.  
  24. class Game {
  25.    std::list<Sprite> sprites;
  26.    Personage personage;
  27.    SDL_Surface* default_texture;
  28.    public:
  29.    std::list<Sprite>::iterator new_sprite(const char* texture_file) {
  30.        // avec SDL_Image
  31.        SDL_Surface* texture(IMG_Load(texture_file));
  32.        // Si on trouve pas le fichier, on a une texture par defaut avec une grosse croix rouge
  33.        // C'est mieux qu'une segfault...
  34.        if (!texture) texture = default_texture;
  35.        return new_sprite(texture);
  36.    }
  37.    std::list<Sprite>::iterator new_sprite(SDL_Surface* texture) {
  38.        sprites.push_back(Sprite(texture, 0, 0, 0));
  39.    }
  40.    void delete_sprite(std::list<Sprite>::iterator sprite) {
  41.        sprites.erase(sprite);
  42.    }
  43.    
  44.    void prepare_draw() {
  45.        std::sort(sprites.begin(), sprites.end());
  46.    }
  47.    void draw(SDL_Surface* surface) {
  48.        // pour chaque element de la liste des sprites :
  49.            sprite.draw(surface);      
  50.    }
  51.    
  52. };
  53.  
  54. class Personage {
  55.    Game& game_;
  56.    std::list<Sprite>::iterator head_;
  57.    std::list<Sprite>::iterator body_;
  58.    public:
  59.    Personage(Game& game)
  60.        : game_(game)
  61.        , head_(game.new_sprite("hero_head.png" ))
  62.        , body_(game.new_sprite("hero_body.png" )) {
  63.    }
  64.    ~Personage() {
  65.        game_.delete_sprite(head);
  66.        game_.delete_sprite(body);
  67.    }
  68.    void move_left() {
  69.        // Si tu veux faire du joli objet, tu fais des accesseurs et tout ca...
  70.        head_->x += 2;
  71.        body_->x += 2;
  72.    }
  73.    void move_right() {
  74.        head_->x -= 2;
  75.        body_->x -= 2;
  76.    }
  77. };
  78.  
  79. class Text {
  80.      std::list<Sprite>::iterator text_texture_;
  81.      public:
  82.  
  83.      // constructeur destructeurs, etc...
  84.  
  85.      void set_text(const std::string& text) {
  86.            // en vrai y'a l'ancienne texture a liberer...
  87.            text_texture_->texture = TTF_RenderText_Solid( ta_font, text, ta_couleur);
  88.      }
  89.      void set_text_position(int x, int y) {
  90.            // Et la si t'es courageux, tu ajoute des proprietes d'alignement
  91.            // et tu fais tes calculs en fct de la taille de la texture
  92.            text_texture_->x = x;
  93.            text_texture_->y = y;
  94.      }
  95. };
 

Bref, l'idée c'est que tes différents objets gèrent tous un ou plusieurs objets Sprite (si ils ont des trucs à afficher), mais ne font que changer ses propriétés, quand vient le moment de tout afficher, c'est game qui le fait d'un coup, de manière efficace. (Par exemple il peut au passage décider de ne pas blitter les éléments hors-écran...)
T'auras aussi des objets de plus haut niveau : la classe Dialogue par exemple, qui elle contiendra des objets Text, les positionnera, les ajoutera et les supprimera au fil du dialogue, sans jamais avoir besoin de savoir quel est le code pour réellement afficher ce text.


Message édité par 0x90 le 09-07-2010 à 00:17:56

---------------
Me: Django Localization, Yogo Puzzle, Chrome Grapher, C++ Signals, Brainf*ck.
Reply

Marsh Posté le 09-07-2010 à 07:22:09    

Alors merci beaucoup pour le message, ça va effectivement être plus aisé de faire comme ça, c'est vrai que le découpage du projets, des classes et surtout de leur méthode donc est pas facile, donc merci.
 
Par contre il y a des notions que je ne saisie pas bien, les layers notamment, je sais pas trop ce que signifie le mot-clé iterator, mais j'ai tout le temps d'apprendre ces choses.
 
Le plus important c'est que je vois à peu près la manière dont je devrais m'y prendre.
 
Je viens à une nouvelle question ^^ :
 
Est-ce qu'il serait plus pratique de créer des tableaux dynamiques de chaque classe en référence aux éléments que je crée :
 

Code :
  1. int main()
  2. {
  3. Personnage perso1;
  4. Personnage perso2;
  5. Personnage perso3;
  6. // La classe Personnage possède un attribut m_Count qui vaut ici 3.
  7. Personnage &Personnages[]
  8. }


 
Si ça te dit quelque chose, je voudrais faire comme dans le logiciel AGS, c'est-à-dire contrôler mes objets avec différents tableaux où entrer l'ID de chaque élément (perso1.ID = 0, perso2.ID = 1, etc...), ce qui permettrait de les contrôler nettement plus facilement par le biais d'une boucle et de m_Count (automatiquement recalculé), ainsi si je veux faire une commande pour tous mes objets, une boucle de la commande et c'est réglé.
Sauf que je sais pas trop où mettre le tableau de références ?
Et pour attribuer tous les personnages créés je doit faire appel à une fonction ? De quelle classe ? Game ou Personnage ?
 
Moi ma philosophie de programmation c'est d'essayer d'avoir le plus possible de manipulation d'objet (donc comme tu l'as souligné, je fais beaucoup d'accesseurs) dans le main.c jusqu'à ne pas avoir à toucher la SDL si on peut, ce qui impliquerait la gestion des évènement ailleurs que dans le main.c
 
Je suis convaincu qu'un plan de programmation impérialement structuré permet de faire très facilement le jeu (quitte à ce que les méthodes soient très lourdes).

Message cité 1 fois
Message édité par Dolphin Dice le 09-07-2010 à 07:22:43

---------------
Apprenez à créer des jeux d'aventure avec AGS
Reply

Marsh Posté le 09-07-2010 à 10:58:37    

Dolphin Dice a écrit :

Alors merci beaucoup pour le message, ça va effectivement être plus aisé de faire comme ça, c'est vrai que le découpage du projets, des classes et surtout de leur méthode donc est pas facile, donc merci.

 

Par contre il y a des notions que je ne saisie pas bien, les layers notamment, je sais pas trop ce que signifie le mot-clé iterator, mais j'ai tout le temps d'apprendre ces choses.


Ce que j'ai appelé layer, j'aurais pu aussi l'appeller profondeur (même si tu fais un jeu en 2D), ou z-index (si t'as fait du web ça doit te dire quelque chose ça), c'est juste une valeur pour savoir quand t'as 2 sprites lequel doit être dessiné par dessus l'autre. Par exemple le background aura la valeur 0, les objets dans le décor des valeurs entre 1 et 1000, ton personage la valeur 1000 et ton interface des sprites avec une valeur encore plus grande. Comme je trie tout les sprite par numero de layer avant de les dessiner, je suis sur quand je dessine tout les sprite de peindre le background en premier et l'interface en dernier.

 

L'iterator c'est plus embétant que tu comprenne pas, il te manque un gros bout de C++, c'est pas un mot clef c'est un type, et ça sert à naviguer dans les collections d'objet comme std::list

Dolphin Dice a écrit :


Le plus important c'est que je vois à peu près la manière dont je devrais m'y prendre.

 

Je viens à une nouvelle question ^^ :

 

Est-ce qu'il serait plus pratique de créer des tableaux dynamiques de chaque classe en référence aux éléments que je crée :

 
Code :
  1. int main()
  2. {
  3. Personnage perso1;
  4. Personnage perso2;
  5. Personnage perso3;
  6. // La classe Personnage possède un attribut m_Count qui vaut ici 3.
  7. Personnage &Personnages[]
  8. }
 

Si ça te dit quelque chose, je voudrais faire comme dans le logiciel AGS, c'est-à-dire contrôler mes objets avec différents tableaux où entrer l'ID de chaque élément (perso1.ID = 0, perso2.ID = 1, etc...), ce qui permettrait de les contrôler nettement plus facilement par le biais d'une boucle et de m_Count (automatiquement recalculé), ainsi si je veux faire une commande pour tous mes objets, une boucle de la commande et c'est réglé.
Sauf que je sais pas trop où mettre le tableau de références ?
Et pour attribuer tous les personnages créés je doit faire appel à une fonction ? De quelle classe ? Game ou Personnage ?


C'est en gros ce que je fais là. std::list<Sprite> dans Game pour stocker les objets. std::list<Sprite>::iterator qui sert d'identifiant pour accéder à l'objet, puis pour le détruire quand on en a plus besoin. tu peux faire une autre liste, de personages de la même manière.

Dolphin Dice a écrit :


Moi ma philosophie de programmation c'est d'essayer d'avoir le plus possible de manipulation d'objet (donc comme tu l'as souligné, je fais beaucoup d'accesseurs) dans le main.c jusqu'à ne pas avoir à toucher la SDL si on peut, ce qui impliquerait la gestion des évènement ailleurs que dans le main.c

 

Je suis convaincu qu'un plan de programmation impérialement structuré permet de faire très facilement le jeu (quitte à ce que les méthodes soient très lourdes).


Typiquement dans ton main tu pourrais avoir quelque chose de la forme :

Code :
  1. int main(int argc, char** argv) {
  2.    SDL_Init(SDL_INIT_VIDEO);
  3.    SDL_Surface* screen = SDL_SetVideoMode(640, 480, 0, SDL_HWSURFACE | SDL_DOUBLEBUF);
  4.    Game game;
  5.    Uint32 time = SDL_GetTicks();
  6.    while (!game.finished()) {
  7.        SDL_Event event;
  8.        while(SDL_PollEvent(&event)) {
  9.            switch (event.type) {
  10.                case SDL_MOUSEMOTION:
  11.                    game.on_mouse_move(event.motion.x, event.motion.y);
  12.                    break;
  13.                case SDL_MOUSEBUTTONDOWN:
  14.                    game.on_mouse_down(event.button.button, event.button.x, event.button.y);
  15.                    break;
  16.                case SDL_MOUSEBUTTONUP:
  17.                    game.on_mouse_up(event.button.button, event.button.x, event.button.y);
  18.                    break;
  19.                case SDL_KEYDOWN:
  20.                    game.on_key_down(event.key.sym);
  21.                    break;
  22.                case SDL_KEYUP:
  23.                    game.on_key_up(event.key.sym);
  24.                    break;
  25.            }
  26.        }
  27.        
  28.        Uint32 new_time = SDL_GetTicks();
  29.        // Tres utile si t'as des animations...
  30.        game.advance_time(new_time - time);
  31.        time = new_time;
  32.        
  33.        game.prepare_draw();
  34.        game.draw(screen);
  35.        
  36.        SDL_Flip(screen);
  37.    }
  38.    SDL_Quit();
  39.    return 0;
  40. }


Donc avec quasiment (à part la gestion de la texture des Sprite et le dra) tout le code spécifique à la SDL dans ton main.

 

Ensuite dans Game, tu réagis aux clicks/clavier/etc... dans les fonctions on_* qui font avancer l'état du jeu, jusqu'au moment où tu fais quelque chose qui fait que game.finished() retourne true, et le jeu se ferme.


Message édité par 0x90 le 09-07-2010 à 10:59:05

---------------
Me: Django Localization, Yogo Puzzle, Chrome Grapher, C++ Signals, Brainf*ck.
Reply

Marsh Posté le 09-07-2010 à 11:53:58    

Un mot : merci !!  :love:  :love:  :love:  
 
Et je suis même heureux d'apprendre l'intérêt de l'iterator ! Pour les layers effectivement c'est essentiel, bon par contre j'ai jamais touché au web script :pt1cable:  
J'apprends le C++ sur le site du zéro donc étant pour des amateurs le tuto ne peut pas être le plus complet et pourtant cette notion est supère importante la preuve !!
J'irai voir sur Developper.com ou ici si y a des tutos :)
Et franchement merci pour le code, ça m'oriente beaucoup, donc ya de la SDL juste pour l'initialisation et les évènements.
C'est peut être une question idiote mais créer une fonction game.Init() pour regrouper les lignes 2 et 3 est possible ou la SDL sera quittée dès que la fonction sera terminée ?
 
 :bounce:

Message cité 1 fois
Message édité par Dolphin Dice le 09-07-2010 à 11:55:23

---------------
Apprenez à créer des jeux d'aventure avec AGS
Reply

Marsh Posté le 09-07-2010 à 12:05:36    

Dolphin Dice a écrit :


C'est peut être une question idiote mais créer une fonction game.Init() pour regrouper les lignes 2 et 3 est possible ou la SDL sera quittée dès que la fonction sera terminée ?
:


C'est possible, (la SDL ne quitte qu'au moment de SDL_Quit), mais ça n'apporte rien :
- tu va agrandir Game, qui est déjà une classe bien chargée
- ça va faire plus de code, sans gagner en maintenabilité ou flexibilité
- tu va éloigner le SDL_Init du SDL_Quit, alors que les deux vont logiquement ensemble
- tu augmente les responsabilités de Game pour rien, il n'a pas besoin de savoir créer sa propre surface pour faire son boulot, si tu y tiens tu peux avoir un objet qui s'occupe de gérer la surface (et même du coup, de changer de surface à la volée, pour entrer/sortir d'un mode fullscreen par exemple), mais là encore, pas la peine de forcer Game à tout faire.


---------------
Me: Django Localization, Yogo Puzzle, Chrome Grapher, C++ Signals, Brainf*ck.
Reply

Marsh Posté le 09-07-2010 à 17:51:23    

OK, Game était un exemple, je comptais faire une classe System voire SDL, avec les méthodes Init() et Quit() et pourquoi pas un changement de mode de résolution :3
 
Moi personnellement je trouve ça plus qu'inutile pour les mêmes raisons, mais dans un projet un équipe, je pense vraiment qu'une aisance de lecture de code s'impose pour éviter la démotivation, les commentaires ne suffisent pas toujours. En fait le fait d'englober les deux fonctions permettra d'indiquer aux programmeurs (qui apprennent actuellement la SDL) que les trois fonctions sont liées (les deux premières dans le Init et la fonction Quit() dans la méthode Quit()).
 
Encore une fois, grand merci à toi, je viendrai sûrement toquer pour plus d'info.
Bon j'ai commencer à lire ton lien sur l'iterator, Mon Dieu :heink: j'ai l'impression qu'il me manque des masses :cry:


---------------
Apprenez à créer des jeux d'aventure avec AGS
Reply

Sujets relatifs:

Leave a Replay

Make sure you enter the(*)required information where indicate.HTML code is not allowed