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

C++ Discussion :

[C++] Utilisation du pattern strategie (ou pont)


Sujet :

C++

  1. #21
    Membre Expert Avatar de DonQuiche
    Inscrit en
    Septembre 2010
    Messages
    2 741
    Détails du profil
    Informations forums :
    Inscription : Septembre 2010
    Messages : 2 741
    Par défaut
    Citation Envoyé par screetch Voir le message
    ne prend pas mal ce que j'ai dit
    Je te rassure, ce n'est pas le cas, ta critique était légitime et j'ai simplement tenu à m'expliquer sur au moins un des points.

  2. #22
    Membre Expert
    Avatar de Aspic
    Homme Profil pro
    Étudiant
    Inscrit en
    Août 2005
    Messages
    3 905
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Localisation : France, Hauts de Seine (Île de France)

    Informations professionnelles :
    Activité : Étudiant
    Secteur : High Tech - Électronique et micro-électronique

    Informations forums :
    Inscription : Août 2005
    Messages : 3 905
    Par défaut
    Citation Envoyé par DonQuiche Voir le message
    Avant toute chose, nous semblons avoir un problème de sémantique, tu comprends de travers la notion de détention des responsabilités dans le contexte de conception objet. Ainsi, quand tu nous as montré ce code où World parcourt les entités en appelant pour chacune d'elles "update", tu nous as dit que c'était World qui avait donc la responsabilité de mettre à jour la position des entités. C'est faux : World demande aux entités de se mettre à jour mais ce sont bien elles-mêmes qui décident quoi changer et que faire des informations fournies (ou peut-être délèguent-elles à une autre classe). Dans ton exemple, c'est donc bien Entity qui possède cette responsabilité et non World. World a au mieux la responsabilité de requérir des entités qu'elles se mettent à jour. Ce qui n'a rien d'anormal, tout ça est correct. Le rôle de chef d'orchestre est de toute façon inévitable puisqu'il faut bien que la pile des appels ait une racine, le tout est que cela soit fait avec un faible couplage et ça ne veut pas dire pour autant que le chef d'orchestre a toutes les responsabilités du monde.

    De même, tu nous as dit que Entity avait pour responsabilité le rendu. C'est faux là aussi, Entity ne fait que spécifier la position de chaque sprite. En somme, Entity ne fait que spécifier ce qui sera rendu et où mais les détails du rendu (batching, appel des API graphiques, etc) sont gérés par autre chose, peut-être sprite ou peut-être une autre classe.
    Tu as peut être raison
    En fait je crois que je commence à comprendre cette histoire de sémantique de responsabilités :
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
     
    bool World::update(int direction, int buttons)
    {
        bool bOk = true;
     
        std::vector<IEntity*>::const_iterator it;
        for (it = entities.begin(); it != entities.end(); ++it)
        {
            bOk &= (*it)->move(direction);
            bOk &= (*it)->update(direction, buttons);
        }
     
        return bOk;
    }
    Quand je fais ca dans la classe World, pour moi c'est World qui avait la responsabilité de déplacer et de mettre à jour les animations. Apparemment c'est faux

    Donc je dirais que en fait vu que la méthode move de Entity appelle la méthode onMove du IMoveBehaviour:
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
     
    ....
    IMoveBehaviour* pMoveBehaviour; // pointeur sur son type de déplacement
    ....
    bool Entity::move(int directions)
            {
                return pMoveBehaviour->onMove(*pSprite, directions);
            }
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    class IMoveBehaviour
    {
        public:
            virtual bool onMove(Core::ISprite& sprite, int directions) = 0;
    };
    Je suppose que c'est donc IMoveBehaviour qui a la responsabilité de déplacer les Entités ? Est ce correct ou pas ?

    De même pour le dessin dans la classe World :
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    bool World::draw(const Core::IRenderer& r)
    {
        bool bOk = true;
     
        std::vector<IEntity*>::const_iterator it;
        for (it = entities.begin(); it != entities.end(); ++it)
        {
            bOk &= (*it)->draw(r);
        }
     
        return bOk;
    }
    On fait appel à la méthode draw() de Entity :
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    bool Entity::draw(const Core::IRenderer& r)
            {
                return pSprite->draw(r);
            }
    Et cette méthode fait aussi appelle à la méthode draw() du sprite qui fait elle même appelle à la méthode drawSurface() du Renderer.

    Donc au final, c'est bien Renderer qui est responsable de l'affichaga des Entity ?
    Qui ne tente rien n'a rien !
    Ce qui ne nous tue pas nous rends plus fort !!
    Mon projet ZELDA en C++/Allegro
    http://www.tutoworld.com - Le Forum -
    Mes ressources Dotnet (cours, sources, tutos)
    --------------------------------------------
    + + =

    Ne pas oublier le Tag !

  3. #23
    Membre éclairé
    Homme Profil pro
    Développeur informatique
    Inscrit en
    Février 2008
    Messages
    308
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Âge : 37
    Localisation : France

    Informations professionnelles :
    Activité : Développeur informatique
    Secteur : Finance

    Informations forums :
    Inscription : Février 2008
    Messages : 308
    Par défaut
    Yop,

    En ayant juste lu rapidement de fil de discussion, tu te prend la tête pour des truc simple.

    Tu as une classe World, qui contient toutes tes entités (d'une façon ou d'un autre)
    Tes entités peuvent être de plusieurs types, mais ils ont tous un point commun, ils peuvent etre mis a jour et affiché

    Donc, ton "world", lui se contente de parcourir tes entités, et de les mètre a jour l'un après l'autre avec "update", puis les affiché avec "draw"

    Pour les déplacements, gère ça directement dans ta classe,
    imaginons que player hérite de unit qui herite de entity, tu surcharge "update" dans player et tu code ton comportement "si flèche gauche et pas collision alors je me déplace a gauche"
    pour Enemy, tu surcharge "update" et tu code ton comportement "si cible a droite et pas collision alors je me déplace a droite"

    Pour le dessin, pareil, tu gères ça dans ta classe
    un "enemy" a un nom affiché au dessus de sa tete ? bah tu surcharge "draw" pour afficher le nom en plus de ce que fait déjà de "draw" de "entity"
    un "enemy_quifaittresmal" qui hérite de "enemy" bah lui c'est pareil sauf que en plus de dessiner le nom, tu lui dessine une tête de mort a coté du nom

    Après a toi de décider ce qui fait d'une classe qu'elle mérite d’être une classe (player est controllé par le clavier) et ce qui peut être un simple paramètre (unité se déplace a une vitesse x, pour player x=10, pour enemy x=8)

  4. #24
    Membre Expert
    Avatar de Aspic
    Homme Profil pro
    Étudiant
    Inscrit en
    Août 2005
    Messages
    3 905
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Localisation : France, Hauts de Seine (Île de France)

    Informations professionnelles :
    Activité : Étudiant
    Secteur : High Tech - Électronique et micro-électronique

    Informations forums :
    Inscription : Août 2005
    Messages : 3 905
    Par défaut
    Citation Envoyé par bebaijhi Voir le message
    Tu as une classe World, qui contient toutes tes entités (d'une façon ou d'un autre)
    Tes entités peuvent être de plusieurs types, mais ils ont tous un point commun, ils peuvent etre mis a jour et affiché

    Donc, ton "world", lui se contente de parcourir tes entités, et de les mètre a jour l'un après l'autre avec "update", puis les affiché avec "draw"

    Pour les déplacements, gère ça directement dans ta classe,
    imaginons que player hérite de unit qui herite de entity, tu surcharge "update" dans player et tu code ton comportement "si flèche gauche et pas collision alors je me déplace a gauche"
    pour Enemy, tu surcharge "update" et tu code ton comportement "si cible a droite et pas collision alors je me déplace a droite"

    Pour le dessin, pareil, tu gères ça dans ta classe
    un "enemy" a un nom affiché au dessus de sa tete ? bah tu surcharge "draw" pour afficher le nom en plus de ce que fait déjà de "draw" de "entity"
    un "enemy_quifaittresmal" qui hérite de "enemy" bah lui c'est pareil sauf que en plus de dessiner le nom, tu lui dessine une tête de mort a coté du nom

    Après a toi de décider ce qui fait d'une classe qu'elle mérite d’être une classe (player est controllé par le clavier) et ce qui peut être un simple paramètre (unité se déplace a une vitesse x, pour player x=10, pour enemy x=8)
    Salut,
    Le problème n'est pas là... ca j'ai bien compris comment utiliser un simple polymorphisme

    Par contre, j'aimerais bien savoir si j'ai bien compris cette histoire de responsabilité (cf mon post précédent)

    En attendant, je vais continuer à étudier le code de Koala01
    Qui ne tente rien n'a rien !
    Ce qui ne nous tue pas nous rends plus fort !!
    Mon projet ZELDA en C++/Allegro
    http://www.tutoworld.com - Le Forum -
    Mes ressources Dotnet (cours, sources, tutos)
    --------------------------------------------
    + + =

    Ne pas oublier le Tag !

  5. #25
    Membre Expert Avatar de DonQuiche
    Inscrit en
    Septembre 2010
    Messages
    2 741
    Détails du profil
    Informations forums :
    Inscription : Septembre 2010
    Messages : 2 741
    Par défaut
    Bonjour Aspic.

    Sur le début, tu as tout à fait raison, c'est bien MoveBehaviour qui détient dans ce cas la responsabilité du déplacement. Sur le draw, c'est un peu moins net et on pourrait ergoter mais cela reviendrait à débattre du nombre d'anges sur une tête d'épingle. Pour ma part je m'y serais pris un peu différemment mais peu importe.

    Par conte, sur le movebehaviour, il y a quand même une chose assez laide : tu passe à toutes tes entités l'état du contrôleur alors que seul le joueur va avoir besoin de ces informations, c'est assez tordu. Les diverses propositions qui ont été faîtes pour éviter cela (utiliser des membres statiques pour exposer certaines informations ou passer une instance d'une classe contenant des références vers tous les services importants) valent à mon avis un coup d'oeil.

  6. #26
    Membre Expert
    Avatar de Aspic
    Homme Profil pro
    Étudiant
    Inscrit en
    Août 2005
    Messages
    3 905
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Localisation : France, Hauts de Seine (Île de France)

    Informations professionnelles :
    Activité : Étudiant
    Secteur : High Tech - Électronique et micro-électronique

    Informations forums :
    Inscription : Août 2005
    Messages : 3 905
    Par défaut
    Citation Envoyé par DonQuiche Voir le message
    Par conte, sur le movebehaviour, il y a quand même une chose assez laide : tu passe à toutes tes entités l'état du contrôleur alors que seul le joueur va avoir besoin de ces informations, c'est assez tordu. Les diverses propositions qui ont été faîtes pour éviter cela (utiliser des membres statiques pour exposer certaines informations ou passer une instance d'une classe contenant des références vers tous les services importants) valent à mon avis un coup d'oeil.
    Utiliser des membres statiques c'est comme des variables globales et pour moi ce n'est pas mieux du tout
    Utiliser une instance d'une classe contenant tout ce qu'il faut revient à faire une God Class et la passer partout dans les fonctions, c'est pas top non plus

    Mais effectivement, tu as raison la direction n'est utilisée que par Le PlayerBehaviour et pas les autres, comment résoudre ce problème ? Je n'en ai aucune idée

    Autre chose, je dois gérer le Scroll (changement d'écran comme dans un zelda classique), pendez vous qu'il faut faire une classe à part et y mettre une référence dans ma classe World ? Ou alors directement gérer dans World (mais ce n'est pas son rôle je pense...) ...
    Qui ne tente rien n'a rien !
    Ce qui ne nous tue pas nous rends plus fort !!
    Mon projet ZELDA en C++/Allegro
    http://www.tutoworld.com - Le Forum -
    Mes ressources Dotnet (cours, sources, tutos)
    --------------------------------------------
    + + =

    Ne pas oublier le Tag !

  7. #27
    Expert éminent
    Avatar de koala01
    Homme Profil pro
    aucun
    Inscrit en
    Octobre 2004
    Messages
    11 644
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Âge : 53
    Localisation : Belgique

    Informations professionnelles :
    Activité : aucun

    Informations forums :
    Inscription : Octobre 2004
    Messages : 11 644
    Par défaut
    Au fait, concernant le MoveBehaviour...

    Certains types d'entités (en fait, celles qui sont déplaçables dans mon exemple) ont effectivement un comportement de déplacement...

    Mais il faut être clair : non seulement, c'est une relation AS UN (donc, nous sommes plus proche de la composition ou de l'aggrégation que de l'héritage, mais, en plus, ce n'est pas le cas de n'importe quelle entité...

    Il n'y a en effet aucune raison pour qu'un élément (immobile) du décors n'aie besoin d'un quelconque comportement de déplacement

    Par contre, on peut parfaitement envisager le fait qu'il puisse exister différents type de comportement de déplacement !

    L'exemple simple est qu'un ennemi va se déplacer sur base des décisions de l'IA (par exemple, en utilisant l'algorithme A* ), alors que le joueur va se déplacer sur base des boutons ou touches enfoncées par celui qui est derrière son écran ou qu'un sprite se déplacera "en droite ligne" selon une vitesse et une direction donnée

    Nous pourrions donc parfaitement avoir une hiérarchie de comportement de mouvement proche de
    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
    class MoveBehaviour /* c'est une classe de base qui n'hérite de rien d'autre ;) */
    {
        public:
            virtual ~MoveBehaviour(){}
            /* on sait que l'on doit permettre de calculer la prochaine position */
            virtual computeNextPosition(Coordinate const & ) = 0;
            /* et qu'on doit pouvoir appliquer le déplacement à l'objet */
            void updatePosition(MovableEntity & entity)
            {
                 entity.moveTo(nexPos_);
            }
       protected:
           void setNextPos(Coordinate const & toset){nextPos_=toset;}
       private:
           Coordinate nextPos_;
    };
    /* par exemple, pour le mouvement des spries */
    class SpriteMovingBehaviour : public MoveBehaviour
    {
        public:
            SpriteMovingBehaviour(int time, int speed, double angle): time_(time), speed_(speed),
            angle_(angle){}
            virtual void computeNextCoordinate(Coordinate const &ActualPosion)
            {
                Coordinate temp =
                /* calcule de la position suivante par rapport à AcutalPosition,
                 * en fonction de la vitesse et de l'angle indiquant la direction ;)
                 */
               setNextPosition(temp);
            }
    };
    /* d'autres classes héritées de MoveBehaviour, adaptées pour le joueur,
     * les ennemis et les éléments de décors mobiles ;)
     */
    La classe MovableEntity sera donc sans doute intelligemment modifiée en
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    class MovableEntity
    {
        public:
            MovableEntity(MoveBehaviour * behav,int xpos, int ypos, bool visible =false):Entity(xpos, ypos, visible), move_(behav){}
            void computeNextPosition()
            {
                 move_->computeNextPosition(Coordinate(xpos(),ypos());
            }
            /* comme précédemment :D */
        private:
            MoveBehaviour * move_;
    };
    et ce sont les classes qui dérivent de MovableEntity qui fournissent le pointeur vers le comportement adapté:
    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
    class Sprite : public MovableEntity
    {
        public: 
            Sprite(int xpos, int ypos, bool visible =false):MovableEntity(new SpriteMovingBehaviour,xpos, ypos, visible){}
    };
    class Ennemy : public MovableEntity
    {
        public: 
            Ennemy(int xpos, int ypos, bool visible =false):MovableEntity(new EnnemyMovingBehaviour,xpos, ypos, visible){}
    };
    class Gamer : public MovableEntity
    {
        public: 
            Gamer(int xpos, int ypos, bool visible =false):MovableEntity(new GamerMovingBehaviour,xpos, ypos, visible){}
    };
    Il semble en effet "logique" de se dire que l'utilisation du comportement qui consiste à se déplacer soit effectuée par... l'entité qui est susceptible de se déplacer, et ce meme si le comportement de déplacement vient à être adapté par rapport au type réel de l'entité qui doit en profiter

    Si besoin est, tu peux en outre parfaitement regrouper toutes les entités susceptibles de se déplacer dans une seule collection de manière à pouvoir provoquer le calcul de la prochaine position depuis un endroit unique, ce qui participera en outre au respect du sacrosaint principe de la responsabilité unique

    Maintenant, il y a sans doute d'autre comportements, et l'on peut parfaitement les envisager de manière similaires (bien qu'ils n'héritent très certainement pas de MoveBehaviour )
    A méditer: La solution la plus simple est toujours la moins compliquée
    Ce qui se conçoit bien s'énonce clairement, et les mots pour le dire vous viennent aisément. Nicolas Boileau
    Compiler Gcc sous windows avec MinGW
    Coder efficacement en C++ : dans les bacs le 17 février 2014
    mon tout nouveau blog

  8. #28
    Membre Expert Avatar de DonQuiche
    Inscrit en
    Septembre 2010
    Messages
    2 741
    Détails du profil
    Informations forums :
    Inscription : Septembre 2010
    Messages : 2 741
    Par défaut
    Citation Envoyé par Aspic Voir le message
    Utiliser des membres statiques c'est comme des variables globales et pour moi ce n'est pas mieux du tout
    As-tu des arguments à avancer qui s'appliquent au cas présent ou s'agit-il d'une doctrine religieuse ?
    Pardon, je te taquine mais ma question reste plus que pertinente.
    Au passage, variable globale != membre statique. Pas de pollution d'espace des noms, initialisation différente, etc.

    Utiliser une instance d'une classe contenant tout ce qu'il faut revient à faire une God Class et la passer partout dans les fonctions, c'est pas top non plus
    Si la classe en question est extrêmement simple et se contente d'exposer les services, je ne crois vraiment pas que ce soit un problème. D'autant que ton alternative actuelle entraîne un couplage plus fort : si l'on décide de changer la gestion des états du contrôleur, il faut modifier toutes les entités et comportements. Dans les solutions qui t'ont été proposée tu as au pire deux ou trois classes à modifier.

    Autre chose, je dois gérer le Scroll (changement d'écran comme dans un zelda classique), pendez vous qu'il faut faire une classe à part et y mettre une référence dans ma classe World ? Ou alors directement gérer dans World (mais ce n'est pas son rôle je pense...) ...
    Qui a la responsabilité de décider quelles entités doivent être créées ou détruites ? Si tu réponds à cela, tu réponds à ta question.
    Note qu'il te faudra aussi assigner à une classe la responsabilité de lire la map pour en déduire quelles entités s'y trouvent. Au vu du scrolling, ce seront deux responsabilités distinctes.

  9. #29
    Membre Expert
    Avatar de Aspic
    Homme Profil pro
    Étudiant
    Inscrit en
    Août 2005
    Messages
    3 905
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Localisation : France, Hauts de Seine (Île de France)

    Informations professionnelles :
    Activité : Étudiant
    Secteur : High Tech - Électronique et micro-électronique

    Informations forums :
    Inscription : Août 2005
    Messages : 3 905
    Par défaut
    @koaka01 :
    Citation Envoyé par koala01 Voir le message
    Au fait, concernant le MoveBehaviour...

    Certains types d'entités (en fait, celles qui sont déplaçables dans mon exemple) ont effectivement un comportement de déplacement...

    Mais il faut être clair : non seulement, c'est une relation AS UN (donc, nous sommes plus proche de la composition ou de l'aggrégation que de l'héritage, mais, en plus, ce n'est pas le cas de n'importe quelle entité...

    Il n'y a en effet aucune raison pour qu'un élément (immobile) du décors n'aie besoin d'un quelconque comportement de déplacement
    Je suis d'accord avec toi, pas de raison qu'une entité qui ne se déplace pas ait un comportement de déplacement. Le problème est qu'avec ta solution, tu as énormément de classes rien que pour gérer le déplacement avec 3 types de déplacements possibles (Gamer, Enemy et Sprite).
    Imagine pour la gestion des collisions, comment vas tu gérer les collisions entre tous les types d'entités d'un jeu par exemple ?

    Citation Envoyé par koala01 Voir le message
    Par contre, on peut parfaitement envisager le fait qu'il puisse exister différents type de comportement de déplacement !

    L'exemple simple est qu'un ennemi va se déplacer sur base des décisions de l'IA (par exemple, en utilisant l'algorithme A* ), alors que le joueur va se déplacer sur base des boutons ou touches enfoncées par celui qui est derrière son écran ou qu'un sprite se déplacera "en droite ligne" selon une vitesse et une direction donnée

    Nous pourrions donc parfaitement avoir une hiérarchie de comportement de mouvement proche de
    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
    class MoveBehaviour /* c'est une classe de base qui n'hérite de rien d'autre ;) */
    {
        public:
            virtual ~MoveBehaviour(){}
            /* on sait que l'on doit permettre de calculer la prochaine position */
            virtual computeNextPosition(Coordinate const & ) = 0;
            /* et qu'on doit pouvoir appliquer le déplacement à l'objet */
            void updatePosition(MovableEntity & entity)
            {
                 entity.moveTo(nexPos_);
            }
       protected:
           void setNextPos(Coordinate const & toset){nextPos_=toset;}
       private:
           Coordinate nextPos_;
    };
    /* par exemple, pour le mouvement des spries */
    class SpriteMovingBehaviour : public MoveBehaviour
    {
        public:
            SpriteMovingBehaviour(int time, int speed, double angle): time_(time), speed_(speed),
            angle_(angle){}
            virtual void computeNextCoordinate(Coordinate const &ActualPosion)
            {
                Coordinate temp =
                /* calcule de la position suivante par rapport à AcutalPosition,
                 * en fonction de la vitesse et de l'angle indiquant la direction ;)
                 */
               setNextPosition(temp);
            }
    };
    /* d'autres classes héritées de MoveBehaviour, adaptées pour le joueur,
     * les ennemis et les éléments de décors mobiles ;)
     */
    La classe MovableEntity sera donc sans doute intelligemment modifiée en
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    class MovableEntity
    {
        public:
            MovableEntity(MoveBehaviour * behav,int xpos, int ypos, bool visible =false):Entity(xpos, ypos, visible), move_(behav){}
            void computeNextPosition()
            {
                 move_->computeNextPosition(Coordinate(xpos(),ypos());
            }
            /* comme précédemment :D */
        private:
            MoveBehaviour * move_;
    };
    et ce sont les classes qui dérivent de MovableEntity qui fournissent le pointeur vers le comportement adapté:
    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
    class Sprite : public MovableEntity
    {
        public: 
            Sprite(int xpos, int ypos, bool visible =false):MovableEntity(new SpriteMovingBehaviour,xpos, ypos, visible){}
    };
    class Ennemy : public MovableEntity
    {
        public: 
            Ennemy(int xpos, int ypos, bool visible =false):MovableEntity(new EnnemyMovingBehaviour,xpos, ypos, visible){}
    };
    class Gamer : public MovableEntity
    {
        public: 
            Gamer(int xpos, int ypos, bool visible =false):MovableEntity(new GamerMovingBehaviour,xpos, ypos, visible){}
    };
    Il semble en effet "logique" de se dire que l'utilisation du comportement qui consiste à se déplacer soit effectuée par... l'entité qui est susceptible de se déplacer, et ce meme si le comportement de déplacement vient à être adapté par rapport au type réel de l'entité qui doit en profiter
    On a plus ou moins la même vision de la chose, la seule différence est que je considère que toutes les entités peuvent se déplacer. De ce fait pas d'héritage du tout, que de la composition. J'ai modifié un peu mon code, maintenant j'utilise une structure pour définir une entité :
    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
    struct Entity
    {
        Core::ISprite* pSprite; // pointeur sur son sprite
        IVect2D pos; // position courante
        USize2D size; // taille
        Type type; // type d'entité
        bool bVisible; // visibilite
        bool bAlive; // vivante ou pas
     
        // behaviours
        IMoveBehaviour* pMoveBehaviour; // pointeur sur son type de déplacement
        IHealthBehaviour* pHealthBehaviour;
        ICollisionBehaviour* pCollisionBehaviour;
     
    };
    Et donc pour une entité qui n'a pas de déplacement, il suffit de lui coller un NoMoveBehaviour qui ne fera rien, certes je suis d'accord c'est pas très logique ^^ mais au moins pour ajouter un nouveau type de déplacement, il suffit de créer une classe qui dérive de l'interface IMoveBehaviour et de l'implémenter. Pour modifier un déplacement, il suffit de modifier la classe en question.
    Donc je trouve que c'est facilement maintenable non ?



    @DonQuiche :
    Même si les membres statiques sont mieux que des variables globales, pour y accéder on a pas besoin d'objet donc elles sont accessibles partout

    Alors peut être que tu as raison, mettre les touches du clavier/joystick en static est peut être une solution, au moins je n'aurais pas à le trimbaler en arguments partout ^^ mais pour moi, je ne trouve pas que ca respecte les principes OO.

    Si la classe en question est extrêmement simple et se contente d'exposer les services, je ne crois vraiment pas que ce soit un problème. D'autant que ton alternative actuelle entraîne un couplage plus fort : si l'on décide de changer la gestion des états du contrôleur, il faut modifier toutes les entités et comportements. Dans les solutions qui t'ont été proposée tu as au pire deux ou trois classes à modifier.
    La je ne vois pas ce que tu veux dire, as tu un exemple d'une telle classe ?

    Enfin, pour la responsabilité de Scroll, pour moi c'est le World qui est en charge de loader/detruire les entités de la map et de les "gérer" donc le Scroll devrait se trouver dans la classe World.
    Qui ne tente rien n'a rien !
    Ce qui ne nous tue pas nous rends plus fort !!
    Mon projet ZELDA en C++/Allegro
    http://www.tutoworld.com - Le Forum -
    Mes ressources Dotnet (cours, sources, tutos)
    --------------------------------------------
    + + =

    Ne pas oublier le Tag !

  10. #30
    Membre Expert Avatar de DonQuiche
    Inscrit en
    Septembre 2010
    Messages
    2 741
    Détails du profil
    Informations forums :
    Inscription : Septembre 2010
    Messages : 2 741
    Par défaut
    @Aspic
    Plutôt que de directement mettre les états du contrôleur (clavier ou pad) en membre statique, tu peux aussi envisager d'exposer l'instance représentant les états du contrôleur via un membre statique. Par exemple tu pourrais avoir une classe de ce genre, avec des membres statiques, ou des membres d'instance si tu souhaites la passer en argument à chaque update() :

    CStatesAndServices
    * getPlayerEntity() // Nécessaire pour les ennemis qui veulent suivre Link
    * getControlerState() // Nécessaire pour la mise à jour de Link
    * getSoundMixer() // Nécessaire pour jouer un son en réaction à un coup d'épée
    * getInventory() // Nécessaire si Link ramasse un coeur (collisions)
    // J'ai utilisé des méthodes plutôt que des champs mais il y a peu de chances que l'on en ait vraiment besoin.

    Tu pourrais aussi la découper en plusieurs morceaux si tu restes sur l'idée d'une instance à passer à chaque appel de update() : avoir un UpdateArgs, CollisionArgs, etc... Ou mixer le tout pour avoir les services exposés par des membres statiques et les états par des *Args.

    Maintenant, si tu préfères la méthode statique, tu peux aussi fourrer tout ça dans World (ou Game) en sachant que ça n'accroît le couplage que sur le papier puisque cette partie-ci de World changerait rarement. Oui, ça va à l'encontre du SRP mais le SRP est une aide à la conduite, pas un dogme religieux. Parfois on peut s'en affranchir si ça ne crée pas de problème. Et si tu es certain que World (ou Game) existera toujours et que ce sera toujours lui qui contiendra ces services et états, ça ne crée pas de problème.

    Tout ceci étant dit, deux remarques :
    * Si tu souhaites mettre en place des tests unitaires, la version à base de UpdateArgs et CollisionArgs est nettement plus sympathique puisqu'on peut l'adapter aisément pour se donner la possibilité de créer des mocks. Mais la même chose peut être faîte avec les membres statiques en prévoyant des setters.
    * Inversement, l'avantage des membres statiques est que, si jamais tu as besoin à un moment d'un état que jusqu'à présent tu ne relayais pas jusqu'à un niveau si profond dans la pile des appels, tu vas devoir modifier tous tes callers pour ce faire. Mais les *Args compensent cette faiblesse, rendant les modifications à faire moins nombreuses.

    Au final, c'est aussi une question d'esthétisme. Tu sembles par exemple affecter un certain purisme, peut-être parce que tu as ainsi l'impression de ne pas pouvoir faire d'erreurs. Dans tous les cas, toutes ces solutions sont meilleures que ta solution actuelle, laide et peu maintenable.

  11. #31
    Expert éminent
    Avatar de koala01
    Homme Profil pro
    aucun
    Inscrit en
    Octobre 2004
    Messages
    11 644
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Âge : 53
    Localisation : Belgique

    Informations professionnelles :
    Activité : aucun

    Informations forums :
    Inscription : Octobre 2004
    Messages : 11 644
    Par défaut
    Citation Envoyé par Aspic Voir le message
    @koaka01 :

    Je suis d'accord avec toi, pas de raison qu'une entité qui ne se déplace pas ait un comportement de déplacement. Le problème est qu'avec ta solution, tu as énormément de classes rien que pour gérer le déplacement avec 3 types de déplacements possibles (Gamer, Enemy et Sprite).
    Et alors le mouvement et la manière dont il est géré n'a, tout simplement rien à voir avec le reste

    Tu peux très bien considérer le fait que seules les entités qui bougent (MovableEntity dans mon exemple) sont susceptibles d'avoir un déplacement, dont le mode est adapé à leur type réel (ennemy, sprite, gamer, que sais-je )

    "Tout ce qu'il faut", c'est, effectivement, que le comportement de déplacement soit délégué à une classe particulière

    Imagine pour la gestion des collisions, comment vas tu gérer les collisions entre tous les types d'entités d'un jeu par exemple ?
    Bien que tu aies deux type d'entités différents (celles qui bougent et celles qui ne bougent pas), tu n'as que deux cas de collisions : une entité qui bouge avec une entité qui ne bouge pas, et une entité qui bouge avec une entité qui bouge (tu ne verra jamais deux entités qui ne bougent pas entrer en collision )

    Par contre, tu devras de toutes manière mettre en place une certaine "priorité de réaction" en cas de collision, dépendant peut etre du type réel de l'entité, voir de l'entité qui est "active" par rapport à celle qui est "passive" dans la collision
    On a plus ou moins la même vision de la chose, la seule différence est que je considère que toutes les entités peuvent se déplacer. De ce fait pas d'héritage du tout, que de la composition. J'ai modifié un peu mon code, maintenant j'utilise une structure pour définir une entité :
    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
    struct Entity
    {
        Core::ISprite* pSprite; // pointeur sur son sprite
        IVect2D pos; // position courante
        USize2D size; // taille
        Type type; // type d'entité
        bool bVisible; // visibilite
        bool bAlive; // vivante ou pas
     
        // behaviours
        IMoveBehaviour* pMoveBehaviour; // pointeur sur son type de déplacement
        IHealthBehaviour* pHealthBehaviour;
        ICollisionBehaviour* pCollisionBehaviour;
     
    };
    Tu donnes, de nouveau, trop de responsabilité à une entité, lorsque tu ne sais pas exactement de quel type elle est !!!

    Tant que tu ne parles que d'une entité, sans précision, elle ne doit avoir qu'une seule responsabilité (par exemple, celle de fournir les informations permettant de la placer sur ta carte, et/ou celle de s'afficher )

    Ce n'est qu'une fois que tu spécialise ta classe (en MovableEntity ou NonMovableEntity) que tu peux déterminer que l'une est susceptible de se déplacer et l'autre pas...

    Il ne servira donc à rien d'avoir un "NoMoveBehaviour"

    Ton "IMoveBehaviour" sera, effectivement, spécialisé en fonction du type réel de l'entité (qui peut se déplacer), mais c'est ce que nous faisons tous les deux, si ce n'est que je sais d'office qu'il y a tout un pan de la hiérarchie "Entity" qui n'en aura pas besoin
    Et donc pour une entité qui n'a pas de déplacement, il suffit de lui coller un NoMoveBehaviour qui ne fera rien, certes je suis d'accord c'est pas très logique ^^
    Comme tu le dis, ce n'est pas logique

    Dés lors, pourquoi le faire

    Si tu fais la distinction entre les entités qui peuvent bouger et celles qui ne peuvent pas bouger, tu as la certitude que seules les première devront disposer d'un comportement de déplacement adapté
    mais au moins pour ajouter un nouveau type de déplacement, il suffit de créer une classe qui dérive de l'interface IMoveBehaviour et de l'implémenter. Pour modifier un déplacement, il suffit de modifier la classe en question.
    Mais, tu remarqueras que c'est ce que je fais

    La seule différence, c'est que je m'assure qu'un entité ne devant pas se déplacer n'aura en aucun cas besoin d'un comportement de déplacement
    @DonQuiche :
    Même si les membres statiques sont mieux que des variables globales, pour y accéder on a pas besoin d'objet donc elles sont accessibles partout

    Alors peut être que tu as raison, mettre les touches du clavier/joystick en static est peut être une solution, au moins je n'aurais pas à le trimbaler en arguments partout ^^ mais pour moi, je ne trouve pas que ca respecte les principes OO.
    Mais, réfléchis un peu!!!!

    Où vas tu avoir besoin du clavier et du joystick

    Exclusivement au niveau du gestionnaire d'événement du joueur

    De là à dire qu'il peut, d'une manière ou d'une autre, n'exister qu'au travers de l'existence du joueur (ou de son système de gestion d'événements), il n'y a qu'un pas allègrement franchi

    C'est d'autant plus vrai que tu n'en as besoin qu'au moment où tu vas créer l'événement (l'appui sur une touche ou le basculement du joystick dans une direction va provoquer la création d'un événement qui sera "propagé" là où nécessaire )

    Citation Envoyé par DonQuiche Voir le message
    As-tu des arguments à avancer qui s'appliquent au cas présent ou s'agit-il d'une doctrine religieuse ?
    Pardon, je te taquine mais ma question reste plus que pertinente.
    Au passage, variable globale != membre statique. Pas de pollution d'espace des noms, initialisation différente, etc.
    Des arguments, il y en a à la pelle

    Problème de réentrance, de gestion de thread, de "pureté" des fonctions...

    Tu devrais t'intéresser à cette discussion, essentiellement à partir de l'intervention numéro 5, pour éviter d'avoir à tout réécrire
    Si la classe en question est extrêmement simple et se contente d'exposer les services, je ne crois vraiment pas que ce soit un problème. D'autant que ton alternative actuelle entraîne un couplage plus fort : si l'on décide de changer la gestion des états du contrôleur, il faut modifier toutes les entités et comportements. Dans les solutions qui t'ont été proposée tu as au pire deux ou trois classes à modifier.
    Une "GodClass" ne sera jamais simple!!!

    Il est bien plus facile et maintenable de garder des petites classes ayant des responsabilités limitées qu'une seule classes dont on s'attendrait presque à ce qu'elle nous fasse le café, tant elle a de responsabilités
    A méditer: La solution la plus simple est toujours la moins compliquée
    Ce qui se conçoit bien s'énonce clairement, et les mots pour le dire vous viennent aisément. Nicolas Boileau
    Compiler Gcc sous windows avec MinGW
    Coder efficacement en C++ : dans les bacs le 17 février 2014
    mon tout nouveau blog

  12. #32
    Membre Expert Avatar de DonQuiche
    Inscrit en
    Septembre 2010
    Messages
    2 741
    Détails du profil
    Informations forums :
    Inscription : Septembre 2010
    Messages : 2 741
    Par défaut
    Citation Envoyé par koala01 Voir le message
    Mais, réfléchis un peu!!!!
    Ça commence mal...

    * Sur les états du contrôleur :
    Tu pars du principe qu'il va utiliser ton système à base d'événements, ce qui n'est pas dit. Depuis le début je m'efforce de rester à un niveau général quand toi tu développes *ton* implémentation.

    * Sur les variables statiques
    ** Si une instance mutable est partagée par plusieurs threads, elle ne pose ni plus ni moins de problème que des membres statiques.
    ** Code réentrant : voir ci-dessus.
    ** Pureté des fonctions : voir ci-dessus.
    Maintenant si tu prends la peine de regarder ce qui serait partagé, que vois-tu ? Soit des données modifiées une fois et lues par le reste du code (état du contrôleur), soit des instances mutables qui seront partagées (entité représentant Link, service sonore, etc).

    * Sur la GodClass jamais simple :
    Nous ne parlions pas d'une classe regroupant des pelletées de responsabilités. Nous parlions d'une classe ne fournissant aucune service et se contentant d'exposer quelques singletons (pas le pattern, l'unicité) en lecture seule (initialisés au démarrage).

  13. #33
    Membre Expert
    Avatar de Aspic
    Homme Profil pro
    Étudiant
    Inscrit en
    Août 2005
    Messages
    3 905
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Localisation : France, Hauts de Seine (Île de France)

    Informations professionnelles :
    Activité : Étudiant
    Secteur : High Tech - Électronique et micro-électronique

    Informations forums :
    Inscription : Août 2005
    Messages : 3 905
    Par défaut
    Citation Envoyé par koala01 Voir le message
    Bien que tu aies deux type d'entités différents (celles qui bougent et celles qui ne bougent pas), tu n'as que deux cas de collisions : une entité qui bouge avec une entité qui ne bouge pas, et une entité qui bouge avec une entité qui bouge (tu ne verra jamais deux entités qui ne bougent pas entrer en collision )
    Exact vu comme ca
    Citation Envoyé par koala01 Voir le message
    Par contre, tu devras de toutes manière mettre en place une certaine "priorité de réaction" en cas de collision, dépendant peut etre du type réel de l'entité, voir de l'entité qui est "active" par rapport à celle qui est "passive" dans la collision
    Je ne comprends pas très bien.
    Dans le cas de ton implémentation, comment vas tu gérer les collisions de tes deux types d'entités (Movable et NonMovable) ?
    Si tu créés un ICollisionBehaviour et que tu l'associes aux classes MoveEntity et NoMoveEntity, comment vas tu gérer les collisions au cas par cas, c'est à dire :
    • Enemy (Movable) VS Player (Movable) => faudra déclencher un "Hit" sur le Player
    • Enemy (Movable) VS Hole (NoMovable) => Enemy va changer de direction pour éviter le trou (d'ailleurs le déplacement des ennemies est aléatoire dans Zelda pas d'algo de type A* ou autre)
    • Player (Movable) VS Door (NoMovable) => Déclencher une téléportation vers une autre zone de la map

    En fait ton implémentation me plait bien , j'ai juste peur d'être coincé au niveau des collisions entre toutes les entités

    Dernière chose, tu as des entités qui ont un Sprite qui s'anime quand il se déplace (Enemy et Player), d'autres entités qui s'animent indéfiniment (des bougies, des éléments du décor pour rendre la map "vivante"), et d'autre qui sont fixes (une pancarte, une maison...) et d'autres qui s'animent lors d'un événement (ouverture d'une porte par exemple, apparition d'un objet...).
    Comment vas tu gérer ca ? Tu ne vas quand même pas créer des nouveaux types d'entités qui dérivent de Entity ?

    PS : Je crois que le "Ah mais réfléchie un peu !!!" était destiné à moi même

    Merci

    EDIT :

    En essayant d'implémenter la solution de koala01, je me suis rendu compte qu'il y a un petit problème :
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    void computeNextPosition()
            {
                 move_->computeNextPosition(Coordinate(xpos(),ypos());
            }
    La fonction suivante dans la classe MovableEntity ne pourra jamais être appelée car le World manipule un vector de Entity, Or cette fonction est spécialisée à un MovableEntity donc je ne pourrais pas l'appeler à partir de World... (ou alors comme d'hab, j'ai pas compris )

    En clair:
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
    // je crée un player en position (100,100)
    Entity* player = new Gamer(100, 100);
     
    //Je l'ajoute aux entités du monde
    world->addEntity(player);
     
    // et maintenant comment le World va t-il appeler la fonction spécialisée
    // computeNextPosition() de MovableEntity ?
    De même dans ce design, qui est en charge de vérifier si le mouvement est valide ou pas ?
    Qui ne tente rien n'a rien !
    Ce qui ne nous tue pas nous rends plus fort !!
    Mon projet ZELDA en C++/Allegro
    http://www.tutoworld.com - Le Forum -
    Mes ressources Dotnet (cours, sources, tutos)
    --------------------------------------------
    + + =

    Ne pas oublier le Tag !

  14. #34
    Expert éminent
    Avatar de koala01
    Homme Profil pro
    aucun
    Inscrit en
    Octobre 2004
    Messages
    11 644
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Âge : 53
    Localisation : Belgique

    Informations professionnelles :
    Activité : aucun

    Informations forums :
    Inscription : Octobre 2004
    Messages : 11 644
    Par défaut
    Citation Envoyé par DonQuiche Voir le message
    Ça commence mal...
    Le mais réfléchis un peu était adressé à aspic, en fait
    * Sur les états du contrôleur :
    Tu pars du principe qu'il va utiliser ton système à base d'événements, ce qui n'est pas dit. Depuis le début je m'efforce de rester à un niveau général quand toi tu développes *ton* implémentation.
    Je pars surtout du principe qu'il faut absolument faire en sorte de déléguer les responsabilités...

    Il faut, bien sur, prendre les inputs de l'utilisateur en compte, et, le fait que l'utilisateur décide d'appuyer sur la barre d'espace, sur o ou sur la fleche montante est, quoi que tu en dise, un événement

    La seule inconnue, c'est la réaction que le système devra avoir face à cet événement

    Comme on travaille sur une application qui suit une "ligne de temps" ponctuée par des événements, il est logique que l'on se base sur ces événements pour mettre un système de réaction au point

    Maintenant, la hiérarchie "événement" peut clairement prendre un grand nombre de cas en compte tels que inputs utilisateur, collision entre entités, arrivée à un point de scrolling, décision "hors du jeu" de la part de l'utilisateur, timer écoulé ou que sais-je

    Chaque événement contient son propre contexte et seules les classes susceptibles à un type d'événement particulier en tiennent compte

    Ce n'est pas *mon* implémentation (je n'ai d'ailleurs quasiment pas donné de code en ce qui concerne les événement ), c'est juste le moyen le moins mauvais de faire en sorte que tout le monde communique de manière correcte et coordonnée
    * Sur les variables statiques
    ** Si une instance mutable est partagée par plusieurs threads, elle ne pose ni plus ni moins de problème que des membres statiques.
    ** Code réentrant : voir ci-dessus.
    ** Pureté des fonctions : voir ci-dessus.
    Hé bien, il y a cependant une différence flagrante :

    Un membre (ou une instance) mutable ne va agir que sur elle-même ou sur l'objet dont il est membre, alors qu'une variable statique (ou une variable globale) va agir sur toutes les instances qui la manipulent:

    si tu as une classe Machin (disons : Ennemy pour rester dans le cadre de la discussion ) qui dispose d'un membre statique brol, et que tu as deux deux instances de Ennemy (et tu risques vraiment d'en avoir encore plus ), mettons E1 et E2, la modification que tu pourras effectuer sur E1.brol se répercutera immanquablement sur E2.brol, alors que cela ne doit, a priori, pas arriver

    Si brol est, non plus statique, mais mutable, et que tu as deux threads qui utilsent E1, (et deux autres qui utilisent E2), "tout ce qu'il convient de faire", c'est de s'assurer qu'un des thread (manipulant E1 ou E2) prendra le pas sur l'autre, mais les modifications apportées à E1.brol n'influeront que... E1 et "foutra la paix" à E2
    Maintenant si tu prends la peine de regarder ce qui serait partagé, que vois-tu ? Soit des données modifiées une fois et lues par le reste du code (état du contrôleur), soit des instances mutables qui seront partagées (entité représentant Link, service sonore, etc).
    Ce que tu sembles oublier, c'est que ton service sonore peut parfaitement être totalement indépendant de tout le reste!!!

    Tout ce qu'il faut, c'est un moyen de lui faire savoir quels sons jouer et dans quel ordre...

    Encore une fois, un système d'événements semble être la meilleure des solutions pour y parvenir: il reçoit d'office tous les événements et, en fonction de ceux-ci, détermine quel son doit etre joué.

    Il place alors les sons dans une queue d'attente, et joue les sons "un à un"

    Tout ce qu'il faut, encore une fois, c'est que chaque événement se fasse connaitre du service sonore au moment de sa création (et que le service sonore soit en mesure de mettre les événements en attente de gestion dans une file d'attente )
    * Sur la GodClass jamais simple :
    Nous ne parlions pas d'une classe regroupant des pelletées de responsabilités. Nous parlions d'une classe ne fournissant aucune service et se contentant d'exposer quelques singletons (pas le pattern, l'unicité) en lecture seule (initialisés au démarrage).
    Et tu crois que le fait de fournir une pelleté d'instances uniques ne va pas occasionner une pelletée de responsabilité, sans doute

    Si tu délègue correctement les responsabilités, il est tout à fait possible que chaque "module" du programme puisse fonctionner de manière indépendante en n'étant, en gros, piloté qu'en fonction des événements qui surviennent...

    Il n'y a aucune raison que le système de sons soit connu (ou connaisse) le système de détection de collisions, le système d'affichage ou le système de gestion des ennemis.

    Le seul système plus ou moins central qu'il faut avoir est... le système de génération d'événements, et encore, la génération d'événement peut parfaitement etre décentralisée en fonction du type d'événement à générer, car, de toutes façons, l'événement est destiné à être récupéré par... le moteur de jeu de manière à etre transmis à "qui de droit"
    Citation Envoyé par Aspic Voir le message
    Exact vu comme ca

    Je ne comprends pas très bien.
    Dans le cas de ton implémentation, comment vas tu gérer les collisions de tes deux types d'entités (Movable et NonMovable) ?
    Le cas de la collision entre une entité Mobile et une entité immobile est le cas le plus simple à vrai dire : l'entité active (celle qui entre en collision avec l'autre ) est l'entité mobile, alors que l'entité passive (celle qui subit la collision) est l'entité immobile

    Les choses deviennent certainement plus compliquées au niveau des collisions entre entités mobile
    Si tu créés un ICollisionBehaviour et que tu l'associes aux classes MoveEntity et NoMoveEntity, comment vas tu gérer les collisions au cas par cas, c'est à dire :
    • Enemy (Movable) VS Player (Movable) => faudra déclencher un "Hit" sur le Player
    • Enemy (Movable) VS Hole (NoMovable) => Enemy va changer de direction pour éviter le trou (d'ailleurs le déplacement des ennemies est aléatoire dans Zelda pas d'algo de type A* ou autre)
    • Player (Movable) VS Door (NoMovable) => Déclencher une téléportation vers une autre zone de la map

    En fait ton implémentation me plait bien , j'ai juste peur d'être coincé au niveau des collisions entre toutes les entités
    Le fait est qu'une collision provoque un système de réaction de la part des deux intervenants...

    L'entité qui entre en collision avec une autre va réagir à sa manière d'un coté et celle qui subit la collision va réagir à sa manière de l'autre

    Le tout est de savoir qui va prendre la priorité dans l'ordre de la réaction

    Mais, tu prend l'exemple d'une porte, sa réaction en cas de collision sera de forcer l'entité qui est entrée en collision avec elle (si elle existe encore après la collision ) à se déplacer instantanément vers une autre position
    Dernière chose, tu as des entités qui ont un Sprite qui s'anime quand il se déplace (Enemy et Player), d'autres entités qui s'animent indéfiniment (des bougies, des éléments du décor pour rendre la map "vivante"), et d'autre qui sont fixes (une pancarte, une maison...) et d'autres qui s'animent lors d'un événement (ouverture d'une porte par exemple, apparition d'un objet...).
    Comment vas tu gérer ca ? Tu ne vas quand même pas créer des nouveaux types d'entités qui dérivent de Entity ?
    En fait, je crois que l'on ne se comprend pas bien sur ce qui pourrait etre un sprite

    Pour moi, un sprite est, simplement, une entité qui a sa vie propre mais qui n'est ni un élément du décors, ni un objet d'inventaire, ni un ennemi, ni le joueur...

    Un sort lancé par un ennemi ou un joueur ou un objet n'attendant qu'à etre ramassé pourraient, par exemple, etre des sprite

    Le fait qu'une bougie, par exemple, puisse utiliser plusieurs images pour illustrer le mouvement de sa flamme, je veut pas forcément dire que la flamme doive être un sprite : la bougie est, simplement, composées de plusieurs images utilisées dans un ordre donné

    Le fait qu'une porte change d'aspect lorsqu'on l'ouvre n'implique absolument pas que l'on ait besoin d'un sprite pour y arriver : on a simplement deux états (ouverte et fermée) qui correspondent à deux représentations graphiques différentes

    Tu auras d'ailleurs remarqué que la classe Sprite que je fournis hérite, tout simplement, de MovableEntity, et dispose d'informations susceptible de lui permettre d'avoir sa vie propre (vitesse et direction )

    Ce qu'il peut manquer, éventuellement, c'est une information sur le son à jouer à sa création, à sa destruction et durant sa durée de vie

    Il ne s'agirait, en effet, pas de créer une classe sprite par image que tu veux afficher pour celui-ci, il "suffirait" que le sprite dispose de l'ensemble des images qu'il doit faire afficher les unes après les autres

    Par contre, si cela a une utilité quelconque, nous pourrions envisager de spécialiser la classe Sprite en différentes catégories telles que "armes", "aliments" (ou nous trouverions des pommes, des potions ou des bout de gigot ) et "sorts"

    Je ne dis pas que cela doit se faire, hein, je dis juste que, si cela présente un intérêt quelconque, il peut etre utile de l'envisager
    A méditer: La solution la plus simple est toujours la moins compliquée
    Ce qui se conçoit bien s'énonce clairement, et les mots pour le dire vous viennent aisément. Nicolas Boileau
    Compiler Gcc sous windows avec MinGW
    Coder efficacement en C++ : dans les bacs le 17 février 2014
    mon tout nouveau blog

  15. #35
    Membre Expert Avatar de DonQuiche
    Inscrit en
    Septembre 2010
    Messages
    2 741
    Détails du profil
    Informations forums :
    Inscription : Septembre 2010
    Messages : 2 741
    Par défaut
    @koala01
    Le fait que la remarque était adressée à Aspic ne la rend pas moins grossière.

    *Sur les états du contrôleur:
    Laisse-moi résumer : dans ton premier post tu me faisais une remarque qui ne s'appliquait qu'au cas où l'état du joueur serait mis à jour par les événements du contrôleur. Après que j'ai pointé cette restrictions, tu m'expliques donc que, oui, mais bon, de toute façon il faut faire comme ça et que c'est beaucoup plus naturel ainsi. D'abord, ça me fait un peu penser à l'expression "noyer le poisson".

    Maintenant, je reviens tout de même un instant sur cette conception. Quand vas-tu devoir changer l'état de Link ? A chaque collision. A chaque pression des touches du contrôleur. A chaque fois qu'une animation bloquante se termine. Etc, etc, etc... Et à chaque fois tu vas devoir modifier plusieurs états en conséquence : l'animation, le mode d'interaction avec le monde (pendant l'anim qui suit un coup le perso devient invincible), avec le contrôleur (certaines animations ne peuvent pas être interrompues), etc. Qu'est-ce qui te semble le plus simple à écrire, lire et maintenir ? D'exploser cette gestion d'états conflictuels sur cinq ou six événements avec du code redondant (vérifier que les états de plus hautes priorités autorisent l'exécution) et dont on ne sait pas dans quel ordre ils s'exécutent, ou bien d'écrire une procédure unifiée appelé une seule fois par frame ?

    Toujours sur cette conception, il ne faudrait pas confondre les choses : on peut très bien avoir un scheduler basé sur des événements pour ordonner par exemple "exécute tel code dans x millisecondes", ou un système événementiel à un plus haut niveau (UI). Mais il n'y a pas besoin pour autant que le coeur soit basé sur des événements. Personnellement, de mon expérience, ce n'est pas l'architecture la plus aisée pour le coeur du jeu et il est plus simple de mettre chaque entité à jour à chaque frame.



    *Sur les membres statiques:
    Le fait que E1 et E2 soient tous deux affectés par le changement de la variable statique n'est pas un problème, c'est le but recherché par la déclaration en statique (du moins dans le problème que tu exposais, pas dans ce que je décrivais à Aspic : il n'y a dans ce cas aucun problème de ce genre).

    J'insiste : il n'y a aucune différence entre une variable statique et une instance mutable partagée du point de vue de tous les problèmes que tu as énoncés et il serait honnête de le reconnaître. Les variables statiques ne sont pas à prohiber comme tu le pensais, voilà tout.

    Concernant le système sonore, il est bien mutable, point barre. Accessoirement, non, tu vas pas mettre en file les sons. Un son peut soit en écraser un autre, soit se superposer à ceux en cours mais il ne se met pas en file.



    *Sur le regroupement des services:
    Non, qu'une classe contienne une référence vers une instance ne lui donne pas les responsabilités de cette instance. Et il n'a jamais été question que ce système soit fait pour permettre au gestionnaire de collisions d'accéder au système sonore ou quoi que ce soit de ce genre. Je t'invite à relire les échanges successifs.

  16. #36
    Expert éminent
    Avatar de koala01
    Homme Profil pro
    aucun
    Inscrit en
    Octobre 2004
    Messages
    11 644
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Âge : 53
    Localisation : Belgique

    Informations professionnelles :
    Activité : aucun

    Informations forums :
    Inscription : Octobre 2004
    Messages : 11 644
    Par défaut
    Citation Envoyé par DonQuiche Voir le message
    @koala01
    Le fait que la remarque était adressée à Aspic ne la rend pas moins grossière.
    Elle était peut etre un peu rude, mais n'était pas grossière, ou, du moins, ne voulait pas l'être

    Il n'en demeure pas moins qu'il est beaucoup plus cohérent de faire en sorte que tout ce qui correspond à un input issu du joueur soit "centralisé" en un point (ou dans un module particulier), que ce module se charge de traiter l'information ainsi reçue et de transmettre l'information partout, sans qu'il soit besoin, hors du module concerné, de de voir s'intéresser à la touche qui a effectivement été enfoncée.

    Tu n'as donc absolument aucun besoin d'avoir un accès global aux inputs issus du joueur car à la réception de chaque input ne doit correspondre qu'un "signal" donné, qui pourra etre compris comme tel par l'ensemble des "modules" susceptibles d'y réagir...

    Cela permettra en outre, par exemple, au joueur de modifier la touche qui a pour effet de te faire avancer ou d'aller à droite: il n'y a qu'une table de correspondance à mettre à jour entre la touche enfoncée et le "signal" envoyé, et il n'y a meme plus besoin d'avoir accès à la configuration actuelle pour déterminer si le joueur voulait avancer, reculer, sauter ou aller à droite
    *Sur les états du contrôleur:
    Laisse-moi résumer : dans ton premier post tu me faisais une remarque qui ne s'appliquait qu'au cas où l'état du joueur serait mis à jour par les événements du contrôleur. Après que j'ai pointé cette restrictions, tu m'expliques donc que, oui, mais bon, de toute façon il faut faire comme ça et que c'est beaucoup plus naturel ainsi. D'abord, ça me fait un peu penser à l'expression "noyer le poisson".
    Tu as très mal interprété ce que j'ai dit...

    Je te fais un copier coller de ma première intervention pour ce qui concerne le joueur:
    Citation Envoyé par moi meme

    Il t'est tout à fait possible de considérer ton joueur comme étant une entité pour tout ce qui a trait à la gestion "classique" de ton joueur en tant que tel (tout ce qui a trait à l'affichage, va-t-on dire )

    Par contre, tu devras, de toutes manières, gérer ton (tes) joueur(s) comme... des joueurs pour toute une série de situations (tout ce qui a trait à son (leur) déplacement, ses (leurs) actions, sa (leur) "vie dans le monde", ... )

    Il faudra donc, quitte à "enregistrer" ton joueur comme étant une entité à afficher, prévoir toute un système de gestion du joueur en tant que tel!

    Ce système de gestion du joueur s'occupera, entre autres, de la gestion des événements de l'utilisateur, et les différents héritages n'auront sans doute meme pas à intervenir à ce niveau
    Autrement dit :
    1. Par moment, on pourra parfaitement se contenter de considérer le joueur comme étant une entité "quelconque"
    2. A d'autre moments, il faudra considérer le joueur comme étant du type d'entité particulier :joueur, et le fait que joueur hérite (de manière indirecte) de Entité n'interviendra sans doute pas dans la gestion qui en sera faite, car nous ne nous intéresseront exclusivement à ce qui fait que notre entité est un joueur
    3. Parmi tout ce dont le "module" qui s'occupe de la gestion du joueur aura à s'occuper, on trouvera, entre autres, une partie qui s'occupera de gérer les inputs issus du joueurs et de générer les événements qui vont bien en fonction de ces inputs.
    Par la suite, j'ai effectivement introduit la notion de système d'événements, parce que la grosse majorité des réactions se fera sur un dénominateur commun qui est : la survenue d'un événement.

    Je ne parle pas ici uniquement des événements issus de l'appui sur une touche par l'utilisateur!!!

    Je dis que, lorsque deux entités entre en collision, qu' un ennemi décide de frapper quelque chose ou de lancer un sort, qu'un sort touche quelque chose, ou que sais-je, nous observons la survenue d'un événement, et que c'est ce qui jalonne littéralement la ligne de temps de l'application.

    Je dis, enfin, que, dans la liste de tous les événements susceptibles de survenir, il y aura, entre autres provoqués par l'appuis d'une touche de la part de l'utilisateur

    Tu aurais donc une classe de base Event, qui correspond à un événement dont "on ne sait rien", qui sera spécialisé en "CollisionEvent", en "NpcEvent", en "GamerEvent" ou en que sais-je, pour pouvoir déterminer l'origine des événements et qui seront eux meme spécialisées en d'autres événements plus spécifiques de manière à provoquer la réaction appropriées dans les autres modules.

    Le tout en sachant qu'il est tout à fait possible de faire en sorte qu'un module particulier ignore carrément un type particulier d'événement, parce que sans objet pour le module en question (par exemple, le système qui s'occupe de gérer le joueur n'aura sans doute que faire des événements provoqués par les ennemis, mais devra gérer un grand nombre des événements susceptibles de survenir en cas de collision)

    Je suis désolé si cela contredit ton approche, mais il me semble que cela correspond à l'approche "logique" dés le moment où l'on envisage de séparer clairement les responsabilités en créant, au final une série de modules indépendants les un des autres : chaque module se charge de créer les événements qui lui sont propres pour qu'ils soient récupérés par le moteur de jeu et "dispatchés" vers les modules susceptibles d'y réagir
    Maintenant, je reviens tout de même un instant sur cette conception. Quand vas-tu devoir changer l'état de Link ? A chaque collision. A chaque pression des touches du contrôleur. A chaque fois qu'une animation bloquante se termine. Etc, etc, etc... Et à chaque fois tu vas devoir modifier plusieurs états en conséquence : l'animation, le mode d'interaction avec le monde (pendant l'anim qui suit un coup le perso devient invincible), avec le contrôleur (certaines animations ne peuvent pas être interrompues), etc.
    Tu as bien utilisé le terme : état...

    Beaucoup de choses agiront au final comme des "mini machines à états"...

    Pour chaque état donné, il y aura une série d'événement autorisés ou non...

    En Créant une hiérarchie d'événement cohérente et correcte, il te sera parfaitement possible de faire en sorte que la grosse majorité des événements refusés par un état donné soit purement et simplement envoyés vers une fonction "qui ne fait rien" pour n'avoir, au final que quelques événements susceptibles de provoquer une transition vers un nombre limité d'états suivants
    Qu'est-ce qui te semble le plus simple à écrire, lire et maintenir ? D'exploser cette gestion d'états conflictuels sur cinq ou six événements avec du code redondant (vérifier que les états de plus hautes priorités autorisent l'exécution) et dont on ne sait pas dans quel ordre ils s'exécutent,
    en gérant correctement tes états et tes événements, tu n'auras meme pas à t'en inquiéter : chaque état ne pouvant, de toutes manières, permettre la transition vers un nombre limité d'états suivants, cela se fera de manière systématique, et sans qu'il ne soit besoin pour la cause d'avoir du code redondant
    ou bien d'écrire une procédure unifiée appelé une seule fois par frame ?
    Elle ne sera peut etre pas appelée une seule fois par frame, mais la procédure restera unifiée malgré tout...

    Si tu te place au niveau du game engine(qui, rappelons le, récupère tous les événements générés par les différents modules ) cela revient à faire
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    transmitEvent( event)
    update()
    draww()
    avec la fonction transmitEvent qui ressemble à
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
    void GameEngine::transmitEvent(Event * event)
    {
        ennemiesManager_.setNextEvent(event);
        gamerManager_.setNextEvent(event);
        entitiesManager_.setNextEvent(event);
        /* ...
         * tout vers quoi il faut envoyer l'événement ;) */
    }
    la fonction update() qui ressemble à
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
    void GameEngine::update()
    {
        ennemiesManager_.update();
        gamerManager_.update();
        entitiesManager_.update();
        /* ...
         * tout ce qui doit etre mis à jour ;) */
    }
    et la fonction draw() qui ressemble à
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    void GameEngine::draw()
    {
        entitiesManager_.draw();
    }
    Toujours sur cette conception, il ne faudrait pas confondre les choses : on peut très bien avoir un scheduler basé sur des événements pour ordonner par exemple "exécute tel code dans x millisecondes", ou un système événementiel à un plus haut niveau (UI). Mais il n'y a pas besoin pour autant que le coeur soit basé sur des événements.
    Pourquoi pas, étant donné que tout est susceptible de provoquer survenue ou de réagir à la survenue d'un événement, et que c'est quand meme ce qui jalonne la ligne de temps de ton application
    Personnellement, de mon expérience, ce n'est pas l'architecture la plus aisée pour le coeur du jeu et il est plus simple de mettre chaque entité à jour à chaque frame.
    Et tu mets ton entité à jour de quelle manière sur base de l'air du temps sur base d'un comportement qui n'a rien à faire de ce qui a pu se passer à coté d'elle ou sur base des événements qui sont survenus entre la frame précédente et celle que l'on va devoir afficher (et qui ont eu un impact sur elle ou sur sa réaction )


    *Sur les membres statiques:
    Le fait que E1 et E2 soient tous deux affectés par le changement de la variable statique n'est pas un problème, c'est le but recherché par la déclaration en statique (du moins dans le problème que tu exposais, pas dans ce que je décrivais à Aspic : il n'y a dans ce cas aucun problème de ce genre).
    C'est, justement, le problème...

    Tu ne peux pas garantir que, dans un contexte identique, l'appel d'une fonction en passant en paramètre E1 ou E2 aura un comportement identique si un membre est partagé par les deux objets (et risque d'être modifié "par ailleurs"!!!

    Que tu déclares une constante statique passe encore, vu que, par définition, sa valeur ne changera pas!!!

    Mais si tu utilises une variable statique, tu n'a aucun contrôle sur la valeur qu'elle pourra avoir à un instant T pour un objet X, vu que tous les objets de même types sont susceptibles d'y avoir chipoté.
    J'insiste : il n'y a aucune différence entre une variable statique et une instance mutable partagée du point de vue de tous les problèmes que tu as énoncés et il serait honnête de le reconnaître. Les variables statiques ne sont pas à prohiber comme tu le pensais, voilà tout.
    Les constantes statiques ne sont pas à prohiber!!!

    Les variables statiques sont le meilleur moyen de "foutre le bordel" en te retrouvant avec un objet dont le comportement est incohérent du seul fait que tu perds tout contrôle de la valeur de la variable!!!
    Concernant le système sonore, il est bien mutable, point barre.
    Il n'a meme pas à être mutable : il a, purement et simplement, à etre indépendant et à être "piloté" (par envois d'événement) par le Game Engine

    Tu n'as aucun besoin d'y faire appel depuis ailleurs que... depuis le game engine
    Accessoirement, non, tu vas pas mettre en file les sons. Un son peut soit en écraser un autre, soit se superposer à ceux en cours mais il ne se met pas en file.
    ca, je suis d'accord, j'aurais pu etre plus explicte quant à mon explication
    *Sur le regroupement des services:
    Non, qu'une classe contienne une référence vers une instance ne lui donne pas les responsabilités de cette instance. Et il n'a jamais été question que ce système soit fait pour permettre au gestionnaire de collisions d'accéder au système sonore ou quoi que ce soit de ce genre. Je t'invite à relire les échanges successifs.
    Le problème est que, si cette instance se balade partout, elle prend, fatalement, la responsabilité de fournir l'accès aux différents gestionnaires, et que, de ce seul fait, elle fournira n'importe où l'accès à n'importe quel gestionnaire!!!

    De là à se retrouver dans une situation dans laquelle le module qui s'occupe de gérer les collisions fasse directement appel au système de gestion des sons, il n'y a qu'un pas qui sera bien trop allègrement franchi uniquement parce que "il est possible de le faire"!!!

    Pourquoi diable faudrait il passer une instance qui donne accès "à tout" alors qu'on n'a de toutes manières besoin que d'une infime partie de ce à quoi cette instance donne accès
    A méditer: La solution la plus simple est toujours la moins compliquée
    Ce qui se conçoit bien s'énonce clairement, et les mots pour le dire vous viennent aisément. Nicolas Boileau
    Compiler Gcc sous windows avec MinGW
    Coder efficacement en C++ : dans les bacs le 17 février 2014
    mon tout nouveau blog

  17. #37
    Membre Expert
    Avatar de Aspic
    Homme Profil pro
    Étudiant
    Inscrit en
    Août 2005
    Messages
    3 905
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Localisation : France, Hauts de Seine (Île de France)

    Informations professionnelles :
    Activité : Étudiant
    Secteur : High Tech - Électronique et micro-électronique

    Informations forums :
    Inscription : Août 2005
    Messages : 3 905
    Par défaut
    En tout cas moi je ne comprends plus rien du tout
    Tout se disperse et au final tout m'embrouille.
    Je vois un ennemiesManager mais c'est quoi ? D'un coté le World met à jour toutes les entités mais il y a quand même un Manager par type d'entité, je suis paumé sans compter des événements à transmettre à tout le monde

    Je pense qu'un exemple complet serait le bienvenue
    Qui ne tente rien n'a rien !
    Ce qui ne nous tue pas nous rends plus fort !!
    Mon projet ZELDA en C++/Allegro
    http://www.tutoworld.com - Le Forum -
    Mes ressources Dotnet (cours, sources, tutos)
    --------------------------------------------
    + + =

    Ne pas oublier le Tag !

  18. #38
    Expert éminent
    Avatar de koala01
    Homme Profil pro
    aucun
    Inscrit en
    Octobre 2004
    Messages
    11 644
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Âge : 53
    Localisation : Belgique

    Informations professionnelles :
    Activité : aucun

    Informations forums :
    Inscription : Octobre 2004
    Messages : 11 644
    Par défaut
    Bon, reprenons...

    Je te conseille, depuis le départ, de déléguer au maximum les responsabilités en créant ce que j'appelais au départ des "systèmes de gestion", mais dont le terme correct est en fait beaucoup plus proche de "module".

    Tu créerais donc une série de modules gérant le "cycle de vie" des différents types d'entités en les considérant comme tels.

    Cela te donne (cf le code de cette réponse) des modules tels que:
    • un module de gestion des sprites
    • un module de gestion des ennemis
    • un module de gestion des éléments de décors
    • un module de gestion des objets d'inventaire
    • ...
    Chaque module tournant, entre autres, autour d'une classe dont la responsabilité est de garder une trace de toutes les entités du type concernées par le module qui sont créées, entre le moment de leur création et celui de leur destruction.

    Tous ces modules sont "mis en relation" entre eux grâce au game engine qui dispose, d'une (et une seule) instance de cette fameuse classe dont la responsabilité est la gestion des entités d'un type particulier: ce que j'ai nommé (dans ma dernière intervention, mais aussi dans le message auquel j'ai déjà fait référence plus haut ) sous les noms de ennemiesManager_ , gamerManager_ , ou encore entitiesManager_ .


    Les gros avantages de travailler de la sorte sont:
    • De nous permettre, pour chaque entité existante, de disposer d'un moyen de l'utiliser en fonction de son type réel
    • De nous permettre de gérer "à la chaine" l'ensemble des entités d'un type particulier ( comprend : dont le type réel correspond au type d'entité dont un module particulier a la charge)
    • De permettre de travailler sur un type d'entités particulier sans "s'inquiéter du reste"

    Parallèlement à cela, il faut mettre en place un système permettant aux différents modules de réagir les uns par rapport aux autres...

    Le meilleur moyen d'y arriver est de travailler sur les événements qui peuvent survenir: Chaque module va générer un nombre "restreint" d'événement en fonction du type d'entité dont il a la responsabilité.

    Chaque fois qu'un événement sera généré par un module particulier, il devra être "récupéré" par le game engine de manière à être transmis aux autres modules afin de leur donner l'occasion d'y réagir, ou non, selon l'intérêt que l'événement peut avoir pour un module donné.

    Le monde quant à lui interviendra dans un module qui connait l'ensemble des entités existantes, quel que soit leur type réel, mais qui ne peut donc les manipuler que... en tant que Entity et qui réagira essentiellement aux événement susceptibles de provoquer le "scrolling" vers une position donnée de la carte

    En restant dans les modules ou types particuliers, nous avons également le sound player, qui n'a que faire des entités, mais qui réagit exclusivement aux événements (potentiellement à tous les événements), bien qu'ayant (si besoin) également sa propre vie propre (ex: pour la musique de fond), ou le render qui aura pour responsabilité d'afficher l'ensemble des entités considérées par le monde comme étant à afficher

    Si je trouve un peu de courage pour le faire, je te placerai (peut etre) un diagramme de classe et (qui sait ) un diagramme de communication pour te rendre les choses un peu moins abstraites
    A méditer: La solution la plus simple est toujours la moins compliquée
    Ce qui se conçoit bien s'énonce clairement, et les mots pour le dire vous viennent aisément. Nicolas Boileau
    Compiler Gcc sous windows avec MinGW
    Coder efficacement en C++ : dans les bacs le 17 février 2014
    mon tout nouveau blog

  19. #39
    Membre Expert
    Avatar de Aspic
    Homme Profil pro
    Étudiant
    Inscrit en
    Août 2005
    Messages
    3 905
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Localisation : France, Hauts de Seine (Île de France)

    Informations professionnelles :
    Activité : Étudiant
    Secteur : High Tech - Électronique et micro-électronique

    Informations forums :
    Inscription : Août 2005
    Messages : 3 905
    Par défaut
    Oui je crois comprendre ce que tu veux faire mais encore une fois, je n'arrive pas à voir comment les événements vont être transmis par le moteur de jeu aux différents "manager".

    Oui je pense qu'un diagramme de classe serait cool
    Un diagramme de communication, je ne sais pas ce que c'est je connais le diagramme de séquence mais ca doit être assez proche
    Citation Envoyé par koala01 Voir le message
    Pour moi, un sprite est, simplement, une entité qui a sa vie propre mais qui n'est ni un élément du décors, ni un objet d'inventaire, ni un ennemi, ni le joueur...

    Un sort lancé par un ennemi ou un joueur ou un objet n'attendant qu'à etre ramassé pourraient, par exemple, etre des sprite

    ....

    Tu auras d'ailleurs remarqué que la classe Sprite que je fournis hérite, tout simplement, de MovableEntity, et dispose d'informations susceptible de lui permettre d'avoir sa vie propre (vitesse et direction )
    Je reviens sur les sprites, tu as dis que pour toi un sprite c'était par exemple un coeur a ramasser. Or après tu dis que ta classe Sprite dérive de MovableEntity. Or un coeur ne se déplace pas...
    Donc je vois mal ce que tu appelles "Sprites" car pour moi, un sprite est simplement une image qui peut s'animer (plusieurs images) ou pas (une seule image). Une entité est alors composé d'un Sprite, ainsi que d'autres attributs comme la position, la taille, la visibilité...
    Qui ne tente rien n'a rien !
    Ce qui ne nous tue pas nous rends plus fort !!
    Mon projet ZELDA en C++/Allegro
    http://www.tutoworld.com - Le Forum -
    Mes ressources Dotnet (cours, sources, tutos)
    --------------------------------------------
    + + =

    Ne pas oublier le Tag !

  20. #40
    Expert éminent
    Avatar de koala01
    Homme Profil pro
    aucun
    Inscrit en
    Octobre 2004
    Messages
    11 644
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Âge : 53
    Localisation : Belgique

    Informations professionnelles :
    Activité : aucun

    Informations forums :
    Inscription : Octobre 2004
    Messages : 11 644
    Par défaut
    Citation Envoyé par Aspic Voir le message
    Oui je crois comprendre ce que tu veux faire mais encore une fois, je n'arrive pas à voir comment les événements vont être transmis par le moteur de jeu aux différents "manager".
    Le moteur de jeu est, finalement, le seul point où l'ensemble des modules utilisés est connu, et donc d'où il sera possible d'accéder au manager propre à chaque module.

    Or, la plupart des modules vont générer une série d'événements liés aux entités dont ils ont la charge:

    Le module de détection de collisions, par exemple, pourrait générer un événement (dérivé du) type CollisionEvent qui dérive quant à lui (de manière plus ou moins directe) de la classe ancêtre Event.

    Le moteur de jeu va récupérer tous les événements générés (dont certains lui seront d'ailleurs sans doute exclusivement destinés, pour permettre, par exemple de passer en mode "modification des options"), et, comme il a acces à l'ensemble des modules, il est en mesure de renvoyer tous les événements qu'il reçoit vers une classe qui, en fonction du module dans lequel elle se trouve, réagira de manière adaptée à l'événement.

    Si tu crées une hiérarchie suffisamment précise d'événement (par exemple, le lancement d'un sort par quelqu'un génère un événement de type SpriteCreationEvent qui hérite de SpriteEvent qui hérite de Event), il sera particulièrement facile de faire en sorte que cette classe "laisse tomber" tout un pan de la hiérarchie Event, parce qu'elle n'a pas à s'en occuper
    Je reviens sur les sprites, tu as dis que pour toi un sprite c'était par exemple un coeur a ramasser. Or après tu dis que ta classe Sprite dérive de MovableEntity. Or un coeur ne se déplace pas...
    Rien n'empêche une MovableEntity d'avoir une direction et une vitesse nulle, ce qui la réduirait de facto à... rester sur place

    C'est, peut etre, sans effet sur un jeu 2D, mais les objets qui attendent d'être ramassés pourraient parfaitement être soumis à la pesenteur et ne peuvent, en aucun cas, être considérés comme étant aussi immobiles (meme s'il le sont de facto du fait de leur vitesse et leur direction nulle) que l'arbre ou la pierre qui fait partie du décors

    De plus, et, malgré les apparences, un objet d'inventaire est beaucoup plus mobile qu'il ne peut y paraitre au premier abord:
    • Lorsqu'il sera ramassé, il passera du monde à l'inventaire
    • Si le joueur le tiens en main, il subira les mouvement du bras auquel est rattachée la main qui le tiens
    • S'il y a un "bandeau d'inventaire", il peut se trouver (ou non) à différentes positions dans ce bandeau, et le bandeau lui-même peut se trouver à différentes positions dans l'écran de jeu
    Tu me diras sans doute que j'ergote ou que je joue sur les mots, mais, comprend tu pourquoi il est important qu'un objet d'inventaire soit considéré comme une entité susceptible de se déplacer ?
    Donc je vois mal ce que tu appelles "Sprites" car pour moi, un sprite est simplement une image qui peut s'animer (plusieurs images) ou pas (une seule image). Une entité est alors composé d'un Sprite, ainsi que d'autres attributs comme la position, la taille, la visibilité...
    Le problème est alors que tout est sprite, car, meme s'il n'y a qu'une image à afficher, cela peut etre considéré comme un sprite

    Mais, dans ce cas, quelle différence fait on entre le sprite et l'entité à moins qu'il n'y ait, tout simplement, qu'une relation d'aggrégation entre les deux, et que le sprite participe au comportement d'affichage

    Ce n'est pas exclu, c'est une manière d'envisager les choses
    A méditer: La solution la plus simple est toujours la moins compliquée
    Ce qui se conçoit bien s'énonce clairement, et les mots pour le dire vous viennent aisément. Nicolas Boileau
    Compiler Gcc sous windows avec MinGW
    Coder efficacement en C++ : dans les bacs le 17 février 2014
    mon tout nouveau blog

Discussions similaires

  1. [DESIGN J2ME] Utilisation du pattern STATE ?
    Par BerBiX dans le forum Java ME
    Réponses: 0
    Dernier message: 04/12/2008, 15h55
  2. [Stratégie] Pattern Strategie avec parametres variables
    Par new.proger dans le forum Design Patterns
    Réponses: 3
    Dernier message: 10/06/2007, 20h48
  3. BDS - utilisation des Patterns
    Par RamDevTeam dans le forum Delphi .NET
    Réponses: 1
    Dernier message: 05/11/2006, 17h35
  4. Réponses: 4
    Dernier message: 22/12/2004, 14h28

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