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

Projets Discussion :

OpenAWars - Advance wars like


Sujet :

Projets

  1. #141
    Responsable 2D/3D/Jeux


    Avatar de LittleWhite
    Homme Profil pro
    Ingénieur développement logiciels
    Inscrit en
    Mai 2008
    Messages
    26 859
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Localisation : France

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

    Informations forums :
    Inscription : Mai 2008
    Messages : 26 859
    Points : 218 579
    Points
    218 579
    Billets dans le blog
    120
    Par défaut
    Citation Envoyé par Emmanuel Deloget Voir le message
    IL faudrait que je reprenne la discussion depuis le début (ou depuis un post déterminé, mais je ne sais pas lequel) pour vous apporter mon grain de sel. Il y a des choses intéressantes à dire, je pense.
    Il y a toujours des choses intéressantes à dire
    Je vous conseille depuis la page 5 (ou 6) pour la section UML de l'interface entre l'application et les bibliothèques.
    Sinon depuis la page 1 pour toute la progression du projet.

    (Note: En ce moment, certes je ne travaille pas beaucoup sur le projet mais je refléchis déjà aux changements sur l'application (Notamment, la suppression de la classe Sprite, une classe Animation et non AnimatedSprite ... et même une classe Theme car je voulais faire une gestion des thèmes )
    Vous souhaitez participer à la rubrique 2D/3D/Jeux ? Contactez-moi

    Ma page sur DVP
    Mon Portfolio

    Qui connaît l'erreur, connaît la solution.

  2. #142
    Expert confirmé

    Homme Profil pro
    Développeur informatique
    Inscrit en
    Septembre 2007
    Messages
    1 895
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Âge : 47
    Localisation : France, Bouches du Rhône (Provence Alpes Côte d'Azur)

    Informations professionnelles :
    Activité : Développeur informatique
    Secteur : High Tech - Opérateur de télécommunications

    Informations forums :
    Inscription : Septembre 2007
    Messages : 1 895
    Points : 4 551
    Points
    4 551
    Par défaut
    Je vais reprendre tout ce à quoi je vais penser depuis le premier post que je vois en page 6. Et avant tout, je tiens à te tirer mon chapeau : ton projet est bien avancé, et ce n'est pas une mince affaire d'arriver à ce niveau.

    Attention : plus long, tu meurs (je vise l'award "post le plus long de l'année" de DVP.com).

    Citation Envoyé par LittleWhite Voir le message
    Nous voici au tout début de la refonte de la base du jeu. Le but est donc de refaire toute l'interface entre le jeu en lui même et la bibliothèque graphique. Plus précisément, je vais faire en sorte que changer de bibliothèque graphique soit plus que simple et surtout que cela ne modifie en rien le code du jeu.
    Voeux pieux

    Le premier problème qui risque de se poser est que tu doivent te limiter au système le plus simple. Le second problème est que sur certaines plateformes spécifiques, tu risque fort d'avoir des lenteurs induites par le fait qu'il te faille changer la structure des données (par exemple pour faire des copies de buffer de taille importante, etc). Si je prends DX9.c et OpenGL, j'ai une gestion totu à fait différente des resources. En fullscreen, DX9 peut "perdre" les textures qui sont en mémoire graphique, me forçant à les réinitialiser (grosse copie de buffer). Je n'ai pas ce problème avec OpenGL puisque le contexte de rendu n'est jamais perdu. C'est un exemple, bien sûr, mais il montre bien le problème.

    Citation Envoyé par LittleWhite Voir le message
    Pour ce faire, je vais crée classe virtuelle pure définissant les fonctions devant être réimplémentées par la classe utilisant la bibliothèque.
    La petite particularité que je tiens à rajouter, c'est que la classe doit avoir une visibilité tout aussi importante qu'une bibliothèque externe. Cela veut dire, que malgré un héritage, il n'y aura pas d'instance de la classe. Pour ce faire, j'utilise une classe statique dans laquelle on appelera la classe d'interface (j'aurais aussi pu utilisé un Singleton ...).
    Il serait plus judicieux selon moi de faire une fonction factory : en fonction des paramètres et de ce que permet la plateforme, la bonne classe est instanciée. Pas besoin de singleton pour ça (c'est même une assez mauvaise chose).

    Citation Envoyé par LittleWhite Voir le message
    Pour indication, j'ai fait un pseudo diagramme UML (voir pièce jointe). Je n'avais pas de logiciel spécialisé, alors j'ai fait le tout avec OpenOffice (je ne sais plus grand chose de l'UML ... donc c'est peut être légèrement faux).
    C'est légèrement faux, certes, mais ce n'est pas forcément le plus important. C'est compréhensible. Par contre, c'est très peu détaillé. On va travailler la dessus

    Citation Envoyé par LittleWhite Voir le message
    Voici la liste des types que j'utilise actuellement:
    <snip>
    Voici la liste des fonctions:
    <snip>
    Et la liste des macros / définition supplémentaires:
    <snip>

    Pour les types, je redéfini mes types génériques (Colour , Rect, Surface). Lors de l'appel aux fonctions de NEngine, j'utiliserai mes types, et si besoin, ceux ci seront convertis par la couche implémentant la bibliothèque graphique.
    Ces types génériques (on va dire types usuels, parce que "générique" a une signification bien particulière dans le monde C++) peuvent profiter de stratégies qui sont définies selon la plateforme, de manière à profiter du meilleur des mondes : une interface identique quelle que soit la plateforme, mais une implémentation qui correspond à celle de la plateforme. La stratégie peut être définie au rintime (idiome pimpl) ou à la compilation (les policy d'Alexandrescu). Ce dernier point est je pense préférable. Concrètement, ça se passe comme ça :

    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
     
    namespace details 
    {
    	template <class ColorPolicy>
    	class basic_color
    	{
    	public:
    		typedef ColorPolicy policy_type;
    		typedef typename policy_type::color_type color_type;
    		typedef typename policy_type::composant_type composant_type;
     
    	private:
    		color_type m_color;
     
    	public:
    		basic_color() : m_color() { }
    		basic_color(const basic_color& c) : m_color(c) { }
    		basic_color(composant_type r, composant_type g, composant_type b) : m_color(policy_type::make_color(r,g,b)) { }
    		~basic_color();
     
    		basic_color& operator=(const basic_color& other)
    		{
    			m_color = other.m_color;
    			return *this;
    		}
     
    		copmosant_type r() const { return policy_type::fetch_r(m_color); }
    		copmosant_type g() const { return policy_type::fetch_g(m_color); }
    		copmosant_type b() const { return policy_type::fetch_b(m_color); }
     
    		void r(composant_type v) { return policy_type::set_r(m_color, v); }
    		void g(composant_type v) { return policy_type::set_g(m_color, v); }
    		void b(composant_type v) { return policy_type::set_b(m_color, v); }
    	};
    }
     
    // policy pour un l'API toto : 
     
    namespace toto
    {
      struct color_policy
      {
        typedef unsigned int color_type;
        typedef unsigned char composant_type;
        static color_type make_color(composant_type r, composant_type g, composant_type b) { ... return color; }
        static composant_type fetch_r(color_type c) { .... return r; }
        // pareil pour g, v
        static void set_r(color_type& color, composant_type v) { color = (color & 0x00FFFFFF) | (v << 24); }
      };
    }
     
    // l'instanciation. La je met des #if / #else, mais d'autres solutions sont possibles, à partir
    // du moment ou le choix s'effectue à la compilation
     
    typedef details::basic_color<toto::color_policy> color;
     
    // Le reste du code utilise la classe color, sans se poser de question.
    Le tout est joué. Avec du code bien écrit, le compilateur va optimiser sérieusement le code écrit, et tu te retrouvera avec une version optimale de la classe color, quel que soit la plateforme considérée.

    Citation Envoyé par LittleWhite Voir le message
    Je profite d'une petite retouche de mes (mon) types déjà implémentés: Vec2.
    Je lui ai défini deux nouveau noms:
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
     
    typedef Vec2<int> ISize2;
    typedef Vec2<unsigned int> USize2;
    Et ses membres peuvent être appelé de deux manières différentes grace à un 'union':
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
     
    union
    {
    	T x;				/*!< The value on the x axis */
    	T width;
    };
    union
    {
    	T y;				/*!< The value on the y axis */
    	T height;
    };
    Cela peut, d'après moi, rendre le code plus lisible, lorsque j'utilise le Vec2 pour définir des tailles (par exemple, taille de la fenêtre nouvellement créée)
    J'ai un problème avec cette solution : c'est de la réutilisation de code par fainéantise. Etant donné qu'il y a peu de chance pour que tu passes de l'un à l'autre (que tu affecte une taille de fenête à une position), je préconise l'utilisation de deux types différents et non compatible - concrètement, deux classes : size<> et vec2<>. Si tu a besoin de passer de l'un a l'autre, deux fonctions de conversions explicites feront l'affaire (size_to_vec, vec_to_size ; on pourrait me demander "mais pourquoi ne pas prévoir des constructeurs dans chaque classe pour faire la correspondance ?", à quoi je répondrais "mais parce que tu n'as pas envie que la conversion puisse se faire de manière implicite ").

    Citation Envoyé par LittleWhite Voir le message
    La grande question était: comment faire en sorte que nous ayons un seul type visible dans l'application, mais que celui ci est différent pour chaque bibliothèque. A cause de ce problème (et de la solution que j'ai choisi) on ne va pas pouvoir utiliser deux bibliothèques en même temps.
    Voir la solution que je préconise plus haut. De toute façon, faire un projet pour que le choix de la librairie se fasse au runtime est selon moi une erreur. Il est préférable que le choix se fasse à la compilation.

    Citation Envoyé par LittleWhite Voir le message
    Voici un exemple de la solution que j'ai pris pour le type de la fenêtre:
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
     
    typedef unsigned int Uint32;
     
    #ifdef SDL_ENGINE
    	#include <SDL/SDL.h>
     
    	typedef SDL_Surface* Window;
    #elif SFML_ENGINE
    	typedef sf::RenderWindow Window;
    #elif GLUT_ENGINE
    	typedef int Window;	// In GLUT, there is no access to the Window. So int is the type (to check error)
    #endif
    Comme ceci, selon la bibliothèque utiliser, le type va être différent. Par contre, l'utilisateur aura accès à ces types, mais ne pourra pas en faire grand chose. Ce sont les fonctions des implémentations des bibliothèques qui auront à utiliser ces types et non l'utilisateur (sinon, ce dernier n'aura pas de code indépendant de la bibliothèque).
    (Je continue à être perplexe sur ce code ... )
    Dans un sens, c'est une partie de ce qu'il faut faire. Il faut pousser le bouchon encore plus loin : il est nécessaire d'encapsuler tout ça pour proposer à l'utilisateur une interface unique. L'utilisateur n'accède pas aux types dépendant de la plateforme, il utilise les services qui eux, en interne, y accèdent. Tu peux prévoir une passerelle pour leur permettre de faire des choses non standard et non portable, mais dans ce cas, préviens les des risques encourus (problème de portabilité, ...) et si tu le fais, garde à l'esprit que tu es responsable de l'état interne de tes classes. Ca veut dire que si tu donnes accès à un HWND (sous Windows) et que l'utilisateur te fais un DestroyWindow() dessus, c'est de ta faute, et ta librairie ne doit pas planter - à moins que tu ne prévienne l'utilisateur via la documentation que certaines opérations sont interdites car elles risquent de ne pas être compatibles avec ce dont a besoin ta librairie.

    Citation Envoyé par LittleWhite Voir le message
    Maintenant, le code de NE / NEngine / NESDL:
    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
     
    class NE
    {
    private:
    	static NEngine* pEngine;
     
    public:
    	static bool init(void);
    	static void stop(void);
    	static NEngine* get(void);
    };
     
    NEngine* NE::pEngine = NULL;
     
    bool NE::init(void)
    {
    	if ( NE::pEngine != NULL )
    	{
    		LError << "The native engine is already defined";
    		return false;
    	}
     
    	try
    	{
    		NE::pEngine = new NESDL();
    	}
    	catch (ConstructionFailedException& cfe)
    	{
    		LError << cfe.what();
    		return false;
    	}
     
    	return true;
    }
     
    void NE :: stop(void)
    {
    	delete NE::pEngine;
    	NE::pEngine = NULL;
    }
     
    NEngine* NE :: get(void)
    {
    	assert(NE::pEngine);
    	return NE::pEngine;
    }
    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
     
    class NEngine
    {
    private:
     
    public:
    	virtual ~NEngine() {}
     
    	virtual Window createWindow(const USize2& winSize, const unsigned short bpp, const bool isFullscreen, const bool isOpenGL)=0;
    	virtual USize2 getWindowSize(const Window win)=0;
    	virtual int getBitsPerPixel(const Window win)=0;
    	virtual void destroyWindow(Window win)=0;
     
    	virtual bool isCursorVisible(void)const=0;
    	virtual void setCursorVisible(const bool mustShowCursor)const=0;
    	virtual void setCaption(const std::string& windowName, const std::string& iconName)const=0;
     
    	virtual bool needWindowClosure(void)const=0;
    };
    Un singleton ? Ma foi, ce n'est pas une très bonne idée ici. En plus, c'est un singleton sur une classe dieu (god class, la classe qui fait tout). C'est pour le plaisir de te compliquer les choses sur un futur refactoring ou sur des évolutions à venir, hein ?

    Citation Envoyé par LittleWhite Voir le message
    Pour le code de NESDL, on retrouvera exactement ce qui avait dans mon ancienne classe Window (qui a été enlevé entre temps).
    J'ai commencé à intégré ce nouveau code dans l'application, et pour le moment, je peux dire que cela va. Même si le design n'est peut être pas si bien que cela.

    Demain, j'implémenterai le reste de l'interface.
    C'est déjà un bon point

    Citation Envoyé par gbdivers Voir le message
    Je me fais la réflexion suivante : si tu avais travaillé sur la conception dès le départ, en partant d'une feuille blanche, tu te serais probablement posé la question suivante : "Quelle est la responsabilité de mon moteur de rendu ? Et donc, quelles sont les fonctionnalités que je veux exposer dans mon interface ?"
    Là, en faisant du refactoring, tu te poses la question suivante : "Quelles sont les fonctionnalités actuelles ?" Et j'ai l'impression que du coup, tu as beaucoup trop de fonctionnalités pour ton moteur. Et que tu exposes de types ou des fonctionnalités que l'utilisateur ne pourrait pas utiliser (tu le dis toi même), ce qui est un peu inutile (pourquoi ajouter une fonction createWindow qui retourne une Window si l'utilisateur ne peut rien en faire ? = détail d'implémentation)

    Dans le premier cas, tu te serais probablement dis : "la responsabilité de mon moteur est d'afficher mes tiles et mes objets". Et tu aurais probablement exposer les fonctionnalités suivantes :
    - créer une map de dimensions (W, H)
    - affecter le type de tile TILE_TYPE à la position (x, y) (que le moteur de rendu se débrouille avec le gestionnaire de ressources pour savoir l'image à afficher pour TILE_TYPE !)
    - ajouter/déplacer/supprimer un objet OBJECT_TYPE à la position (x, y) (qui peut représenter un véhicule, un bâtiment, une explosion, etc.)
    Et c'est tout

    Je simplifie un peu, pour une première analyse. Il est probable, en poussant l'analyse que l'on pourrait ajouter d'autres fonctionnalités (en particulier init() et quit())
    C'est une simplification certes, mais il y a une sagesse certaine dans ces propos. Se pose la question de savori si on doit faire une analyse upfront un peu poussée ou commencer à coder dès qu'on a une vague idée de ce qu'on souhaite faire. Selon moi, la seconde option n'est disponible qu'aux personnes qui ont une bonne expérience dans l'écriture de code correctement architecturé. Un étudiant brillant peut aussi partager cette intuition qu'il est sur la bonne voie, donc je ne vais pas généraliser, mais dans la plupart des cas, expérience relativement faible en conception == analyse upfront indispensable lorsqu'on travaille sur un projet de cette taille là.

    Il n'y a pas besoin de pousser l'analyse au fond des choses, mais il faut la pousser suffisament pour avoir une vision d'ensemble cohérente de ce que le code va donner au final. Il ya des choix d'architecture importants à faire (et certains de ces choix sont tout sauf triviaux tant ils peuvent avoir une importance grandissante au fur et à mesure que le projet avance).

    Citation Envoyé par gbdivers Voir le message
    L'idée sur laquelle je veux insister est que tu n'as peut être (je précise "peut être", ce n'est que mon avis) pas besoin que NEngine fournisse des fonctionnalités comme charger une image, dessiner un rectangle, etc. A mon avis, ce ne sont que des détails d'implémentation.
    Ce n'est surtout pas le rôle d'un moteur. D'ailleurs, il faudra un jour s'entendre sur ce qu'est un moteur. En fait, pour réaliser une architecture propre, il est indispensable de savoir ce que signifie chacun des termes qu'on emploie. La sémantique rejoint l'architecture un peu trop souvent pour que ça soit une simple coincidence.

    Citation Envoyé par gbdivers Voir le message
    En complément, pour bien faire les choses et bien séparer les responsabilités, il faudrait en plus un gestionnaire de ressources (lire une map, lire des images), un gestionnaire de ta map (un conteneur pour la map de tiles et un conteneur pour la liste des objets) et un moteur de rendu qui affiche la map. Chaque gestionnaire serait constitué de plusieurs classes, par contre chaque gestionnaire serait indépendant l'un de l'autre et serait couplé à ton moteur de jeu.
    Question de sémantique aussi : qu'est-ce qu'un gestionnaire ? Si c'est une collection de resources chargées, alors c'est un collection. Si c'est le système qui permet de créer les resources, alors c'est une factory. Si le gestionnaire gère la durée de vie des ressources, alors c'est encore autre chose. En tout cas, et selon ce que je professe régulièrement ici, ça ne peut pas être les trois ensemble

    Citation Envoyé par LittleWhite Voir le message
    Je ne sais vraiment pas ... j'imagine, un manque d'expérience. Pour moi, le NEngine doit savoir charger des fichiers, après peut être que je vais faire en sorte que le chargement des fichiers ne soit jamais visible dans l'application ... c'est un point que je n'avais pas trop pensé.
    Mauvais choix, selon moi : la lecture d'un fichier n'étant pas une opération triviale, il faut que l'utilisateur sache quand est-ce qu'un fichier va être chargé. Il est toujours possible de proposer des services qui cachent ça, mais l'utilisateur doit pouvoir n'utiliser ces services que s'il le souhaite. Dans le cas contraire, il sera face à un problème difficile à résoudre : il a bien entammé le développement de son jeu, mais à un moment donné, le chargement d'un fichier qu'il ne peut pas éviter rends le jeu inutilisable - et il ne peux rien fair epour changer ça. Imagine la frustration

    Citation Envoyé par LittleWhite Voir le message
    Après vous avoir lu, il semblerait que je laisse trop de visibilité tout le temps, et que mes classes ne font pas vraiment ce qu'elles devraient faire.
    Pour l'instant, je ne pense pas encore être parti dans une mauvaise direction avec mon NEngine. Mais je vais réflechir avec cette histoire de Window que je laisse trop de possibilité ... ... effectivement, l'utilisateur n'a pas besoin de Window ...
    Ca dépends de ce qu'il souhaite faire. Récemment, quelqu'un sur ces forum a posé la question suivante : comment faire pour créer deux fenêtres avec SDL ? Si SDl fournissait un accès bas niveau cohérent au type d'information nécessaire à la création d'une seconde fenêtre, on aurait pu répondre à sa question d'une manière satisfaisante.

    Citation Envoyé par LittleWhite Voir le message
    Je garde un pointeur sur NEngine, car pour faire jouer l'héritage de NEngine sur NESDL ...
    Encore une fois, vous me posez le doute ... j'ai l'impression d'être parti tout de travers avec tout ce programme ...
    C'était surtout car au début du design de NEngine, j'allais faire en sorte que l'on puisse changer à la volé la bibliothèque ... et que puis je me suis rendu compte que c'était pas possible ...
    Espérons que demain j'apporte enfin un truc correct
    On ne part jamais de travers - on se rends compte soudain qu'un choix qu'on a fait rends les choses plus compliqué que nécessaire, voir trop compliquées.

    Comme je l'ai dit plus haut, il est inutile de prévoir le chargement à la volée de la librairie - pourquoi fair eune chose pareille, alors que cette librairie n'est chargée qu'à un seul moment, l'initialisation du programme ? Autant proposer de la linker de manière statique (quitte à générer plusieurs exécutables si l'utilisateur souhaite proposer plusieurs moteurs différents. En pratique, ça peut être une possibilité pour contrer les effets nefastes de certains drivers sur certaines plateformes (par exemple les drivers OpenGL des anciennes cartes ATI sont lents, et il est préférable d'utiliser un moteur D3D sur ces cartes).

    [quote=LittleWhite;5667272]
    Sinon, au niveau des mises à jour:

    Première chose que j'ai fait, c'est de faire en sorte que le nouveau type Window ne cache plus un pointeur.
    Cela donne la déclaration suivante:
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    #ifdef SDL_ENGINE
    	#include <SDL/SDL.h>
     
    	typedef SDL_Surface Window;
    	typedef SDL_Surface Surface;
    #elif SFML_ENGINE
    	#include <SFML/Graphics.hpp>
     
    	typedef sf::RenderWindow Window;
    #elif GLUT_ENGINE
    	typedef int Window;	// In GLUT, there is no access to the Window. So int is the type (to check error)
    #endif
    Et on comprendra facilement que les fonction suivante utilisent maintenant un pointeur:
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    virtual Window* createWindow(const USize2& winSize, const unsigned short bpp, const bool isFullscreen, const bool isOpenGL)=0;
    virtual USize2 getWindowSize(const Window* const pWin)=0;
    virtual int getBitsPerPixel(const Window* const pWin)=0;
    virtual void destroyWindow(Window* const pWin)=0;
    [/code]

    Pourquoi un booléen isOpenGL ? N'est-il pas préférable de créer d'abord une fenêtre, puis de lui affecter un contexte OpenGL ? (je n'ai jamais aimé SDL pour ça).

    Citation Envoyé par LittleWhite Voir le message
    Le deuxième point sur lequel j'ai travaillé, avant de continuer de mettre la pagaille dans mon code, est la documentation. Effectivement, hier en intégrant une première implémentation du NEngine, je n'ai absolument pas documenté mon travail.
    Bonne chose la documentation !

    Citation Envoyé par LittleWhite Voir le message
    Maintenant, je peux continuer mon travail autour du NEngine. Après le fenêtrage, il faut que je m'occupe du dessin à l'écran. En fait, cela sera en grande partie juste un déplacement des fonctions du Renderer dans le NEngine (du coup, le Renderer va partir).
    Première fonction, celle pour nettoyer l'écran, rien de particulier. J'en profite même pour rajouter une petite fonctionnalité permettant d'effacer l'écran avec une couleur passé en paramètre.

    Pour les autres fonctions de dessins, il va falloir un peu modifier le design existant. Il n'aurait jamais vraiment du avoir de lien avec la SDL. Effectivement, les classes Sprite et AnimatedSprite utilisent directement des éléments donnés par la SDL.
    Du coup, au lieu d'avoir un pointeur sur une SDL_Surface, nous avons un pointeur sur une Surface (définie dans le morceau de code ci-dessus). A la place des SDL_Rect, j'utilise mes propres Rect:
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
     
    struct Rect
    {
    	IVec2 position;
    	USize2 size;
     
    	Rect(const IVec2& position, const USize2& size):position(position),size(size) {}
    };
    Lorsque j'ai besoin de la taille d'une surface, j'utilise de nouvelles fonctions introduitent dans le NEngine:
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    virtual USize2 getSurfaceSize(const Surface* const pSurface)=0;
    Voir mon code exemple plus haut.

    Citation Envoyé par LittleWhite Voir le message
    Si vous vous rappelez bien, le Renderer de base connais l'existance des types de l'application ... ce qui n'est pas une bonne chose, car cela rend le Renderer très lié avec le reste de l'application.
    On commence à avoir l'intuition du principe ouvert/fermé, hein ?

    Pour une architecture claire, il est indispensable de chercher à réduire le couplage entre les classes - non pas de manière artificielle, mais de manière naturelle. Pour ça, il faut bien préciser les responsabilités de chaque classe (et respecter le principe de responsabilité unique).

    Citation Envoyé par LittleWhite Voir le message
    Pour enlever ce lien (car il serai dommage d'avoir des références sur Sprite et AnimatedSprite dans le NEngine) je défini des fonctions draw, dans ces deux classes.
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    bool Sprite :: draw(Window* const pWin, const IVec2& position)
    {
    	assert(pWin);
     
    	return NE::get()->drawSurface(pWin, position, this->pSurface);
    }
    et
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
    bool AnimatedSprite :: draw(Window* const pWin, const IVec2& position, const unsigned int time)
    {
    	assert(pWin);
     
    	Rect srcRect = this->getSrcRect(time);
     
    	return NE::get()->drawSurface(pWin,position,this->pSurface,srcRect);
    }
    Bien sur, en complément, il y a dans le NEngine:
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    virtual bool drawSurface(Window* const pWin, const IVec2& position, Surface* const pSurface)=0;
    virtual bool drawSurface(Window* const pWin, const IVec2& position, Surface* const pSurface, const Rect& srcRect)=0;
    Une fois que toutes les référence au Renderer sont enlevé (ou remplacer par la Window, lors du passage en paramètre d'une fonction), le travail autour de l'affichage est fini (et cela fonctionne ).
    C'est un premier pas, mais ça met la fenêtre au centre du design, hors une fenêtre, c'est un concept en dehors de toute notion de rendering. A la limite, tu dessine sur un drawable, qui est plus ou mions lié à une fenêtre (sous Windows, il l'est de manière implicite : le HDC est créé pour une fenêtre donnée, et à partir du HDC, on peut retrouver le HWND correspondant s'il y en a un ; mais HDC et HWND ne sont liés explicitement, ce qui donne une indication intéressante sur ce qu'il est possible de faire en terme de partage de responsabilités).

    SDL propose la notion de surface : tu dessines sur une surface, pas sur une fenêtre. C'est à mon sens un pas dans la bonne direction.

    Citation Envoyé par gbdivers Voir le message
    Oui, mais...
    - tu donnes plusieurs responsabilités à ton NEngine (rendu et gestion des ressources) : maintenance du code plus complexe, plus de risque d'erreur, ajout de fonctionnalités plus difficile, etc.
    - la gestion des ressources n'est pas simplement de lire un fichier mais également savoir quand libérer les ressources, etc. Là, tu ne gère pas.
    - un autre point important (à mon avis) pour toi, qui est étudiant : tu dois te créer une bibliothèque de codes que tu pourras réutiliser plus tard, dans la vie professionnelle. Plus tu crées de code générique, plus tu auras de ressources pour plus tard.

    Personnellement, je crée ce genre de gestionnaire (ou moteur de rendu, ou toute brique logiciel qui peut être autonome) en créant un nouveau projet et j'utilise le main.cpp pour implémenter mes tests unitaires. J'implémente ensuite dans un sous répertoire les différentes classes de mon module. Et pour terminer, je créer mon programme et j'importe les différents modules à utiliser. Je suis sur ainsi que chaque module est indépendant des autres.
    Il va falloir que j'écrive un article d'opinion sur ce sujet spécifique. Il y a beaucoup à dire, et beaucoup à proposer. Je rève de voir le jour où les gestionnaires serotn tout aussi décrié que ne l'est le singleton

    Sinon, bonnes remarques sur la gestion des responsabilités (mais pourquoi alors parler de gestionnaire, hein ? )

    Citation Envoyé par gbdivers Voir le message
    Petites remarques sur le code du dernier post :
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    virtual Window* createWindow(bla bla);
    
    bool Sprite :: draw(Window* const pWin, const IVec2& position)
    {
    	assert(pWin);
     
    	return NE::get()->drawSurface(pWin, position, this->pSurface);
    }
    
    virtual bool drawSurface(Window* const pWin, bla bla);
    Si je comprend bien l'idée, pour dessiner, il faut demander a NEngine un pointeur vers la window... qui sera renvoyé à NEngine pour dessiner
    Ça serait pas plus simple de laisser la windows comme paramètre interne de NEngine ?
    Ca permettrait de se presque-débarasser des Window au niveau de l'interface de NENgine. Bonne chose à mon avis (en attendant de trouver un moyen de se débarasser du NEngine ).

    Citation Envoyé par LittleWhite Voir le message
    Je suis entièrement d'accord avec vous ... mais je n'ai pas de bonne idée actuellement.
    En ce moment, je fais en sorte de répondre a vos remarques (+ celle de Mat.M) qui est de faire un moteur plus générique -> le NEngine.
    Par contre, le NEngine n'est pas exactement comme vous le décrivez. Effectivement, celui ci c'est comment lire un fichier, mais personne n'a dit qu'il devait savoir quand le lire / et quand le libérer. Mon SpriteManager / FontManager (bon ok, celui ci va changer ) resteront en place et s'occuperont des ressources.
    cf. mes propres remarques sur le sujet (décidement, sujet chaud !).

    Citation Envoyé par LittleWhite Voir le message
    Actuellement (certes ce n'est pas encore écrit) le NEngine n'aura qu'une fonction readFile() qui retournera un Surface* et c'est tout. La gestion n'entrera pas ici.
    Ce que je veux dire, pour le moment (et cela semble faux, mais je n'ai pas de meilleur design) c'est que le NEngine n'est qu'une classe qui contient toute les fonctions d'interface aux bibliothèques graphiques. Je veux dire que par là, elle ne fait que dire: "oui, si vous voulez implémenter une nouvelle bibliothèque (que ce soit SFML / GLUT / Qt), et bah vous n'avez juste a hérité la classe NEngine, et plop c'est fait et ça marche"
    Mauvaise idée. Ce type de design marche pour une gestion de devices (car tu offres les services du device en interface de classe ; que le device fasse papa et maman n'est pas important dans ce contexte). Mais dès que tu proposes des services, il est important de proposer une architecture où les différentes responsabilités sont découplées. D'une part parce que c'est plus simple à utiliser, d'autre part parce que c'est plus simple à maintenir.

    Certes, on pourrait croire qu'avoir une classe qui fait tout va permettre de simplifier le portage - il n'en est rien. Il est préférable de demander à l'utilisateur de créer des classes qu'il pourra débugger aisément, avec lesquelles il pourra jongler aisément, plutôt que de lui demander de tout réécrire à chaque fois. Par exemple, lui peut décider d'utiliser une partie de telle code plateform-dependant pour fair quelque chose de précis (par exemple, la gestion de fenêtres de Windows), et changer le code pour telle autre partie (support d'un nouveau moteur). Si tout doit être refait en même temps, ça lui est impossible et il se retrouve avec un surcroit de travail à faire.

    Citation Envoyé par LittleWhite Voir le message
    Après, vous me dites que le design ne va pas ... ou n'est pas réutilisable ... bah je suis extrèmement perdu ... car ... jusqu'à présent ... presque aucun de mes codes n'a été réutilisé (ce n'est pas que ce n'est pas ce que je cherche ... mais c'est juste que je change de philosophie tout les deux jours).
    C'est en forgeant qu'on devient forgeron !

    Citation Envoyé par LittleWhite Voir le message
    Ah! et pour la libération des ressources ... oui effectivement il n'y a pas de gestion, mais en même temps, le NEngine est un "Work in Progress" ... donc ... pour l'instant je ne vois même pas ou une telle gestion pourrait s'intégrer. Et puis comme je l'ai dit, le NEngine aura certes une fonction FreeSurface() mais rien de plus (donc on voit bien qu'il n'y a pas de gestion de ressources ... mais juste une fonction qui permet de libérer la ressource.
    Une seule fonction de libération ? Ca semble être un boulot pour un destructeur ça. Il faut éviter l'idiome allocation/libération explicite dès lors qu'on utilise un langage qui permet de s'en passer c'est à dire un langage dans lequel on peut implémenter le concept RAII (et en C++, on peut - c'est même très conseillé).

    Citation Envoyé par LittleWhite Voir le message
    Après, je suis un peu perplexe, vous me parlez de réutilisabilité de code, mais hier, il m'a semblé que vous disiez que mon code était trop générique, et que le votre me sembler restreindre l'utilisateur à faire un script (soit un code avec peu de choix). Mais ... pensez vous qu'un code plus restreint est plus réutilisable?
    Si ma classe toto propose 4 services ayant traits à toto, elle est utilisable à chaque fois que je dois totoiser. Si ma classe propose 20 services dont 4 qui sont liés à toto et que je dois totoiser, il n'est pas dit que je puisse réutiliser la classe en question car les autres conditions d'utilisation ne sont peut être pas remplie.

    Plein de petites classes permet de réduire le couplage, et d'augmenter les chances de réutilisabilité. Donc dans un sens, un code plus restreint mais bien construit est plus réutilisable qu'un code outrancièrement générique.

    Citation Envoyé par LittleWhite Voir le message
    Sinon, je suis dans la globalité d'accord avec vous ... mais je reste tout de même vraiment perplexe (surtout avec la réutilisabilité du code).
    C'est un concept ardu, je le comprends bien (après toutes ces années). En fait la réutilisabilité n'est pas nécessairement la panacée de la programmation objet. C'est une conséquence qui est quelque fois bien sympathique, mais ce n'est pas nécessairement le but à atteindre. Le but à atteindre, c'est l'utilisabilité, pas la réutilisabilité

    Citation Envoyé par LittleWhite Voir le message
    Je dois dire que cette fois, je suis dans une très grande reflexion sur comment faire la base de mon programme.
    J'aimerai bien enlever ce besoin de Window qui se répand sur tout le code, mais si je le fais, je réduit la généricité de mon code ... et du coup, celui-ci devient moins évolutif.
    Non, non

    Tu as besoin d'une Window pour faire quoi, conceptuellement ? Pour gérer tout ce qui concerne le fenêtrage. Tu ne devrais pas en avoir besoin pour dessiner, ni pour faire quoi que ce soit d'autre. Etant donné que le code de gestion du fenêtrage est souvent réduit dans un jeu, le fait de diminuer le besoin d'avoir une Window qui traine ici et là rends le code au contraire plus générique (un besoin en moins, c'est toujours plus sympa), et le rends plus évolutif (parce que tu n'es plus limité par cet objet).

    Citation Envoyé par LittleWhite Voir le message
    Le deuxième exemple du même genre, est basé sur la gestion du curseur (celui du système d'exploitation). Effectivement, il est loin d'être nécessaire de laisser les fonctions suivantes:
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    bool isCursorVisible(void)const;
    void setCursorVisible(const bool mustShowCursor)const;
    Mais cela veut aussi dire que si un jour je reprends ce moteur, je vais devoir changer la base même de celui-ci.
    Ca, c'est un problème d'architecture. Une classe cursor, une fonction get_system_cursor() (+ le set qui va bien) et une méthode window::get_cursor() (+ le set qui va bien) et hop, le tour est joué.

    Citation Envoyé par LittleWhite Voir le message
    Bon, je prends une décision (et j'essaie de ne pas revenir dessus). Le NEngine, aura bel et bien la gestion de multiple fenêtre, mais ceci sera masqué par NE, qui fera en sorte d'en concerver qu'une seule. L'application n'aura jamais accès à celle-ci.
    Il est de même pour le NEngine. L'application ne jouera plus avec directement.
    Du coup, coté NEngine nous avons actuellement:
    Quand j'aurais une meilleure vision de ce que tu souhaites faire, et si j'en ai le temps, je vais tenter de te proposer d'autres solutions. Parce que je ne trouve pas ta solution actuelle très satisfaisante.

    Citation Envoyé par gbdivers Voir le message
    C'est moi que tu vouvoies
    C'est une facheuse habitude qu'il a Remarque, on ne va pas se plaindre de trop de politesse non plus hein ?

    Citation Envoyé par gbdivers Voir le message
    Plusieurs remarques (encore ) :

    1. Toutes les idées que je te donne ne sont pas forcement à implémenter tout de suite. Mais comme on parle de la partie conception, il faut quand même prévoir pour éviter de devoir refactoriser après.

    Prenons par exemple le gestionnaire de ressources. Il est évidement que ce n'est pas le bon moment pour ajouter ce type de nouvelle fonctionnalité, surtout que tu n'as pas encore réfléchit aux stratégies de gestion des ressources. Mais si tu laisses le NEngine s'occuper de la gestion des ressources, tu devras le modifier quand tu implémenteras un vrai gestionnaire. Et tu devras modifier plusieurs classes, avec les risques d'erreurs, de régression et de recherches du code à refactoriser. Si tu transfères tout de suite les fonctions de gestion de ressources dans une classe dédiée, tu gagneras du temps lorsque tu voudras refactoriser.
    Pour faciliter les refactoring à venir, j'ai une technique : si je ne sais pas ù je dois faire telle ou telle chose, alors je crée une classe pour le faire. Il est plus simple d'utiliser une classe dans une autre (par composition) que d'enlever le code d'une classe pour en faire une nouvelle.

    Citation Envoyé par gbdivers Voir le message
    2.
    c'est que le NEngine n'est qu'une classe qui contient toute les fonctions d'interface aux bibliothèques graphiques
    Oui, c'est le but
    L'idéal serait que par exemple, si tu utilises plusieurs lib dans plusieurs modules, les dépendances soient minimales (par exemple, si tu as un module GUI, un module son, un module ressources et un modules réseau et que tu utilises SDL, Fmod, libxml et boost::asio, chaque module ne devrait être lié qu'a une seule lib chacun)

    Le problème dans ton code actuel est que tu masques la dépendance à la SDL mais... la dépendance existe quand même :
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    typedef SDL_Surface Window;
    typedef SDL_Surface Surface;
    Si tu utilises le type Window dans ton code, tu écriras peut être :
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    Surface* s = nengine->readFile(...);
    int width = s->w; // ok
    Ensuite, si tu changes de lib graphique, tu auras par exemple :
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    typedef QPaintDevice Surface;
    ...
    Surface* s = nengine->readFile(...);
    int width = s->w; // erreur, w n'existe pas dans QPaintDevice !
    Résultat, tu n'as pas supprimé la dépendance à la SDL
    Tout à fait d'accord avec cette analyse. C'est là qu'une architecture à base de policy tel que présenté ci-dessus + modèle de compilation bien pensé peut permettre de résoudre le problème. L'idée est d'avoir, pour une dépendance donnée, une interface qui est toujours la même (la policy jouant le rôle d'un adapteur au sens du design pattern du même nom). L'adapteur est alors compilé de manière conditionnelle en fonction de la plateforme cible.


    Citation Envoyé par gbdivers Voir le message
    3.
    Après, vous me dites que le design ne va pas ... ou n'est pas réutilisable ... bah je suis extrèmement perdu ... car ... jusqu'à présent ... presque aucun de mes codes n'a été réutilisé (ce n'est pas que ce n'est pas ce que je cherche ... mais c'est juste que je change de philosophie tout les deux jours).
    C'est bien le problème. Et c'est surement indicateur d'un problème de conception
    J'irais plus loin : c'est un manque d'expérience dans le domaine de la conception. Du coup, je t'enjoins (publicité gratuite) à visiter mon blog, qui parle de ces choses et d'autres. Les catégories qui peuvent t'intéresser sont celles ayant trait à l'architecture orientée objet, à l'architecture de manière générale, et au C++. Il y a bien evidemment d'autres ressources totu aussi bonne (ouh la ; auto-lancé de fleurs) voire bien meilleure sur la toile.

    Citation Envoyé par gbdivers Voir le message
    4.
    Après, je suis un peu perplexe, vous me parlez de réutilisabilité de code, mais hier, il m'a semblé que vous disiez que mon code était trop générique, et que le votre me sembler restreindre l'utilisateur à faire un script (soit un code avec peu de choix). Mais ... pensez vous qu'un code plus restreint est plus réutilisable?

    Il faut que le code soit générique au maximum pour être réutilisé mais chaque élément doit avoir une responsabilité unique. Dans le cas contraire, quand tu modifieras une des reponsabilités, tu devras tout modifier (non respect du OCP)

    Par exemple, la resposabilité "dessiner l'écran de jeu" de NEngine peut être diviser en plusieurs responsabilités : "dessiner du texte", "dessiner une image", etc.
    Bon, d'autres me font de la pub, du coup, je suis moins circonspect (merci gbdivers ! )

    Citation Envoyé par gbdivers Voir le message
    <snip point 5>

    6. Est-il indispensable de créer 2 classes pour les sprites (AnimatedSprite et Sprite). Ne peut on pas considérer que Sprite est un AnimatedSprite avec 1 seule image ?
    Pas d'accord avec toi : un sprite n'est pas un sprite animé. Conceptuellement, c'est la même différence qu'entre un film et une image. On voit que les deux n'ont pas le même comportement, et forcer l'utilisation d'une interface unique pour gérer les deux cas va forcer les objets à s'intéresser à leur contenu pour effectuer des décisions qui sont basée sur le type de ce contenu. Du coup, c'est une violation d'OCP.

    Citation Envoyé par gbdivers Voir le message
    Mon conseil : mets à plat sur une feuille de papier la structure de l'application, en indiquant bien les responsabilités de chaque classe/module et les connections entre elles. Et lit le blog d'Emmanuel
    Ca c'est une bonne chose à faire. Pas lire mon blog (ça se discute ; mais j'aime bien qu'on lise mon blog quand même), mais le fait de poser la structure sur une feuille blanche. Il faut s'intéresser à deux choses : la première, le comportement souhaité. La seconde, l'abstraction (au sens mathématique, pas au sens classe virtuelle) souhaitée. Mais surtout, il faut comprendre le problème que tu poses toi même - et ce n'est pas la partie la plus facile. Il faut aussi éviter un découpage en objets qui suivrait un découpage trop physique (cette histoire de classe table et classe chaise m'a toujorus horripilé), qui est ce que j'appelle régulièrement sur mon blog une vision ontologique (ontologie = étude de l'être en tant qu'être ; c'est à dire se mettre à la place de la chose qu'on décrit pour se décrire), pour préférer un découpage comportemental, donc plus orienté sur ce que doivent faire les classes (en y introduisant les bonnes abstractions).

    Ca aussi c'est un billet (assez théorique) qu'il me faudra écrire un jour. Ca fait des années que je le repousse, mais je pense qu'il me sera nécessaire de l'écrire dans un futur proche.

    Citation Envoyé par gbdivers Voir le message
    Je vais te faire quelques graphismes pour illustrer mon propos.
    Je serais bien inspiré de faire de même

    Citation Envoyé par LittleWhite Voir le message
    NEngine décrit une interface pour guider l'implementation de bibliothèques différentes.
    NE est une manière de cacher le tout afin de rendre un accès global dans tout le Jeu à NEngine (et en le simplifiant) (on va surement revenir dessus)

    Le problème dans ton code actuel est que tu masques la dépendance à la SDL mais... la dépendance existe quand même
    <snip>

    Alors je ne sais pas comment faire.
    J'ai des bibliothèques qui chacune, redéfinissent un type pour les fenêtres, et un type pour les surfaces (Sprite / Image).
    Donc oui, certes je ne cache pas qu'à un moment ou a un autre, j'utilise des SDL_Surface ou autre, mais d'un coté, cela est dans le choix du créateur de l'application qui utilise le NEngine, car:
    • Je ne peux pas cacher le type, car celui ci est nécessaire à l'utilisateur (et j'imagine que la seule solution, c'est d'utiliser des classes Window et Sprite, qui utilisent les types que j'ai défini, non ?)
    • Si l'utilisateur veut ne pas faire de multi plateforme / multi bibliothèques, bah il peut toujours utiliser le type comme bon lui semble

    Donc oui, c'est mal ... je ne cache pas efficacement le type. Par contre, je lui donne toutes les fonctions qu'il faut pour l'utiliser de manière multiplateforme. Que me proposez vous, de propre, qui demande peu de changement lorsque je dois implémenté une nouvelle bibliothèque (car refaire une classe Window pour chaque bibliothèque, n'est ce pas un peu trop lourd?)
    C'est possible (j'ai cité les deux possibilités plus haut : idiome pimpl ou classe policy.

    Pendant que j'y suis: pimpl, ça veut dire private implementation. Ca s'implémente comme ça (pseudo-code):

    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
     
    class A_interface
    {
    private:
      A_implementation* m_pimpl;
    public:
      A_interface(params...)
      {
        // serait aussi bien dans la liste d'initializers, 
        // mais bon, pour une histoire de clarté du code...
        m_pimpl = new A_specific_implementation(params...);
        // bien évidemment, la classe d'implémentation utilisée 
        // dépends de la plateforme cible
      }
      void do_something()
      {
        m_pimpl->do_something();
      }
    };
    Citation Envoyé par LittleWhite Voir le message
    <snip>

    En même temps, le NEngine ne dessine pas l'écran ... mais dessine une Surface ... donc je ne vois pas le problème ... je suis dans la forme canonique (si je puis dire)
    Mauvaise foi !

    Plus sérieusement, ce que gbdivers essaie de dire c'est que ce n'est pas au NEngine de centraliser les méthodes de dessin. Il est bien plus judicieux d'avoir (par exemple) un text_renderer, un model_renderer, un sprite_renderer (note : ce découpage n'a pas nécessairement de sens ; c'est un exemple), aux résponsabiulités réduites et au comportement bien défini.

    Citation Envoyé par LittleWhite Voir le message
    Ouep ... bah je ne vois pas encore comment faire ... pour que en même temps je me débarrasse de ce passage de paramètre à travers tout le programme pour avoir la fenêtre. D'un autre coté, j'essaie d'éviter le Singleton (je vais lire l'article cette après midi)
    A tu vraiment besoin de la fenêtre partout ?

    Mon idée est que non. Lorsque tu souhaite dessiner, tu as besoin d'un drawable quelqconque (ça peut être la fenêtre, soyons large d'esprit). Mais en a tu besoin pour déplacer un sprite ? Pour vérifier qu'un missile touche sa cible ?

    Exercice : pour chaque fonction de ton code, limite ses besions à ce dont cette fonction a besoin au niveau conceptuel. Par exemple, pour déplacer un sprite, tu as besoin d'avoir les limites de déplacement, le vecteur de déplacement et le sprite. Pour faire évoluer une animation de sprite, tu as besoin du sprite animé et d'une donnée permettant de spécifier quelle frame doit être affiché (ça peut être l'heure système). Etc.

    Citation Envoyé par LittleWhite Voir le message
    Oui et non. Mais une seule classe serait de donner trop d'importance à cette classe (responsabilité unique?). (Il faut que je lise l'article aussi (je vais le faire)).
    Y a un Sprite, et y a un Sprite qui change selon le temps (soit un héritage, comme je l'ai fait ...) Donnez moi des arguments (favorables et défavorables) et je verrai si je dois changer
    You got it right ! Sauf peut-être pour l'héritage (étant donné le comportement différent, il y a peut être une violation du principe de substitution de Liskov ; ça doit être évalué au cas par cas).

    Citation Envoyé par LittleWhite Voir le message
    Mon conseil : mets à plat sur une feuille de papier la structure de l'application, en indiquant bien les responsabilités de chaque classe/module et les connections entre elles.
    Ouep, je bloque peut être un peu sur cette partie
    La solution à ce problème tiens dans la réponse à la question "est-ce que tu sais vraiment ce que tu veux faire ?". Si tu connait la réponse, commence par une feuille blanche ou tu listes les différentes choses que tu souhaite faire. Ce n'est pas une description technique : c'est une description qu'on dit fonctionnelle (je n'aime pas ce terme car à la fois trop vague et trop précis). Les phrases ressemblent à :

    • créer un sprite
    • afficher un sprite
    • créer un sprite animé
    • ...


    Une fois que pense avoir une liste complète (et quand je dis complète, je le pense : tu n'imagines pas le nombre de projets qui ont échoué parce qu'au début, on a pas pensé à une petite fonctionnalité ayant un impact global sur l'architecture finalement choisie. Comme l'application mono document qui doit tout à coup gérer plusieurs documents à la fois), tu devrais pouvoir voir des motifs apparaître dans ta description. C'est ta base de travail. Laisse ton esprit y réflechir et reviens-y rapidement pour déterminer un premier découpage.

    Citation Envoyé par LittleWhite Voir le message
    J'essaie de créer un jeu video d'abord Après, j'essaie de le faire pas trop mal. Là je me rends compte que l'on se penche plus sur la création d'un moteur, que du jeu en lui même (et oui, malheureusement fearyourself, je vais perdre encore un peu de temps là dessus )
    Je cherche à avoir un Design assez niquel (mais si en programmation tout n'est ni blanc ni noir) ... et que par la discussion je chercher à connaitre le pour et le contre de chaque solution. Pour l'instant, le design avec le NEngine n'est pas trop mauvais (pour moi), juste son utilisation dans le NE qui est très barbare.
    Après, une note que j'aimerais bien travailler avec le NEngine, c'est la séparation en petits modules (Window / Draw / Sound / Event) ... mais là je peine encore un peu.
    Si l'utilisation d'un module est cauchemardesque, ça veut dire que l'architecture du module en question pose un problème Toutefois, ce n'est pas très grâve - après tout, on acquiert plus d'expérience en se trompant qu'en faisant juste du premier coup. Comme le disais Edison, Je n'ai pas échoué. J'ai simplement trouvé 10.000 solutions qui ne fonctionnent pas.

    La suite est à lire en conjonction avec :
    Citation Envoyé par fearyourself Voir le message
    Je ne questionne pas le bon fondement de cette discussion, ni de son utilité. C'est un peu la discussion que nous avons eu sur mon projet : veux-tu faire un moteur générique ou un jeu ? Ensuite dans le "faire un jeu", veux-tu le faire avec un moteur spécifique que tu pourras réutiliser que très peu ou assez générique pour ce que tu veux ?

    Je pense, personnellement, qu'il vaut mieux faire une première version de ton jeu avec ton moteur comme il est, et ensuite voir, une fois le projet fini, ce qui était moins bon et ce qui était super.

    Ne perd pas de temps maintenant sur ce genre de détail et avance dans le jeu sans forcément prendre trop de raccourcis mais il vaut mieux un truc qui fonctionne que pleins de choses sur du papier.

    Si on prend mon moteur comme exemple, clairement il est improvisé et clairement il y a des choses qui bougent car je n'ai pas pensé à ce détail ou à celui-là. Mais, lorsque j'aurai fini la première version, je peux le reprendre, tirer les conclusions nécessaires et en faire une refonte compléte. Moi, je vise un moteur générique pour des raisons personnelles, mais toi tu faisais un jeu bien défini, alors pourquoi, pour ce projet, t'embêter avec ces détails ?

    Moi, je le faisais comme projet du soir/weekend pour délirer un peu et pas trop prendre la tête dessus, c'est pour me reposer et m'amuser, et toi ?

    C'est sur ces points que tu me perds un peu, je pense et potentiellement, tu te perds un peu,
    Jc
    Le principal est de réussir à faire ton jeu. Personnellement, et malgré tout ce que j'ai pu dire, ne change rien à ton moteur si ce n'a pas d'utilité directe pour ton jeu. Dès que tu aura fini ton jeu avec comme cible une plateforme unique, porte le sur une autre plateforme. Puis une autre. Dans le process, tu va devoir refactoriser de bonnes portions de ton code. Puis, une fois que tu aura ciblé plusieurs plateformes, essaie de faire un autre jeu sur la même base. Tu va voir que "rapidement", tu va voir par toi même quels sont les problèmes d'architecture - et tu va voir aussi comment les corriger par toi même.

    Au final, l'architecture de ton moteur ne sera peut-être pas encore niquelle, mais elle aura évolué de manière significative. Et surtout, le prochain moteur que tu fera incorporera directement les solutions aux problèmes que tu auras rencontré - et ça, c'est une très bonne chose.

    Pour faire court : fait quelque chose qui marche le plus vite possible, et reviens dessus lorsque tu le peux. Comme j'ai coutume de le dire régulièrement, cent fois sur le métier remettez votrez ouvrage. Ca vaut pour les artistes, pour les artisans qui visent une certaine perfection, et ça vaut aussi pour les programmeurs qui souhaittent atteindre une certaine excellence.

    (ce qui rejoint :
    Citation Envoyé par CriPpLe Voir le message
    De toute façon même si t'essayes de faire un moteur générique pour ce jeu, pour le prochaine jeu que tu voudras faire tu auras tellement appris qu'au final tu partiras sur un nouveau moteur tout beaux en reprenant que certaines choses de l'anciens.
    même si je préfère ma formulation )

    Citation Envoyé par LittleWhite Voir le message
    * explose de rire
    (C'est nerveux )

    Pardon ... mais c'est surtout que j'ai réfléchi à cette "satané" conception toute l'après midi, et je suis sur d'être loin d'un bon truc ....
    Et ce n'est pas grave : tu te complique trop par rapport à ce que tu souhaites faire dans l'immédiat. Dans la méthodologie Xtreme Programming, il y a une bonne pratique qui s'appelle DTSTTCPW. Sous cet acronyme abscons se cache la phrase "do the simplest thing that could possibly work" - et quand on dit la chose la plus simple, il ne s'agit pas d'une chose simple. Il s'agit vraiment de la chose la plus simple. Après tout, tu ne redéveloppe pas des classes d'entier à chaque fois que tu as besoin de faire une addition

    Citation Envoyé par LittleWhite Voir le message
    Pour réponse à toutes vos questions (du moins j'essaie) ...:

    Je fais ce jeu pour le plaisir, aucune contrainte, aucune obligation, c'est un jeu que j'ai commencé en fin de vacances d'été, et que je continue dès que j'ai un peu de temps libre (ce qui recommence à me manquer à cause de l'université )

    Je suis étudiant, certes, mais je suis étudiant en programmation de jeux vidéos.
    Ou ça ?

    Citation Envoyé par LittleWhite Voir le message
    je tiens à le préciser car cela change un peu la donne sur le mot "étudiant". Pourquoi cela change la donne? Simplement car j'ai un cours qui apprends a crée un jeu video: "Advances Games Software Development" et qui celui-ci utilise la SDL pour la GP2X (comme console à contrainte) et que dans celui-ci, le plan est:
    1. Préparation du projet (faut au moins compilé le Hello World sur la GP2X n'est ce pas )
    2. Ouverture de la fenêtre
    3. Affichage Sprite
    4. Gestion commandes (un joystick et les bouttons)
    5. Son
    6. Boucle de jeu

    Puis dans les choses qui j'ai pas encore fait:
    1. Gestionnaire de mémoire
    2. Système de messages
    3. Un truc surprise

    Sachant que la partie que j'ai déjà faite (je ne suis pas en retard) a été faite en trois mois... et que point de vue réutilisabilité, bah j'ai juste repris le code d'OpenAWars (et comme l'univ a des logiciels contre le plagiat, il faut bien que je précise d'où vient mon code / le pourquoi / que c'est bien le mien )

    Donc pour vous dire, le code que j'ai pour ce cours, ressemble a celui d'OpenAWars (et l'inclusion des exceptions et un ajout que j'avais surtout fait pour le cours).
    Donc j'ai l'impression que dans le monde du jeux videos (et surtout console) on se fout (je ne dis pas que c'est une vérité) de la conception du fond ... tant que l'on puisse afficher nos trois sprites en bataille.
    C'est à la fois vrai et à la fois faux. Vrai, parce que (pour bien connaitre l'industrie en question) l'architecture logicielle a toujours été un non problème. Le syndrome NIH a toujours été très fort dans cette industrie. Il a commencé à disparaitre avec la montée en puissance des cartes vidéos. Tout à coup, les développeurs se sont rendu compte que fair eun moteur pour chaque jeu coutait un fric fou, surtout vu la complexité dudit moteur. Maintenant, la tendance est à la réutilisation (Ubisoft a même crée une unité technologie qui est responsable de la fourniture de moteurs (avec doc, exemples, etc) à toutes les branches développement du studio). Du coup, l'architecture deviens - enfin, dirons certains - un plus non négligeable.

    Il existe à l'heure actuelle deux types de programmer heroes dans l'industrie. Les bidouilleurs de génie comme Carmack - ils sont capables de créer un moteur de jeu comme personne. Leurs connnaissances techniques sont impréssionnantes. Et puis il y a les Mike West et consorts ; des architectes dans l'âme. Les détails techniques sont largement à leur portée, mais ce qui les intéresse, c'est d'avoir quelque chose qui peut être utilisé pour développer des jeux complexes - parce le standard AAA en ce moment, c'est un jeu d'une complexité qui n'a rien à envier à celui d'un logiciel spécialement réalisé pour traiter l'ensemble des transactions boursière arrivant sur Wall Street (et crois moi, il est facile de se sentir petit-joueur devant un programme capable de traiter en temps réel plusieurs millions de transactions boursières par seconde).

    Citation Envoyé par LittleWhite Voir le message
    <snip>

    Revenons à OpenAWars. Oui je travaille la conception, car je me suis rendu compte que c'était un point faible du projet. Peut être pas sur le moteur en lui même même si celui-ci est peu sujet aux évolutions (et d'un coté, cela était normal, car cela n'était absolument pas le but au début ... et je pouvais très bien géré le Renderer en OpenGL comme prévu) ... Mais bon, en période de refactoring, pourquoi ne pas refaire la base :p (et puis je suis sur un système de controle de version et je sens que j'aurai du faire une brance )
    Parce que ça n'a pas d'impact sur le reste. Si l'idée est de faire les choses propres mais sans proposer d'évolution au produit final, alors ça n'a pas d'intérêt

    Citation Envoyé par LittleWhite Voir le message
    Maintenant, il faut dire que cela commence à bassinner un peu (surtout la responsabilité unique) comme vous allez le voir par la suite.
    Je comprends tout à fait. Il faut savori qu'avec nous, tu t'aventures sur un terrain miné. Nous n'allons pas nous contenter de te dire c'est bien. Nous allons te proposer des améliorations (et expliquer en quoi ce sont des améliorations). Le problème est que si tu nous écoutes, tu es toujours sur ton moteur dans 10 ans. Il faut savoir dire stop, c'est à dire enregistrer nos recommandations, et les appliquer sur un prochain projet - parce que le plus important, c'est quand même ton projet, pas ce qu'on dit

    Citation Envoyé par LittleWhite Voir le message
    Alors, oui, je vais faire en sorte d'avoir mon NEngine (la version que je présente après) de stable et propre ... mais si celle que je présente ne convient toujours pas ... bah ... je crois que je vais un peu abandonné l'idée du tout propre tout clean ... parce que là ... cela m'embête vraiment (surtout que je dois faire le refactoring du reste du jeu, qui est tout aussi important).
    Donc, ne le prenez pas mal si j'ai vais avancé sans trop me soucié de mon design ... mais dans tout les cas je vous écoute, et je réfléchi à propos de tout ce que vous me dites. Maintenant, j'ai passé près d'une semaine à tout cassé / reconstruire là ou je ne pensais pas le faire y a une semaine, et comme en entreprise, même si cela n'y est pas ... je vais avancé.
    Donc demain (ou après demain ) j'annonce ... je vais avoir mon joli NEngine tout propre \o/
    Il y a toujours eu (et il y aura toujours) un compromis implcite à trouver entre élégance du design et réalité de celui-ci. Tu commences à l'appercevoir, et c'est une bonne chose. Le design parfait n'existe pas - il est donc important de savoir s'arrêter avant de péter un cable.

    Citation Envoyé par LittleWhite Voir le message
    Donc voici ma nouvelle proposition de design:

    En pièce jointe, vous pouvez trouver encore une fois, un schéma "UML".
    Je souhaiterais que gbdivers y jette un coup d'oeil pour me dire si cela peut convenir. Sachant que j'essaie d'appliquer au mieux ce que l'on me dit. (Notamment, j'utilise des loader spécifiques, pour suivre le principe de responsabilité unique (qui risque de m'enerver )).
    Le schéma n'est pas assez détaillé pour que je donne mon avis.

    Citation Envoyé par LittleWhite Voir le message
    Mon deuxième problème, c'est que l'on a toujours une recompilation totale du projet si on veut changer de bibliothèque. Je n'ai pas trouvé de moyen de l'éviter ... :s
    Scinde le moteur en deux parties : une partie générique, et une partie spécifique à la plateforme (dans l'exemple des policy, ça devient simple, parce que la partie générique est principalement composée de fichiers header contenant des templates). De cette façon, utiliser une nouvelle plateforme signifie recompiler le code de la plateforme et linker le tout avec le code générique et le code du jeu.

    Citation Envoyé par LittleWhite Voir le message
    Sinon, par rapport au schéma, il faut que je décrive un peu les fonctions que je veux mettre dedans.

    La classe Window aura:

    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
     
    Window* createWindow(const USize2& winSize, const unsigned short bpp, const bool isFullscreen, const std::string& windowName, const std::string& windowIcon ="", const bool showCursor = false)=0;
    USize2 getWindowSize();
    int getBitsPerPixel();
    void destroyWindow();
    createWindow ? A quoi pourrait donc servir ce constructeur... (même reflexion pour le destroy).

    Sinon, c'est très bien

    Citation Envoyé par LittleWhite Voir le message
    Le Renderer (qui lors de la const:
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
     
    Renderer(Window* pWin);
     
    clearScreen(const Colour& colour);
     
    bool drawRect(const Rect& tile, const Colour& colour)const;
    bool drawSurface(const IVec2& position, Surface* const pSurface);
    bool drawSurface(const IVec2& position, Surface* const pSurface, const Colour& mask);
    bool drawSurface(const IVec2& position, Surface* const pSurface, const Rect& srcRect);
    bool drawSurface(const IVec2& position, Surface* const pSurface, const Rect& srcRect, const Colour& mask);
     
    bool updateScreen();
    C'est très bien ça.

    (suite prochain message - limite de 60K dépassée)
    [FAQ des forums][FAQ Développement 2D, 3D et Jeux][Si vous ne savez pas ou vous en êtes...]
    Essayez d'écrire clairement (c'est à dire avec des mots français complets). SMS est votre ennemi.
    Evitez les arguments inutiles - DirectMachin vs. OpenTruc ou G++ vs. Café. C'est dépassé tout ça.
    Et si vous êtes sages, vous aurez peut être vous aussi la chance de passer à la télé. Ou pas.

    Ce site contient un forum d'entraide gratuit. Il ne s'use que si l'on ne s'en sert pas.

  3. #143
    Expert confirmé

    Homme Profil pro
    Développeur informatique
    Inscrit en
    Septembre 2007
    Messages
    1 895
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Âge : 47
    Localisation : France, Bouches du Rhône (Provence Alpes Côte d'Azur)

    Informations professionnelles :
    Activité : Développeur informatique
    Secteur : High Tech - Opérateur de télécommunications

    Informations forums :
    Inscription : Septembre 2007
    Messages : 1 895
    Points : 4 551
    Points
    4 551
    Par défaut
    Citation Envoyé par LittleWhite Voir le message
    La classe Sound:

    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
     
    bool play()
    bool stop()
    En fait, comme je ne me suis jamais vraiment interessé au son ... je ne sais pas
    Tu verras plus tard, en fonction de tes besoins. C'est bien de l'avoir prévu. Tu pourrais avoir besoin d'une classe mixer.

    Citation Envoyé par LittleWhite Voir le message
    La classe Input:
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
     
    ArrowsDirection getDirectionPressed(void)const;
    Buttons getButtonsPressed(void)const;
    Il va surement y avoir une liste d'Input que l'on conservera et dans laquelle on pourra mettre un Input que l'on veut (joystick / clavier)
    Suffisant.

    Pour plus tard, tu pourrais avoir besoin d'encapsuler ça pour gérer des combinaisons de touche. Par exemple, la flexhe droite permet d'aller à droite. Il est plus sympa d'avoir un sytème te permettant de savoir ce que tu dois faire plutot qu'un système disant ce que fait l'utilisateur. Mais je le répète, c'est suffisant pour l'instant.

    Citation Envoyé par LittleWhite Voir le message
    La classe TimeSystem:

    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
     
    unsigned int getTime();
    void delay(unsigned int);
    une méthode begin_timer qui renvoie un objet timer (dont le t0 est le moment ou l'appel à la fonction begin_timer s'exécute) pourrait être une addition notable si tu en a besoin. Encore une fois, si tu n'en a pas besoin, ne le fait pas.

    Citation Envoyé par LittleWhite Voir le message
    La classe Surface:

    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
     
    Surface(const USize2& size);
     
    USize2 getSize();
    getNativePtr();	// Surement en friend avec le Renderer ... mais là ... j'ai un GROS doute
    Pas assez de chose à analyser, donc pas d'opinion.

    Citation Envoyé par LittleWhite Voir le message
    Et puis les Loader avec une méthode load:
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
     
    Resource* load(const std::string& fileName);
    Les loaders vont faire ce que j'appelle "ResourcesManager" soit ma std::map qui permet de charger qu'une seule fois pour toute le fichier. Est-ce que je casse la réponsabilité unique en faisant cela? Je pense que oui, mais au début, je voulais faire un ResourceManager template, tout cela en forçant les types pris en compte par cette classe à avoir une fonction load / free. En pensant cela, je voulais faire en sorte que la Surface soit donc une Resource (obligation d'avoir un free()) et une FileResource (héritage de Resource), pour l'obligation du load à partir d'un fichier. Mais je me suis dit que cela n'était pas possible et que cela cassait totalement avec cette réponsabilité unique.
    Autre point sur lequel je bute ... c'est que ce "Loader" fera donc la gestion de la libération O_o
    C'est plus compliqué que ça. Pour un premier jet, ça ira certainement.

    Citation Envoyé par LittleWhite Voir le message
    Ah oui! J'oubliais ! L'Engine est une classe qui contient tout les éléments comme marqué dans le diagramme ... et qui propose une fonction init / quit (pour l'initialisation des bibliothèques) et des setter / getter pour mettre en place la Window le Renderer ...
    Du coup, NEngine ne sert plus à grand chose. Et si il n'y avait plus de NEngine ?

    Citation Envoyé par LittleWhite Voir le message
    Note: Je préfère programmer que faire de l'UML ... car j'étais nul en ce dernier pendant les cours (et bon ... je préfère dans tout les cas sauté sur le code )
    UML est simple. UML n'est pas un process - c'est un langage graphique. Il suffit d'apprendre le sens des diagrammes et la sémantique des symboles pour faire de l'UML et lire de l'UML couramment. Si tu es programmeur, tu ne peux pas être nul à ça.

    Par contre, tu peux avoir des problèmes sur le process à mettre en oeuvre pour créer un projet en s'aidant de diagramme UML. Ca, c'est la méthode (qui n'a rien à voir avec UML, UML se contentant de proposer une représentation du résultat de la phase d'analyse prescrite par la méthode).

    La méthode que j'utilise fait référence à un type de diagramme UML rarement enseigné : le diagramme de robustesse. Celui-ci permet de dériver des comportements et des instances à partir des cas d'utilisation. Du coup, il devient nettement plus aisé de définir un diagramme de classe pendant l'analyse (avec un problème : si on y prends pas garde, on se retrouve vite avec un design ontologique, ce qui me gène beaucoup).

    Citation Envoyé par LittleWhite Voir le message
    Il faut aussi savoir que vous avez souvent critiquer mon choix sur la SDL, mais, sachez aussi que c'est la bibliothèque qui me permettait le plus de plateforme en sorti, tout cela, sans nécessairement faire mon NEngine2 :p Donc, mon choix était justifié est totalement possible. Juste que l'intégration de Qt aurait été un peu plus dure (pour l'intégrateur) mais .... d'un coté je pouvais ne pas m'en soucier (et de toute façon, l'intégration sera toujours aussi dur car Qt est une bibliothèque trop différente de SFML / SDL.
    Je ne me permettrais pas de critiquer un choix Pour des raisons qui ont un lien avec ma propre philosophie (en fait, mon expérience et mes désirs ; rien à voir avec la philosophie), je n'aurais pas fait le même.

    (en même temps, je suis en traind em'arracher les cheveux avec du code X11...)

    Citation Envoyé par LittleWhite Voir le message
    Je parlais aussi un peu de conception en général ... vu qu'il est très souvent nécessaire de revenir sur la conception à cause des contraintes du langage et des plateformes ...

    Bon ... aller ... espérons que j'ai mon NEngine2 de fini pour ce soir (alors qu'il n'est pas commencé )
    Je te le souhaite !

    Citation Envoyé par LittleWhite Voir le message
    Il n'y a pas de mal à critiquer (et je ne vous visez pas (c'était un vous de pluriel ). C'est juste que dans la page 6 de ce fil de discussion vous avez dit que la SDL pouvait être lourde et mal adapter (alors que pour moi, je pensais que c'était la plus adapté).

    Lors de la conception basique au début du jeu, j'avais fait en sorte que l'on puisse implémenté des moteur de rendu différent de ce de la SDL mais ce n'était que pour le rendu. Là, nous sommes parti sur un objectif un peu différent (que je ne critique pas vraiment vu qu'il y a des pour et contre pour les deux).

    Maintenant, personnellement, il est très rare que je pense à Qt pour faire un jeu ... car la bibliothèque est très lourde (taille, fonctionnalité) et n'est pas spécialisé pour les jeux (et ce n'est pas son objectif premier). Après, elle peut très bien le faire ...
    Ce n'est pas grave : Qt propose les fonctionnalités de base pour créer une fenêtre native sur un OS donné. C'est tout ce qu'on lui demande dans le domaine du jeu vidéo

    Citation Envoyé par LittleWhite Voir le message
    <snip>

    Maintenant, j'ai une bonne nouvelle (je crois). J'ai mis en place mon deuxième design du NEngine. Pour l'instant j'ai eu quelques conflits avec la classe Sprite du jeu (car j'ai une classe du même nom) mais comme la classe Sprite (et surement l'AnimatedSprite) vont être modifié par la suite, le problème est juste de courte durée.
    Félicitations !

    Citation Envoyé par LittleWhite Voir le message
    Par contre, je suis sur que j'ai fais des erreurs de conception, notamment lorsque je dois utilisé le mot clé 'friend' (qui j'imagine est un mot clé comme beaucoup, qui existe mais pour l'utilisé correctement il faut être un Guru)
    Non pas. Il y a deux utilisations fréquentes de friend : tu souhaite offrir des services dans une classe, mais tu ne souhaites pas que l'utilisateur y ait accès. Tu déclares en friend la classe qui va pouvoir y accéder. Un exemple (que j'ai moi même mis en place, et dont j'ai déjà parlé sur ce forum il y a quelques mois) :

    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
     
     
    class device;
     
    class resource
    {
    private:
      void tie(native_device nd) { ... }
      friend class device;
    public:
      resource(...) { ... }
      virtual ~resource() { ... }
      ...
    };
     
    class device
    {
    public:
      void register(resource* r) { r->tie(this->m_native_device; }
    };
    Dans une encapsulation de D3D (c'est pour le context ; en fait, on s'en moque).L'utilisateur enregistre une instance de resource auprès du device, mais n'autorise pas l'utilisateur à appeler la méthide tie() et donc à manipuler une instance de native_device.

    Le second cas est similaire : il s'agit de gérer la construction d'une instance, et de l'autoriser uniquement à partir d'un certain nombre de poinst connus d'avance. Par exemple (c'est aussi (vaguement) extrait de mon code récent)

    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
     
    namespace xml
    {
      class attribute
      {
      private:
        attribute(xmlAttrPtr ptr) { ... } // xmlAttrPtr est un type de libxml2. Je ne souhaite pas mettre ce type en interface. 
        friend class node;
      public:
        ...
      };
     
      class node
      {
      public:
        attribute first_attribute() const { return attribute(m_ptr->attribute); }
      };
    }
    (mon code est un peu plus complexe que ça, pour une question d'itérateurs, ...).

    Citation Envoyé par LittleWhite Voir le message
    De plus, j'utilise des cast à la C (car ceux à la C++ ne veulent pas passer (honte à moi)) pour pouvoir donner des accès suffisant de la classe Renderer aux classes Sprite et Window.
    Il est vrai que je voyais une méthode pour éviter ceci, mais cela aller donné accès au pointeur natifs des bibliothèques, à l'utilisateur ... ce qui était vraiment non voulu.
    Ils devraient !

    un cast C est exactement similaire à reinterpret_cast<>(). Mais la plupart du temps, ce que tu souhaites faire, c'est un static_cast<>() - ce qui nécessite la déclaration des constructeurs qui vont bien. Autre solution, utiliser des fonctions libres qui font exactement ce que tu souhaites faire.

    Pour info, voici un code qui fait (très vaguement) ce que font les casts principaux (hors const_cast et dynamic_cast)

    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
     
    template <class Out, class In>
    Out my_static_cast(const In& in)
    {
      return Out(In);
    }
     
    template <class Out, class In>
    Out& my_reinterpret_cast(In& in)
    {
      return *(Out*)(&in);
    }
    Comme tu peut le voir, le reinterpret_cast est très violent

    Voilà, je crois que j'arrive au bout de mes remarques. Sur mon éditeur de texte (parce que vu la longueur, c'était nécessaire d'utiliser un éditeur de texte) je suis à 966 lignes - sachant que ça ne compte que les sauts de ligne, et non pas le nombre de caractères par ligne. wc -c me renvoie 70,0000 caractères, ce que (je pense) est un bon score
    [FAQ des forums][FAQ Développement 2D, 3D et Jeux][Si vous ne savez pas ou vous en êtes...]
    Essayez d'écrire clairement (c'est à dire avec des mots français complets). SMS est votre ennemi.
    Evitez les arguments inutiles - DirectMachin vs. OpenTruc ou G++ vs. Café. C'est dépassé tout ça.
    Et si vous êtes sages, vous aurez peut être vous aussi la chance de passer à la télé. Ou pas.

    Ce site contient un forum d'entraide gratuit. Il ne s'use que si l'on ne s'en sert pas.

  4. #144
    Inactif  


    Homme Profil pro
    Inscrit en
    Novembre 2008
    Messages
    5 288
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Âge : 48
    Localisation : France, Rhône (Rhône Alpes)

    Informations professionnelles :
    Secteur : Santé

    Informations forums :
    Inscription : Novembre 2008
    Messages : 5 288
    Points : 15 620
    Points
    15 620
    Par défaut
    Je savais même pas qu'il y avait une limite dans la taille des messages

    J'étais impatient de lire ton avis. Je ne suis pas déçu

    EDIT :
    J'ai un problème avec cette solution : c'est de la réutilisation de code par fainéantise. Etant donné qu'il y a peu de chance pour que tu passes de l'un à l'autre (que tu affecte une taille de fenête à une position), je préconise l'utilisation de deux types différents et non compatible - concrètement, deux classes : size<> et vec2<>. Si tu a besoin de passer de l'un a l'autre, deux fonctions de conversions explicites feront l'affaire (size_to_vec, vec_to_size ; on pourrait me demander "mais pourquoi ne pas prévoir des constructeurs dans chaque classe pour faire la correspondance ?", à quoi je répondrais "mais parce que tu n'as pas envie que la conversion puisse se faire de manière implicite ").
    C'est effectivement ce que l'on trouve dans Qt (et d'autres libs mais je connais moins) avec QPoint et QSize.

    Ce n'est surtout pas le rôle d'un moteur. D'ailleurs, il faudra un jour s'entendre sur ce qu'est un moteur. En fait, pour réaliser une architecture propre, il est indispensable de savoir ce que signifie chacun des termes qu'on emploie. La sémantique rejoint l'architecture un peu trop souvent pour que ça soit une simple coincidence.
    Question de sémantique aussi : qu'est-ce qu'un gestionnaire ? Si c'est une collection de resources chargées, alors c'est un collection. Si c'est le système qui permet de créer les resources, alors c'est une factory. Si le gestionnaire gère la durée de vie des ressources, alors c'est encore autre chose. En tout cas, et selon ce que je professe régulièrement ici, ça ne peut pas être les trois ensemble
    Il va falloir que j'écrive un article d'opinion sur ce sujet spécifique. Il y a beaucoup à dire, et beaucoup à proposer. Je rève de voir le jour où les gestionnaires serotn tout aussi décrié que ne l'est le singleton

    Sinon, bonnes remarques sur la gestion des responsabilités (mais pourquoi alors parler de gestionnaire, hein ? )
    J'aurais du commencé par là, je dois préciser un petit point : en général, pour moi, les "moteur", "gestionnaire" et autres ne sont pas des classes mais un ensemble de classe (souvent dans un namespace) ayant une certaine cohérence dans leurs finalités. Et donc un "gestionnaire" pourra être constitué de classes factory, collection, etc. chacun ayant une responsabilité unique (en gros, le concept de "gestionnaire" n’apparaît pas vraiment dans le code, à part peut être au niveau de la hiérarchie des fichiers )

    Pas d'accord avec toi : un sprite n'est pas un sprite animé. Conceptuellement, c'est la même différence qu'entre un film et une image. On voit que les deux n'ont pas le même comportement, et forcer l'utilisation d'une interface unique pour gérer les deux cas va forcer les objets à s'intéresser à leur contenu pour effectuer des décisions qui sont basée sur le type de ce contenu. Du coup, c'est une violation d'OCP.
    Mon idée était que un sprite animé affiche une série d'image à chaque update. Un sprite non animé est la même chose, mais avec une liste d'image contenant qu'une seule image. La seule différence est donc lors de la création du sprite, le "gestionnaire" (je sais que tu aimes le terme ) charge une ou plusieurs images en fonction du type de sprite à créer donné par le créateur du sprite.
    Si on a deux classes distinctes, cela veut dire que quelque part dans le code, on indique que tel type correspond à un sprite animé et tel autre type est un sprite non animé. Et si un dessinateur dessine une animation pour un sprite, il faudra modifier le code quelque part. Or il me semble pas mal de pourvoir changer ça sans modifier le code (par exemple fournissant juste les nouvelles images et un fichier texte)

  5. #145
    Responsable 2D/3D/Jeux


    Avatar de LittleWhite
    Homme Profil pro
    Ingénieur développement logiciels
    Inscrit en
    Mai 2008
    Messages
    26 859
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Localisation : France

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

    Informations forums :
    Inscription : Mai 2008
    Messages : 26 859
    Points : 218 579
    Points
    218 579
    Billets dans le blog
    120
    Par défaut
    Merci de vous être penché sur mon problème

    Citation Envoyé par Emmanuel Deloget
    Attention : plus long, tu meurs (je vise l'award "post le plus long de l'année" de DVP.com).
    Lors de la remise de prix, si vous pouviez énoncer mon nom pour vous avoir aidé

    Il faut savoir tout de même que (et j'en suis désolé si cela va paraître comme une perte de temps) mais la moitié de vos critiques se font sur une première version qui est plutôt nulle de mon NEngine.
    Certes (tout de même) la moitié est bien réutilisable sur ma deuxième version.

    Donc, j'ose rappeler que je n'ai plus de Singleton, ni de classe barbare qui présente tout en static public.

    Citation Envoyé par Emmanuel Deloget
    Il serait plus judicieux selon moi de faire une fonction factory : en fonction des paramètres et de ce que permet la plateforme, la bonne classe est instanciée. Pas besoin de singleton pour ça (c'est même une assez mauvaise chose).
    Est-ce une factory, comme le design pattern du même nom ? ou juste une fonction qui retourne ce que l'on veut. Car là, dans votre formulation, je ne vois pas trop.

    Evidemment, je vais me pencher sur le PIMPL et les Policies dans les jours qui suivent.

    Je précise tout de même, que je souhaite finir mon implémentation actuelle, et que du coup, même si je regarde les design patterns conseillé, je risque de ne rien faire en conséquences (à moins que j'ai l'idée lumière) (je l'avais dit ). De plus, vous me le conseillez:
    Citation Envoyé par Emmanuel Deloget
    Je comprends tout à fait. Il faut savori qu'avec nous, tu t'aventures sur un terrain miné. Nous n'allons pas nous contenter de te dire c'est bien. Nous allons te proposer des améliorations (et expliquer en quoi ce sont des améliorations). Le problème est que si tu nous écoutes, tu es toujours sur ton moteur dans 10 ans. Il faut savoir dire stop, c'est à dire enregistrer nos recommandations, et les appliquer sur un prochain projet - parce que le plus important, c'est quand même ton projet, pas ce qu'on dit
    Citation Envoyé par Emmanuel Deloget
    Pourquoi un booléen isOpenGL ? N'est-il pas préférable de créer d'abord une fenêtre, puis de lui affecter un contexte OpenGL ? (je n'ai jamais aimé SDL pour ça).
    Tout autant que moi, vous savez que la SDL (de manière simple) doit être informé que le fenêtre est une fenêtre OpenGL ... Du coup ... un boolean était présent.
    Dans la version 2 du NEngine et grâce aux commentaires de gbdivers, ceci a été supprimé (C'est du ressort de l'implémenteur de la surcharge de Window et du Renderer aussi.

    Citation Envoyé par Emmanuel Deloget
    Pour une architecture claire, il est indispensable de chercher à réduire le couplage entre les classes - non pas de manière artificielle, mais de manière naturelle. Pour ça, il faut bien préciser les responsabilités de chaque classe (et respecter le principe de responsabilité unique).
    D'ailleurs, par rapport à vos commentaires je me rends compte que mon Renderer et ma Window (et la classe Sprite aussi) sont encore beaucoup trop lié (ce que renseigne le mot clé friend).

    Citation Envoyé par Emmanuel Deloget
    Bonne chose la documentation !
    Je la mets très souvent à jour, mais je doute que ce soit vraiment utile ... et que cela m'apporte grand chose. Mais quoi qu'il en soit, je continu

    Citation Envoyé par Emmanuel Deloget
    SDL propose la notion de surface : tu dessines sur une surface, pas sur une fenêtre. C'est à mon sens un pas dans la bonne direction.
    Ouep et peut être que la SDL est la seule à avoir un seul type pour deux choses différentes (la SFML ne le faisant pas sf::RenderWindow). Ce qui me laise penser que séparé les deux reste une bonne chose.

    Citation Envoyé par Emmanuel Deloget
    A tu vraiment besoin de la fenêtre partout ?

    Mon idée est que non. Lorsque tu souhaite dessiner, tu as besoin d'un drawable quelqconque (ça peut être la fenêtre, soyons large d'esprit). Mais en a tu besoin pour déplacer un sprite ? Pour vérifier qu'un missile touche sa cible ?
    Je m'exprime certainement un peu mal. La fonction qui déplace (update()) n'aura jamais d'accès à cette Window. (cela me semble tellement idiot :o)
    Ce que je disais, c'est que dans la classequi intègre la Game Loop, j'avais accès à la Window. Et que pareille pour mon Renderer, il était présent pour toute fonction voulant dessiner. Je trouvais cela un peu exagéré, mais après avoir lu votre traduction sur la Singletonite, on voit qu'il faut bien faire comme cela.

    Citation Envoyé par Emmanuel Deloget
    Citation Envoyé par LittleWhite
    Pour réponse à toutes vos questions (du moins j'essaie) ...:

    Je fais ce jeu pour le plaisir, aucune contrainte, aucune obligation, c'est un jeu que j'ai commencé en fin de vacances d'été, et que je continue dès que j'ai un peu de temps libre (ce qui recommence à me manquer à cause de l'université )

    Je suis étudiant, certes, mais je suis étudiant en programmation de jeux vidéos.
    Ou ça ?
    University of Teesside (Note: dans ma signature, il y a assez de lien pour le trouver )

    Citation Envoyé par Emmanuel Deloget
    C'est à la fois vrai et à la fois faux. Vrai, parce que (pour bien connaitre l'industrie en question) l'architecture logicielle a toujours été un non problème. Le syndrome NIH a toujours été très fort dans cette industrie. Il a commencé à disparaitre avec la montée en puissance des cartes vidéos.
    NIH c'est un nouveau nom de virus de grippe ? (ou alors le bruit que je fais lorsque je ne comprends pas )
    (Pouvez vous me donner la signification de 'NIH' ? S'il vous plait)

    Citation Envoyé par Emmanuel Deloget
    Scinde le moteur en deux parties : une partie générique, et une partie spécifique à la plateforme (dans l'exemple des policy, ça devient simple, parce que la partie générique est principalement composée de fichiers header contenant des templates). De cette façon, utiliser une nouvelle plateforme signifie recompiler le code de la plateforme et linker le tout avec le code générique et le code du jeu.
    Avant même que vous ne le dites, j'en étais avec cette manière de procéder (la partie générique et la partie spécifique)

    Citation Envoyé par Emmanuel Deloget
    createWindow ? A quoi pourrait donc servir ce constructeur... (même reflexion pour le destroy).
    Oui et non... Je suis d'accord avec vous, mais je veux savoir si une erreur existe ... donc ... pour suivre votre conseille, je vais devoir recourir aux exceptions ... ce qui n'était pas vraiment ma volonté

    Citation Envoyé par Emmanuel Deloget
    Pour plus tard, tu pourrais avoir besoin d'encapsuler ça pour gérer des combinaisons de touche. Par exemple, la flexhe droite permet d'aller à droite. Il est plus sympa d'avoir un sytème te permettant de savoir ce que tu dois faire plutot qu'un système disant ce que fait l'utilisateur. Mais je le répète, c'est suffisant pour l'instant.
    Les combinaisons de touche sont gérés (les diagonales) et pour les boutons, on peut faire des tests bits à bits.

    Oh! et bien sur, je vais suivre le conseille sur la surcharge du cast (si besoin).

    Citation Envoyé par gbdivers
    Je savais même pas qu'il y avait une limite dans la taille des messages
    Moi non plus

    Citation Envoyé par gbdivers
    Mon idée était que un sprite animé affiche une série d'image à chaque update. Un sprite non animé est la même chose, mais avec une liste d'image contenant qu'une seule image. La seule différence est donc lors de la création du sprite, le "gestionnaire" (je sais que tu aimes le terme ) charge une ou plusieurs images en fonction du type de sprite à créer donné par le créateur du sprite.
    Si on a deux classes distinctes, cela veut dire que quelque part dans le code, on indique que tel type correspond à un sprite animé et tel autre type est un sprite non animé. Et si un dessinateur dessine une animation pour un sprite, il faudra modifier le code quelque part. Or il me semble pas mal de pourvoir changer ça sans modifier le code (par exemple fournissant juste les nouvelles images et un fichier texte)
    Et pour moi, un fichier (image) contient plusieurs images. Et que le sprite animé permet juste de changer le rectangle source de l'image pour faire une animation :p

    Finallement, j'ai mis à jour les sources en ajoutant le namespace pour le NEngine (comme je l'avais dit)
    Vous souhaitez participer à la rubrique 2D/3D/Jeux ? Contactez-moi

    Ma page sur DVP
    Mon Portfolio

    Qui connaît l'erreur, connaît la solution.

  6. #146
    Expert confirmé

    Homme Profil pro
    Développeur informatique
    Inscrit en
    Septembre 2007
    Messages
    1 895
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Âge : 47
    Localisation : France, Bouches du Rhône (Provence Alpes Côte d'Azur)

    Informations professionnelles :
    Activité : Développeur informatique
    Secteur : High Tech - Opérateur de télécommunications

    Informations forums :
    Inscription : Septembre 2007
    Messages : 1 895
    Points : 4 551
    Points
    4 551
    Par défaut
    Je me suis rendu compte effectivement que les premiers commentaires concernent la première version, et qu'ils étaient plus ou moins caduc. Ceci dit, j'en avais écrit une telle tartine que je n'ai pas eu le courage de supprimer tout ça (question d'égo certainement )

    Est-ce une factory, comme le design pattern du même nom ? ou juste une fonction qui retourne ce que l'on veut. Car là, dans votre formulation, je ne vois pas trop.
    Une fonction fera l'affaire dans ce cas.

    Tout autant que moi, vous savez que la SDL (de manière simple) doit être informé que le fenêtre est une fenêtre OpenGL ... Du coup ... un boolean était présent.
    La présence de ton booléen est toute expliquée - mais pas celle de SDL Leur choix est un peu bâtard : ils ont une fonction de création de fenêtre qui fait deux choses différentes (c'est pour ça que je n'aime pas ; il aura mieux valut avoir une fonction de création de fenêtre, puis une fonction attach_opengl_context() ou quelque chose comme ça). Mais ça ne te concerne pas vraiment, vu que c'est une critique gratuite de SDL

    Ouep et peut être que la SDL est la seule à avoir un seul type pour deux choses différentes
    Non

    Dans Windows : HWND et HDC
    Dans X11 : Window et Drawable

    SDL se base donc sur un concept pre-existant. Laurent a simplifié SFML pour que cette distinction ne soit plus nécessaire.

    NIH c'est un nouveau nom de virus de grippe ?
    Ce qui est sûr, c'est que c'est une maladie encore très répandue. On a du passer récemment sous le seuil épidémique, mais le problème existe toujours. NIH veut dire Not Invented Here. Les symptomes, c'est par exemple le programmeur qui recode sa librairie de gestion de chaines en C++ parce qu'il n'a pas confiance dans celle proposée par la librairie standard, tout en admettant ne pas avoir testé celle-ci. Dans les années 1995-2005, il était presque impensable au développeurs de jeu vidéo d'acheter un middleware (moteur de jeu, physique, son,...). Comme si un développeur de programmes de gestion développait son propre SGBD SQL à chaque fois qu'il en avait besoin

    Oui et non... Je suis d'accord avec vous, mais je veux savoir si une erreur existe ... donc ... pour suivre votre conseille, je vais devoir recourir aux exceptions ... ce qui n'était pas vraiment ma volonté
    En même temps, est-ce que tu vas faire quelque chose de super puissant, genre faire croire à l'OS qu'il y a quand même une fenêtre quand tu n'as pas pu la créer ?

    C'est une erreur dure - et une qui ne devrait jamais se produire en fonctionnement normal. Que le constructeur renvoie une exception ne me parait pas aberrant

    Je savais même pas qu'il y avait une limite dans la taille des messages
    Moi non plus
    Moi non plus. C'était une surprise
    [FAQ des forums][FAQ Développement 2D, 3D et Jeux][Si vous ne savez pas ou vous en êtes...]
    Essayez d'écrire clairement (c'est à dire avec des mots français complets). SMS est votre ennemi.
    Evitez les arguments inutiles - DirectMachin vs. OpenTruc ou G++ vs. Café. C'est dépassé tout ça.
    Et si vous êtes sages, vous aurez peut être vous aussi la chance de passer à la télé. Ou pas.

    Ce site contient un forum d'entraide gratuit. Il ne s'use que si l'on ne s'en sert pas.

  7. #147
    Responsable 2D/3D/Jeux


    Avatar de LittleWhite
    Homme Profil pro
    Ingénieur développement logiciels
    Inscrit en
    Mai 2008
    Messages
    26 859
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Localisation : France

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

    Informations forums :
    Inscription : Mai 2008
    Messages : 26 859
    Points : 218 579
    Points
    218 579
    Billets dans le blog
    120
    Par défaut
    Bonjour,

    Je suis enfin de retour pour donner des nouvelles (et pour vous dire que j'ai enfin un aménagement de l'emploi de temps, qui me permet de travailler plus ou moins régulièrement sur le projet).

    Donc, dans les nouveautés du jour, c'est:
    - La version Linux a un Makefile correct \o/ (je l'avais un peu délaissé durant le refactoring)
    - La documentation est en ordre (doxygen ne reporte pas de warning )
    - Le SpriteLoader fait un chargement efficace des données (notamment, il ne charge pas deux fois le même fichier)

    Et c'est de ce dernier point que nous allons parler tout de suite.
    Je sais bien que le chargement efficace est une chose que je faisait déjà, mais pour cela, j'utilisais une classe spécialement pour cela, qui était le SpriteManager.
    Depuis le refactoring, j'ai du avoir un SpriteLoader, afin d'appliquer un minimum le principe de responsabilité unique, et que ce SpriteLoader est une interface pour le chargement par la bibliothèque native.
    Le must, pour moi, était d'avoir le SpriteManager (donc chargement efficace des Sprite) directement dans le SpriteLoader (je ne pense pas que je casse la responsabilité unique en faisant cela, car le SpriteLoader est le seul à pouvoir géré s'il doit charger ou non les données ).
    Le petit "tour de magie" c'est qu'il fallait que je fasse cela dans mon interface, sans demander à chaque implémentation spécifique d'utiliser une banque de Sprite.
    Et je l'ai fait, en gardant la spécificté de l'interface (une méthode virtuelle pure définissant une fonction qui charge le sprite), celle ci est placé en protected (héritable, mais pas visible de l'exterieur).
    Et pour remplacer la nécessité d'avoir une fonction visible (pour faire le chargement) je déclare une fonction public, qui apelera la fonction protégée.
    Comme il y a des risques de confusion, cela donne la déclaration suivante:

    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
     
    namespace NE
     
    {
     
    	class Sprite;
     
     
     
    	class SpriteLoader
     
    	{
     
        private:
     
     
     
            std::map<std::string, Sprite*> spritesBank;  /*!< Bank saving the Sprite loaded */
     
     
     
        protected:
     
            virtual Sprite* loadSprite(const std::string& fileName)=0;
     
     
     
    	public:
     
    		virtual ~SpriteLoader(void);
     
     
     
    		Sprite* loadSpriteFromFile(const std::string& fileName);
     
    	};
     
    }
    Donc, loadSpriteFontFile() appelle loadSprite (qui fait le chargement spécifique à la bibliothèque utilisé) et qui garde le résultat ou pas, dans la banque de Sprite (comme toujours, une table faisant le lien entre le nom et le Sprite).

    Voici le code:
    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
     
    NE::SpriteLoader :: ~SpriteLoader(void)
    {
        for( std::map<std::string, NE::Sprite*>::const_iterator itSprite = spritesBank.begin() ; itSprite != spritesBank.end() ; ++itSprite )
    	{
    		delete (itSprite->second);
    	}
    	spritesBank.clear();
    }
     
    NE::Sprite* NE::SpriteLoader :: loadSpriteFromFile(const std::string& fileName)
    {
        if ( spritesBank.find(fileName) == spritesBank.end() ) // Not found
        {
     
            Sprite* pSprite = loadSprite(fileName);
            if ( pSprite == NULL )
            {
                LError << "NE::SpriteLoader (Fail to load the Sprite ('" << fileName << "')";
                return NULL;
            }
     
            spritesBank[fileName] = pSprite;
            return pSprite;
        }
        else
        {
            return spritesBank[fileName];
        }
     
    }
    De cette manière, la chargement optimale des Sprite arrivera quelque soit l'implémentation spécifique aux bibliothèque (et c'est gagné)

    (Note: Vous remarquerez que mes changements sont plus petits par contre )
    Vous souhaitez participer à la rubrique 2D/3D/Jeux ? Contactez-moi

    Ma page sur DVP
    Mon Portfolio

    Qui connaît l'erreur, connaît la solution.

  8. #148
    Expert éminent sénior

    Avatar de fearyourself
    Homme Profil pro
    Ingénieur Informaticien Senior
    Inscrit en
    Décembre 2005
    Messages
    5 121
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Âge : 43
    Localisation : Etats-Unis

    Informations professionnelles :
    Activité : Ingénieur Informaticien Senior
    Secteur : Industrie

    Informations forums :
    Inscription : Décembre 2005
    Messages : 5 121
    Points : 11 877
    Points
    11 877
    Par défaut
    Excellent, oui en effet on peut faire cela et c'est pas mal du tout. Un jour, je prendrais le temps et j'enléverai les Manager de mon code aussi. On verra quand j'ai le temps et la motivation pour faire cela ;-)

    encore !
    Jc

  9. #149
    Responsable 2D/3D/Jeux


    Avatar de LittleWhite
    Homme Profil pro
    Ingénieur développement logiciels
    Inscrit en
    Mai 2008
    Messages
    26 859
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Localisation : France

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

    Informations forums :
    Inscription : Mai 2008
    Messages : 26 859
    Points : 218 579
    Points
    218 579
    Billets dans le blog
    120
    Par défaut
    Bonjour,

    Cette fois, mon jeu utilise directement (et entièrement) le SpriteLoader et le Sprite du NEngine. Du coup, j'ai enlevé les anciens fichiers pour le Sprite / SpriteManager.

    Cela apporte (malheureusement) un bug, car je n'utilise pas encore le Scaler lors de ce chargement (il faut que je trouve une joli façon de l'intégré dans l'engine).

    L'AnimatedSprite n'est plus un héritage du Sprite (car le Sprite est maintenant une partie du NEngine). Cette fois l'AnimatedSprite à un pointeur sur le Sprite à utiliser (cela doit vous rappeler une idée de gbdivers sur laquelle je n'étais pas d'accord)
    Pourquoi un tel changement de point de vue, simplement parce que comme je charge les Sprite du SpriteLoader (et que celui ci retourne directement un Sprite utilisable) je ne pouvais plus utiliser l'héritage (car il m'aurait fallu construire moi même un Sprite, mais celui ci est une interface à la classe native ... ) Je ne pouvais pas faire l'héritage sur une interface.

    Sinon, j'ai ajouté un SpriteFactory qui construit des Sprite à partir d'une couleur et d'une taille. Celle ci est bien entendu une interface qui doit être réimplémenté pour chaque bibliothèque.
    Il faut faire attention, car ce n'est pas une factory, le design pattern (lequel je pourrais utilisé en fait, afin de crée des sprites à partir de fichier, ou à partir de couleur ...).
    Mais bon, pour deux cas différents, c'est un peu lourd.

    Donc, aujourd'hui ... la version en ligne est complètement cassé mais elle compile et ne crash pas
    Vous souhaitez participer à la rubrique 2D/3D/Jeux ? Contactez-moi

    Ma page sur DVP
    Mon Portfolio

    Qui connaît l'erreur, connaît la solution.

  10. #150
    Responsable 2D/3D/Jeux


    Avatar de LittleWhite
    Homme Profil pro
    Ingénieur développement logiciels
    Inscrit en
    Mai 2008
    Messages
    26 859
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Localisation : France

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

    Informations forums :
    Inscription : Mai 2008
    Messages : 26 859
    Points : 218 579
    Points
    218 579
    Billets dans le blog
    120
    Par défaut
    Bon, désolé du "retard" ... mais après avoir passé deux jours à essayer d'intégré le Scaler dans le NEngine (le code est là, mais il ne marche pas pour une raison assez ... inconnu ) J'ai décidé d'enlever ce scaler.
    Attention, je ne l'enlève pas à cause de l'erreur (j'avais pris un algo de la SDL_GFX (qui partira donc) et j'ai essayer un truc à moi ... mais je ne vois pas mon bug (crash dès que j'accède le pointeur de la SDL_Surface). Bref rien de grave ... il faudrait juste un peu plus de débugguage.
    Je vais l'enlever, car cette histoire de Scaler (qui n'était pas une trop mauvaise idée) en est une pour la simplicité de l'application, et son design. Effectivement, pour intégré le Scaler dans le NEngine, je le faisais utilisé par le SPriteLoader (qui avait donc un pointeur sur le Scaler) afin de redimensionner les surfaces au chargement. Ici, cela va presque.
    Mais le problème, c'est dans tout le reste du jeu, ou il faut prévoir un affichage plus ou moins dynamique selon le redimensionnement. Ce qui devenait une vrai plaie.

    Le Scaler peut facilement devenir superflu si on demande à l'artiste de faire le redimensionnement des Sprites lui même. Les avantages sont:
    - Le redimensionnement est fait par un algo spécialisé (celui de The GIMP ).
    - Le redimensionnement n'est plus dans le code
    - Plus besoin de s'embêter à géré un redimensionnement dans le code

    Voilà.
    La mise à jour (enlever le Scaler) ce fera dans les jours suivant et après, on attaque le refactoring du jeu \o/
    Vous souhaitez participer à la rubrique 2D/3D/Jeux ? Contactez-moi

    Ma page sur DVP
    Mon Portfolio

    Qui connaît l'erreur, connaît la solution.

  11. #151
    Responsable 2D/3D/Jeux


    Avatar de LittleWhite
    Homme Profil pro
    Ingénieur développement logiciels
    Inscrit en
    Mai 2008
    Messages
    26 859
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Localisation : France

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

    Informations forums :
    Inscription : Mai 2008
    Messages : 26 859
    Points : 218 579
    Points
    218 579
    Billets dans le blog
    120
    Par défaut
    Voilà, j'ai enlevé le Scaler du code \o/
    Bien sur, les images (et la fenêtre) sont plus petites à l'écran, mais c'est plus nette / propre
    Il faudra que je rajoute une commande pour changer la taille de la carte vue par la camera
    Vous souhaitez participer à la rubrique 2D/3D/Jeux ? Contactez-moi

    Ma page sur DVP
    Mon Portfolio

    Qui connaît l'erreur, connaît la solution.

  12. #152
    Responsable 2D/3D/Jeux


    Avatar de LittleWhite
    Homme Profil pro
    Ingénieur développement logiciels
    Inscrit en
    Mai 2008
    Messages
    26 859
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Localisation : France

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

    Informations forums :
    Inscription : Mai 2008
    Messages : 26 859
    Points : 218 579
    Points
    218 579
    Billets dans le blog
    120
    Par défaut
    Nouvelle mis à jour:

    En fait, j'ai utilisé le nouveau NEngine pour le projet noté à l'université. J'en suis encore dans l'intégration (bien que tout le NEngine soit porter), car j'avais un InputManager, plus un SoundManager venant de ce projet.

    Du coup, l'InputManager a été porté pour OpenAWars \o/
    Maintenant, il n'y a plus qu'a enregistrer des Input (classe abstraite) dans le InputManager, et on peut avoir un résultat fusionné entre les tout les Input
    Certes cela restreint. Et puis cela ne marcherai pas si on avait deux controlleurs mais bon, le but n'est pas là.
    Sinon, le NE::Engine a été renommer NE::NEngine depuis que les autotools avait des problèmes avec des fichiers de même noms (et classe) mais pas de le même dossier (il me semble )

    Demain (ou plus tard :p), on verra a utiliser tout le NEngine dans OpenAWars, et a supprimé toute la SDL dans OpenAWars (coté jeu). Ensuite, le son \o/
    Vous souhaitez participer à la rubrique 2D/3D/Jeux ? Contactez-moi

    Ma page sur DVP
    Mon Portfolio

    Qui connaît l'erreur, connaît la solution.

  13. #153
    Responsable 2D/3D/Jeux


    Avatar de LittleWhite
    Homme Profil pro
    Ingénieur développement logiciels
    Inscrit en
    Mai 2008
    Messages
    26 859
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Localisation : France

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

    Informations forums :
    Inscription : Mai 2008
    Messages : 26 859
    Points : 218 579
    Points
    218 579
    Billets dans le blog
    120
    Par défaut
    Enfin une (petite) mis à jour.
    Maintenant, les contrôles utilisé viennent tous du NEngine (suppression de l'ancien système)
    Ce NEngine permet des accès uniformisé pour plusieurs contrôle. Par la même occasion, j'ai rajouté une exception, pour signaler qu'un Input n'existait (cas du Joystick sur mon PC )
    Vous souhaitez participer à la rubrique 2D/3D/Jeux ? Contactez-moi

    Ma page sur DVP
    Mon Portfolio

    Qui connaît l'erreur, connaît la solution.

  14. #154
    Responsable 2D/3D/Jeux


    Avatar de LittleWhite
    Homme Profil pro
    Ingénieur développement logiciels
    Inscrit en
    Mai 2008
    Messages
    26 859
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Localisation : France

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

    Informations forums :
    Inscription : Mai 2008
    Messages : 26 859
    Points : 218 579
    Points
    218 579
    Billets dans le blog
    120
    Par défaut
    Bonsoir,

    Petite mis à jour du projet, pour enlever des références à la SDL qui sont devenues inutiles.
    Notamment, le VTime utilise maintenant un pointeur sur un temps natif (celui d'une bibliothèque donc).

    TODO:
    - Des Bitmap font (police sur fichier bitmap)
    Vous souhaitez participer à la rubrique 2D/3D/Jeux ? Contactez-moi

    Ma page sur DVP
    Mon Portfolio

    Qui connaît l'erreur, connaît la solution.

  15. #155
    Responsable 2D/3D/Jeux


    Avatar de LittleWhite
    Homme Profil pro
    Ingénieur développement logiciels
    Inscrit en
    Mai 2008
    Messages
    26 859
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Localisation : France

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

    Informations forums :
    Inscription : Mai 2008
    Messages : 26 859
    Points : 218 579
    Points
    218 579
    Billets dans le blog
    120
    Par défaut
    Encore une mise à jour.

    Cette fois, j'ai rajouté les Bitmap Font, qui sont des polices basées sur des sprites. Cela me permet de supprimer le FontManager, qui est complètement remplacer par le SpriteManager.
    Notez que les Font ne vont pas dans le NEngine, mais utilise les Sprite du NEngine.
    J'ai corrigé un petit bug avec les controlleurs (Input) (lors d'une mauvaise création du Joystick pour SDL).

    Et j'ai mis à jour la documentation.

    Il faut aussi savoir que pour les cours (j'utilise mon NEngine aussi pour mes cours) j'ai rajouté la gestion du son, des threads, et des sémaphores. Du coup, je vais ramener ces ajouts dans le NEngine pour OAW. On remarquera que je suis capable de développer plusieurs points et de les regrouper à la fin
    Après, je commence la refonte de l'architecture du jeu \o/ Enfin nous y arrivons (euh ... surtout moi :p)
    Vous souhaitez participer à la rubrique 2D/3D/Jeux ? Contactez-moi

    Ma page sur DVP
    Mon Portfolio

    Qui connaît l'erreur, connaît la solution.

  16. #156
    Expert éminent sénior

    Avatar de fearyourself
    Homme Profil pro
    Ingénieur Informaticien Senior
    Inscrit en
    Décembre 2005
    Messages
    5 121
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Âge : 43
    Localisation : Etats-Unis

    Informations professionnelles :
    Activité : Ingénieur Informaticien Senior
    Secteur : Industrie

    Informations forums :
    Inscription : Décembre 2005
    Messages : 5 121
    Points : 11 877
    Points
    11 877
    Par défaut
    Excellent boulot, du coup, tu bosses beaucoup sur le fond et l'arrière plan du jeu et moins sur les nouvelles features :-)

    Pour quand un nouveau screenshot ?
    :-)

    Jc

  17. #157
    Responsable 2D/3D/Jeux


    Avatar de LittleWhite
    Homme Profil pro
    Ingénieur développement logiciels
    Inscrit en
    Mai 2008
    Messages
    26 859
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Localisation : France

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

    Informations forums :
    Inscription : Mai 2008
    Messages : 26 859
    Points : 218 579
    Points
    218 579
    Billets dans le blog
    120
    Par défaut
    Citation Envoyé par fearyourself Voir le message
    Excellent boulot, du coup, tu bosses beaucoup sur le fond et l'arrière plan du jeu et moins sur les nouvelles features :-)

    Pour quand un nouveau screenshot ?
    :-)

    Jc
    Et oui ... je bosse sur le fond ... et bientôt un peu moins au fond ... mais j'essaie d'avoir un truc assez clean pour que je puisse avancer sur le jeu
    Vous souhaitez participer à la rubrique 2D/3D/Jeux ? Contactez-moi

    Ma page sur DVP
    Mon Portfolio

    Qui connaît l'erreur, connaît la solution.

  18. #158
    Membre expérimenté
    Homme Profil pro
    Développeur
    Inscrit en
    Juillet 2009
    Messages
    416
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Localisation : France, Hauts de Seine (Île de France)

    Informations professionnelles :
    Activité : Développeur

    Informations forums :
    Inscription : Juillet 2009
    Messages : 416
    Points : 1 443
    Points
    1 443
    Par défaut
    la gestion [...] des métaphores
    ...?

    Ainsi que la gestion des allitérations, de la mise en abyme, du moi et du sur-moi?

    Ou c'est un vrai concept que je ne connais pas?

  19. #159
    Responsable 2D/3D/Jeux


    Avatar de LittleWhite
    Homme Profil pro
    Ingénieur développement logiciels
    Inscrit en
    Mai 2008
    Messages
    26 859
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Localisation : France

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

    Informations forums :
    Inscription : Mai 2008
    Messages : 26 859
    Points : 218 579
    Points
    218 579
    Billets dans le blog
    120
    Par défaut
    Citation Envoyé par Guntha Voir le message
    ...?

    Ainsi que la gestion des allitérations, de la mise en abyme, du moi et du sur-moi?

    Ou c'est un vrai concept que je ne connais pas?
    Je voulais dire ... 'semaphores' ...
    Vous souhaitez participer à la rubrique 2D/3D/Jeux ? Contactez-moi

    Ma page sur DVP
    Mon Portfolio

    Qui connaît l'erreur, connaît la solution.

  20. #160
    Responsable 2D/3D/Jeux


    Avatar de LittleWhite
    Homme Profil pro
    Ingénieur développement logiciels
    Inscrit en
    Mai 2008
    Messages
    26 859
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Localisation : France

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

    Informations forums :
    Inscription : Mai 2008
    Messages : 26 859
    Points : 218 579
    Points
    218 579
    Billets dans le blog
    120
    Par défaut
    Petite mise à jour visant a enlever les références à la SDL dans les fichiers main.
    Premièrement, j'ai enlevé toutes les accès à SDL_TTF que je n'utilise plus du tout \o/
    Deuxièmement, j'ai enlevé l'initialisation de SDL_image qui était des les main. Maintenant, la SDL_image est chargé dans une nouvelle implémentation du SpriteLoader.

    L'ancienne va rester sur le SVN, mais va être utilisé comme chargeur de BMP (code que j'ai pour mon université).

    Voilà, là, je crois que je suis tout à fait près à continuer le jeu en lui même (enfin son architecture) et non plus celle du moteur \o/
    Vous souhaitez participer à la rubrique 2D/3D/Jeux ? Contactez-moi

    Ma page sur DVP
    Mon Portfolio

    Qui connaît l'erreur, connaît la solution.

Discussions similaires

  1. [Projet en cours] Strategy(nom provisoire) - Advance wars like
    Par TiranusKBX dans le forum Projets
    Réponses: 17
    Dernier message: 29/09/2016, 15h46
  2. [VB6] [ADO] Like sur base Access
    Par dlpxlid dans le forum VB 6 et antérieur
    Réponses: 9
    Dernier message: 24/01/2003, 11h03
  3. Créer un interpréteur de langage inspiré du Basic
    Par Picasso dans le forum Algorithmes et structures de données
    Réponses: 4
    Dernier message: 11/05/2002, 17h10

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