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++] Demande précision sur Pattern Factory


Sujet :

C++

  1. #21
    Expert éminent sénior
    Avatar de koala01
    Homme Profil pro
    aucun
    Inscrit en
    Octobre 2004
    Messages
    11 614
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Âge : 52
    Localisation : Belgique

    Informations professionnelles :
    Activité : aucun

    Informations forums :
    Inscription : Octobre 2004
    Messages : 11 614
    Points : 30 626
    Points
    30 626
    Par défaut
    Citation Envoyé par Aspic Voir le message
    Donc si je comprends bien, je vais avoir une classe Mère ObjetUtilisable avec des classes Filles Coeur, Potion...
    Une autre classe Mère ObjetDecors avec comme classes Filles Rocher, Herbe, Bloc, Mur...

    Mais où sont les Fabriques dans ce cas ?
    Ben, tu fais une fabrique d'ObjetDecors, qui s'occupera de la création de tous les objets du décors(rocher, herbe, mur, ...) une autre pour les armes, qui s'occupera de la création des épées, des haches, des arbaletes et autres, et ainsi de suite
    Citation Envoyé par screetch Voir le message
    c'est une (très) mauvaise idée d'avoir des classes our chaque type d'objet.

    Un moyen plus flexibe et bien meilleur au lieu de l'héritage, c'est la composition; un Objet est composé d'aspects, un aspect est par exemple son aspect a l'ecran (son sprite)
    un autre aspect c'est son aspect "poussable"
    un autre aspect c'est "cassable", ou bien "ramassable"
    Les aspects sont, effectivement, utiles, mais il ne sont pas forcément opposés à l'héritage
    ca permet de créer des entités qui sont a la fois Utilisable et Ramassable, sans avoir de l'heritage multiple ou pire, de l'heritage foireux.
    Tu sembles diaboliser l'héritage multiple, pourtant, correctement étudié et mis en oeuvre, c'est sans doute l'une des possibilités les plus puissantes de C++ par rapport aux langages qui ne l'autorisent pas

    Ne serait-ce que parce qu'une fois que tes concepts sont mis en oeuvre, cela représente sans doute le plus simple moyen de fournir l'interface correspondante à tes objets, quitte à passer par du SRTP, par exemple (pour, justement, éviter les problème d'héritage en losange)
    L'heritage de toutes facons c'est le mal.
    Non, l'héritage, ce n'est pas MAL... c'est la relation la plus forte qui puisse exister entre deux classes, tout simplement...

    Mais, si tu veilles à respecter les principes qui sous-tendent à l'héritage, LSP en tête, la sémantique des termes que tu utilises ( XXX EST UN YYY, sémantiquement parlant) et que, enfin, tu évites l'écueil du "god object", en un mot, si tu envisages l'héritage de manière adéquate, c'est un outils extrêmement puissant, d'autant plus que tu ne pourra que difficilement l'éviter dans tes programmes.

    Je t'accorde cependant le fait que la composition doit être préférée chaque fois que faire se peut, et surtout chaque fois que cela a un sens (ou est-ce chaque fois que l'héritage n'en a pas )
    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

  2. #22
    screetch
    Invité(e)
    Par défaut
    je ne diabolise pas l'héritage multiple mais je diabolise l'heritage foireux.

    Ce que je vois venir gros comme un camion, c'est:

    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
    class Entity;
    class CollectableEntity : Entity;
    class UsableEntity : Entity;
    class PushableEntity : Entity;
    class BreakableEnyity : Entity;
     
    class Weapon : UsableEntity
    class Heart : ..... merde, c'est Usable et Collectable, mais toute les deux derivent de Entity...
    voilà c'est tout; il n'y a aucune raison que les classes heritent les unes des autres, elles fournissent des services différents, je pensais donc plus 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
     
    class IPushBehaviour;
    class IBreakBehaviour;
    class ICollectBehaviour;
    class IUseBehaviour;
     
    class Entity
    {
      IPushBehaviour*  onPush;
      IBreakBehaviour* onBreak;
      ICollectBehaviour* onCollect;
      IUseBehaviour* onUse;
    };
     
    // et on herite pas de Entity :)
     
    class PushableObjectBehaviour : public IPushBehaviour
    {
    };
    class ImmovableObjectBehaviour : public IPushBehaviour
    {
    };
    class HeavyObjectBehaviour : public IPushBehaviour
    {
    };
    et ainsi de suite.

    l'avantage, c'est que ajouter des features légèrement differentes au jeu est ultra simple; il suffit de créer une sous-classe du comportement choisi.
    Ajouter une mecanique complexe est simple aussi; il suffit d'ajouter une interface, et de la sous-classer a son tour.

  3. #23
    Expert confirmé
    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
    Points : 4 388
    Points
    4 388
    Par défaut
    Moi je suis pour l'héritage simple mais contre l'héritage multiple (certainement parce que je ne sais pas m'en servir correctement) mais surtout parce que la seule fois où je l'ai utilisé c'est justement pour me retrouver dans le cas du losange de la mort (Dreaded Diamond) et j'ai eu recours à de l'héritage multiple virtuel ! Une vraie connerie qui marche en plus...

    Le plus drôle c'est que le premier code de screetch c'est exactement ce que j'avais implémenté il y'a quelques mois dans un de mes jeux et je me suis retrouvé plus tard dans le mouise mais j'ai réussi à contourner le problème.

    Par contre dans le 2ème code de screetch, je ne vois pas l'utilité de la classe Entity, elle ne sert à rien pour l'instant (enfin de ce que j'en vois). De même, je ne vois pas comment tu peux résoudre le problème d'un Coeur qui est à la fois "Collectable" et à la fois "Usable" sans l'héritage multiple ?

    Tu pourrais juste détailler la classe UsableEntity et donner un exemple d'utilisation pour la création d'un objet "Coeur" par exemple (qui a les caractéristiques "Collectable" et "Usable"). Merci

    Sinon merci Koala pour tes explications
    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 !

  4. #24
    screetch
    Invité(e)
    Par défaut
    la classe Entity est justement celle qui a tous les aspects possible.

    Pour être plus précis, une Entity est une classe (la elle est simplifiée évidemment) dont tu ne changes pas le comportement en la sous-classant, mais en lui donnant des "aspect". C'est ca la composition, dans un sens; une entity est une composition de différents aspects, chacun d'entre eux pouvant être différent.

    En jouant sur les combinaisons tu pourras voir la "beauté" du design: tu pourras faire des choses que tu ne pensais pas pouvoir faire.


    sinon le post plus haut, ce n'est pas une coincidence si tu as aussi fait "l'erreur" plus tôt. C'est très commun. Le moteur Frostbite derrière Battlefield 3, il a les mêmes défauts. C'est comme ca partout. Et ca marche jamais. Apres quelques années d'experience dans le jeu vidéo il serait temps de changer ca



    Bon donc pour faire un Coeur:

    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
    // le comportement si tu le ramasses
    ICollectableBehaviour* collect = new InventoryAddBehaviour(category = Health, value = 1);
    //un truc comme ca; en gros ca veut dire que
    // tu peux le ramasser et si tu le ramasses, ca
    // va ajouter "1" a "health" dans ton onventaire.
    // On peut faire mieux évidemment c'est juste un exemple
     
    // le comportement si tu l'utilises
    IUsableBehaviour* use = new HealBehaviour();
     
    // le comportement du coeur si tu le pousse, c'est de pas bouger
    IPushBehaviour* push = new ImmovableObject();
     
    // le comportement si tu le casses
    IBreakableBehaviour* break = new UnbreakableBehaviour();
     
    Entity* coeur = new Entity(collect, use, push, break);
    voila tu as créé une entité. Si tu essayes de la casser, rien ne se passe. Si tu l'utilises, elle va te soigner. Si tu la pousses, ca marche pas. Si tu la ramasses, ce sera mis dans ton inventaire (par exemple). C'est plutot une potion en fait.



    WARNING! BIG DISCLAIMER! ACHTUNG!
    c'est un exemple avec des behaviours, que tu avais cité plus haut. En aucun cas je cautionne ces behaviours exactement =)
    Je pense que tes behaviours devraient être différent. La seule lecon a retenir de cela c'est le concept, de ne *pas* avoir d'entité que tu sous-classe; d'avoir des concepts/behaviours/aspects, au grain plus fin; chaque aspect ne traite qu'une seule chose; pas comme une entité qui peut tout faire.
    Ensuite, chaque aspect peut avoir une implémentation différente.

    L'entité, c'est uniquement la combinaison de ces aspects, de tous ces aspects. Créer une entité, c'est juste définir pour chaque aspect ce qui se passe.
    Un coeur, donc, dans l'exemple plus haut, c'est un objet:
    * qui ne bouge pas si on le pousse
    * qui donne de la vie si tu l'utilises

    etc etc




    TON BOULOT en premier lieu est de définir tous les aspects dont tu pourrais avoir besoin (les génériques); c'est la liste de tous les aspects, et tu créés une classe abstraite pour chacun d'entre eux.
    Au lieu de pushable, movable, collectable, usable ca devrait sans doute etre plus orienté vers les actions de l'utilisateur; par exemple, tu pourrais avoir un "CollisionBehaviour". Avec des implémentations qui font que en cas de collision:
    * tu peux te teleporter (une porte par exemple, t'emmene dans une cave)
    * tu peux pas le bouger (un mur, une pierre)
    * tu peux le ramasser (Collectable)
    etc

    un autre est le InteractBehaviour. Que se passe t'il si tu utilises le bouton "Action" lorsque tu es a coté de l'objet:
    * Tu ramasses l'objet (une pierre)
    * Tu affiches un texte (panneau)
    * Tu utilises une clé (porte fermée) etc etc

    etc etc

    et créer une entité revient a créer un objet ou tu vas definir chacun de ces behaviours indépendamment.

    En plus lors du developpement, tant que tu as pas implémenté le behaviour dont tu as besoin, il te suffit de le laisser vide ou d'en mettre un autre; ca ne marchera pas mais tu pourras déjà tester le jeu

  5. #25
    Membre émérite
    Profil pro
    Inscrit en
    Novembre 2004
    Messages
    2 764
    Détails du profil
    Informations personnelles :
    Localisation : France

    Informations forums :
    Inscription : Novembre 2004
    Messages : 2 764
    Points : 2 705
    Points
    2 705
    Par défaut
    Citation Envoyé par Aspic Voir le message
    Moi je suis pour l'héritage simple mais contre l'héritage multiple (certainement parce que je ne sais pas m'en servir correctement) mais surtout parce que la seule fois où je l'ai utilisé c'est justement pour me retrouver dans le cas du losange de la mort (Dreaded Diamond) et j'ai eu recours à de l'héritage multiple virtuel ! Une vraie connerie qui marche en plus...
    Où est le problème, alors ?

  6. #26
    screetch
    Invité(e)
    Par défaut
    je ne voudrais pas en arriver a une querelle de clocher sur l'héritage multiple, mais là je trouve que c'est de loin la *pire* solution que l'on puisse avoir pour ce problème (je ne critique pas en général, juste en particulier sur ce projet).

    On va a voir une classe de base avec trop de fonctionnalités (ce qui viole et même viole collectivement le principe d'une fonctionnalité par classe) et dans les classes enfant il va falloir décider quelle fonctionnalité on veut.

    Par exemple dans le cas précédent, un Coeur se comporte des fois comme un Collectable, des fois comme un Usable. Donc pour certaines méthodes il faudra se demander si il faut choisir l'implémentation Collectable, ou bien la Usable.

    L'heritage multiple pour agréger les fonctionnalités de plusieurs composants c'est pas la bonne solution; c'est justement l'agrégation le meilleur design.

  7. #27
    Expert confirmé
    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
    Points : 4 388
    Points
    4 388
    Par défaut
    Je trouve le concept de screetch très intéressant ca donne une autre approche contrairement à l'héritage multiple, je ne suis pas qualifié pour dire si c'est mieux ou pas mais ca m'a l'air très intéressant et puissant

    Par contre, je n'arrive pas encore à l'implémenter, j'ai fais ce 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
    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
    58
    59
    60
    61
    62
    63
    64
    65
    66
    67
    68
    69
    70
    71
    72
    73
    74
    75
    76
    77
    78
    79
    80
    81
    82
    83
    84
    85
    86
    #include <iostream>
     
    using namespace std;
     
    // les comportements génériques
    class IPushBehaviour
    {
        virtual void action() = 0; // virtuelle pur à redéfinir dans les classes filles
    };
    class IBreakBehaviour
    {
        virtual void action() = 0;
    };
    // **************************
     
    // l'entité qui possède tous les comportements génériques
    class Entity
    {
    private :
        IPushBehaviour*  onPush;
        IBreakBehaviour* onBreak;
    public:
        Entity(IPushBehaviour* p, IBreakBehaviour* b)
            : onPush(p), onBreak(b)
        {
     
        }
     
        ~Entity(){};
    };
    // **************************
     
    // c'est quoi ces classes ?
    class PushableObjectBehaviour : public IPushBehaviour
    {
        inline void action()
        {
            std::cout << "Je peux pousser !" << std::endl;
        }
    };
    class ImmovableObjectBehaviour : public IPushBehaviour
    {
        inline void action()
        {
            std::cout << "Je ne peux pas pousser !" << std::endl;
        }
    };
    class HeavyObjectBehaviour : public IPushBehaviour
    {
        inline void action()
        {
            std::cout << "Je suis trop lourd !" << std::endl;
        }
    };
     
    class BreakableObjectBehaviour : public IBreakBehaviour
    {
        inline void action()
        {
            std::cout << "Je suis cassable !" << std::endl;
        }
    };
     
    class UnBreakableObjectBehaviour : public IBreakBehaviour
    {
        inline void action()
        {
            std::cout << "Je suis incassable !" << std::endl;
        }
    };
    // **************************
     
    int main()
    {
        // le comportement du coeur si tu le pousse, c'est de pas bouger
        IPushBehaviour* push = new ImmovableObjectBehaviour();
     
        // le comportement si tu le casses
        IBreakBehaviour* break_ = new UnBreakableObjectBehaviour();
     
        Entity* coeur = new Entity(push, break_);
     
        // j'en fait quoi après de coeur ?
     
        return 0;
    }
    J'ai mis quelques commentaires dedans, je n'ai pas tout saisie je pense car je ne sais pas comment utiliser le fait que le coeur que j'ai créé est ni poussable et ni cassable (je n'ai pas implémenté Collectable pour des raisons de lisibilité).
    J'ai du encore mal comprendre le concept
    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 !

  8. #28
    screetch
    Invité(e)
    Par défaut
    je trouve que pour l'instant au contraire tu as très bien compris le principe.
    Un petit detail: action() ce n'est pas très explicite. Vu que tu vas pouvoir créer plusieurs interfaces racines, tu n'es pas (plus) obligé d'avoir une méthode unique a "overrider" dans toute les classes; tes interfaces IPushBehaviour et IBreakBehaviour sont maintenant complètement indépendantes, peuvent avoir un nombre de méthodes indépendantes et même mieux, des méthodes au nom très explicite. On pourrait imaginer:
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    // les comportements génériques
    class IPushBehaviour
    {
        virtual void tryPush() = 0; // virtuelle pur à redéfinir dans les classes filles
        virtual void beginPush() = 0; // virtuelle pur à redéfinir dans les classes filles
        virtual void endPush() = 0; // virtuelle pur à redéfinir dans les classes filles
    };
    c'est bien sur seulement si tu en as besoin




    Bon moi je trouve que ca a été pas mal compris donc on va continuer (si t'avais rien piger j'arrêterais franchement de te causer :p)

    Une entité tu vas devoir l'afficher a l'écran ca peut être intéressant de lui coller un Sprite aussi, ou je ne sais pas comment tu voulais faire ton rendu

    Ton behaviour va sans doute interagir avec d'autres entités (en principe, le joueur, mais peut être d'autres entités) ca pourrait être intéressant de lui passer des paramètres. Par exemple, si tu donnes un coup d'épée; mettons qu'avec l'épée rustique tu peux pas casser une porte mais avec l'épée blanche tu peux;

    lorsque tu vas actionner ton épée, ton *entité* épée va "déclencher" une action sur le behaviour HitBehaviour de l'entité Porte.
    On voudrait alors casser la porte.

    Plusieurs choses semblent intéragir:

    - le joueur va utiliser l'épée (le UseBehaviour)
    ca va declencher un mouvement de l'épée
    - l'épée va collisionner la porte (le HitBehaviour)
    Ton HitBehaviour va devoir réagir, et par exemple donner des dégâts a la porte, en fonction de l'épée
    - la porte va peut être "mourir", être retirée du jeu

    voila un exemple d'interactions





    Tu devrais avoir quelque part un "Monde"; ce monde est une collection d'entités qui interagissent entre elles. Le monde est divisé en deux:

    * Les règles qui gouvernent cem onde (en gros c'est la physique)
    * Les conséquences de tes actions (en gros c'est tes behaviours)

    Ton monde va par exemple autoriser le joueur a se deplacer;
    lorsque tu appuies sur une touche, l'entité "joueur" va tenter de se déplacer a droite, ce qui va en fait dire au "Monde" "déplace cette entité a gauche", ce qui sera fait... ou pas.

    Lorsque tu fais un déplacement a gauche, le "World" va recalculer les interactions en cours en fonction des règles. Par exemple, tu veux aller a droite, il va vérifier d'abord ce qu'il y a a droite; si il y a deja une entité, mettons un mur, il va pas te deplacer! il va au contraire dire au mur qu'il y a une collision avec le joueur; et au joueur qu'il y a une collision avec le mur.

    Il va donc prendre ces deux entités qui viennent de se taper.
    Entity entity1 = player
    Entity entity2 = mur
    (il sait pas que c'est un mur, il sait juste qu'il y a un truc)

    et il va faire, par exemple:
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    entity1->CollisionBehaviour->onCollision(world, entity1, entity2);
    entity2->CollisionBehaviour->onCollision(world, entity2, entity1);
    le CollisionBehaviour du mur va donc réagir a la collision. bah, il se passe rien. Mais comme on ne peut pas rentrer dans un mur, on peut demander a onCollision de retourner false (ou un enum qui dit "Movement refusé")
    Le CollisionBehaviour du joueur va aussi réagir. Lui, peut etre, va commencer a jouer une animation de tête qui sonne, ou un son



    Donc la phase suivante selon moi serait de créer ta classe World; elle va être une base de donnée des Entités (qui sont juste une liste de behaviours au final) et des règles de mises a jour a chaque frame
    Le world va donc contenir une liste d'entités et leurs positions;
    des fonctions pour ajouter des entités;
    des fonctions pour retirer des entités;
    des fonctions pour mettre a jour le monde

    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
    class World
    {
    std::vector< Entity > entities;
    public:
      void addEntity(Position pos, Entity* entity); // facile a coder pour l'instant
      void removeEntity(Entity* entity); // facile a coder pour l'instant
      void frameUpdate();
     
      void moveEntity(Entity* entity, Position offset);
    };
    void World::frameUpdate()
    {
      //la fonction délicate; toute les entités doivent être mises a jour
      // en fonction de ce qu'elles veulent faire, et de ce qu'elles peuvent faire =)
      // je choisis de faire ainsi: chaque entité a un ControlBehaviour et ce
      // controle behaviour est celui qui est appelé a chaque frame
      for(std::vector it = entities.begin(); it != entities.end(); ++it)
      {
        entities->ControlBehaviour->frameUpdate(this);
        // un control behaviour pourrait demander par exemple de
        // déplacer l'entité dans le monde, ce qui va declencher un
        // update de position, des collisions, etc etc
        // par exemple si le player appuie sur droite, le ControlBehaviour
        // du player va être appelé, vérifier l'input, demander au monde
        // de deplacer l'entité vers la droite
      }
    }
     
    void World::moveEntity(Entity* entity, Position offset)
    {
      bool doMove = true;
      Position pos = getEntityPosition(entity);
      newpos = pos + offset;
      std::vector<Entity> entities = getEntitiesAtPosition();
      for (collision in entities)
      {
        move &= collision->CollisionBehaviour->onCollision(collision, entity, world);
        move &= entity->CollisionBehaviour->onCollision(entity, collision, world);
      }
      if (move)
      {
        // aucune collision n'a provoqué d'erreur =)
        setEntityPosition(entity, newpos);
      }
    }

    voila, donc maintenant on a une sorte de base de données géographique des entités, et une sorte de moteur physique qui va leur permettre de bouger, et ainsi intéragir.
    Les behaviour pourront intéragir avec le monde pour par exemple, créer des nouvelles Entity (un ennemy qui meurt peut laisser un bonus), detruire une entité (un ennemi qui meurt...), intéragir, etc etc.


    Pour en revenir a la porte; lorsque tu vas appuyer sur l'épée, c'est le COntrolBehaviour du joueur qui va initier le mouvement.
    Le ControlBehaviour, si il detecte un appui sur par exemple espace, va créer une entié "EpéeEnMouvement" dans le monde (juste pour quelques frame).
    Ensuite, le boulot du ControlBehaviour du _joueur_ est fini; c'est au tour du control behaviour de l'_épée_ d'entrer en jeu. ce ControlBehaviour, a chaque frame, va deplacer l'épée dans la direction ou tu l'agites. Enfin va demander au monde de déplacer l'objet dans cette direction.

    Mettons maintenant que le World detecte que l'épée touche un mur; et c'est parti, le CollisionBehaviour de l'épée va être appelé.
    Ce CollisionBehaviour peut par exemple influer sur le HealthBehaviour de l'autre unité; par exemple
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
    class WeaponCollisionBehaviour : public ICollisionBehaviour
    {
    public:
      bool onCollision(Entity* weapon, Entity* target, World* world)
      {
        target->HealthBehaviour->onHit(target, weapon, Damage, world);
      }
    };
    On peut penser que le HealthBehaviour du mur, il en a rien a foutre d'un coup d'épée: il a un InvincibleHealthBehaviour.
    La porte elle pourrait vérifier le damage:
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    // cette classe tue l'entité si le damage en entrée est assez grand;
    // c'est pas comme un ennemi dont la santé diminue petit a petit
    // il faut donc donner un grand coup pour tuer l'entité
    class ThresholdHealthBehaviour : public IHealthBehaviour
    {
    public:
      // retourne le statut de l'ennemi
      bool onHit(Entity* myself, Entity* hittingme, Damage damage, World* world)
      {
        if (damage > MaximumDamage)
          world->removeEntity(myself);
      }
    };


    bon c'est pas parfait mais, tu vois le genre; avec un peu de code, tu auras un monde et tu pourras faire intéragir les objets entre eux =)

  9. #29
    Expert confirmé
    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
    Points : 4 388
    Points
    4 388
    Par défaut
    Ouah la longue réponse ^^ par contre ca va un peu vite avec la gestion du World (ca viendra c'est sur dans un premier temps je veux bien comprendre le concept avec notre exemple sur le Coeur )

    Je crois commencer à comprendre ce principe des interactions entres les entités. Cela veut dire que tout est Entity : les ennemies, le joueur, les éléments du décor, les objets, ... ca risque pas de faire une classe énorme ?

    Revenons sur la classe Entity :
    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
    // l'entité qui possède tous les comportements génériques
    class Entity
    {
    private :
        IPushBehaviour*  onPush;
        IBreakBehaviour* onBreak;
    public:
        Entity(IPushBehaviour* p, IBreakBehaviour* b)
            : onPush(p), onBreak(b)
        {
     
        }
     
    IPushBehaviour* getOnPush() const {return onPush;}
    IBreakBehaviour* getOnBreak() const {return onBreak;}
     
        ~Entity(){};
    };
    // **************************
    A quoi serve les variables privées onPush et onBreak ?

    Et dans le main :
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    int main()
    {
        // le comportement du coeur si tu le pousse, c'est de pas bouger
        IPushBehaviour* push = new ImmovableObjectBehaviour();
     
        // le comportement si tu le casses
        IBreakBehaviour* break_ = new UnBreakableObjectBehaviour();
     
        Entity* coeur = new Entity(push, break_);
     
        return 0;
    }
    Comment j'utilise le coeur que je viens de créer ?
    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
    screetch
    Invité(e)
    Par défaut
    Qu'entends tu par "l'utiliser"?
    Honnêtement moi l'entité, j'en aurait fait une struct; une entité est juste un amalgame de behaviours, rien de plus. L'entité elle-même n'a pas de code (ou en a très peu), toute les implémentations sont dans les behaviours.


    Ensuite, tu dois pouvoir comprendre la généralisation du système; une entité est finalement très customizable, les combinaisons possibles entre les différents behaviours sont très très nombreuses

  11. #31
    Expert confirmé
    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
    Points : 4 388
    Points
    4 388
    Par défaut
    Citation Envoyé par screetch Voir le message
    Qu'entends tu par "l'utiliser"?
    Honnêtement moi l'entité, j'en aurait fait une struct; une entité est juste un amalgame de behaviours, rien de plus. L'entité elle-même n'a pas de code (ou en a très peu), toute les implémentations sont dans les behaviours.
    D'accord donc en gros si j'ai 50 behaviors possibles, je vais avoir une structure énorme ? et donc une création d'entité énorme :
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    Entity* e = new Entity(behavior1,behavior2,behavior3,behavior4,behavior5,...);
    Dernière chose, dans l'histoire ton procédé c'est l'application du pattern composite c'est bien ca ?
    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 !

  12. #32
    screetch
    Invité(e)
    Par défaut
    Oui c'est ca, j'ai du mal a voir 50 behaviours différents mais un jeu complexe pourrait avoir besoin de ca.
    Il y a alors des moyens de simplifier ce sera un problème pour toi?

    en fait normalement tu n'as pas besoin de beaucoup de behaviours, puisque ceux-ci sont utilisés seulement pour les intéractions.

    or il n'y a pas 50 facons d'intéragir avec un objet, par contre lorsqu'il y a une interaction il y a beaucoup de réponses possibles de l'objet, d'où l'intérêt du design.

    De plus, chaque behaviour peut traiter plusieurs types d'intéraction comme je l'ai noté plus haut; tu peux avoir un PhysicsBehaviour qui fait onPush, onHit, onBreak, etc etc etc
    il ne faut pas avoir une grnaularité trop élevée, ni trop faible;

    la granularité la plus faible c'est l'Entité qui implémente toute les méthodes et en héritant de Entité on redéfinit son comportement.
    la granularité la plus élevée, c'est l'Entité qui contient des dizaines de pointeurs avec chacun une méthode. Chaque méthode peut être redéfinie individuellement (=flexibilité maximale bien sur) mais un overhead assez élevé.

    La meilleure granularité c'est d'avoir un nombre de Behaviours qui correspond logiquement au nombre de *type* d'intéractions possibles, au nombre de catégories en quelque sorte; entre 5 et 10 devrait vraiment faire l'affaire (ca fait déjà un peu cradoque mais pas tant que ca)

  13. #33
    Expert confirmé
    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
    Points : 4 388
    Points
    4 388
    Par défaut
    Ok merci pour toutes ces infos
    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 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 Aspic Voir le message
    D'accord donc en gros si j'ai 50 behaviors possibles, je vais avoir une structure énorme ? et donc une création d'entité énorme :
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    Entity* e = new Entity(behavior1,behavior2,behavior3,behavior4,behavior5,...);
    Dernière chose, dans l'histoire ton procédé c'est l'application du pattern composite c'est bien ca ?
    cf. http://scottbilas.com/files/2002/gdc...cts_slides.pdf
    cf. http://cowboyprogramming.com/2007/01...your-heirachy/

    Scott Bilas et Mike West présentent des systèmes similaires à ce que tu mets en place, et ont rencontré les même difficultés.
    [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.

  15. #35
    Expert confirmé
    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
    Points : 4 388
    Points
    4 388
    Par défaut
    Intéressant mais sur le slide 8 :
    http://scottbilas.com/files/2002/gdc...cts_slides.pdf

    D'après le diagramme, on a recourt à l'héritage multiple (pour la classe Missile par exemple qui dérive de Colidable et de Drawable) et en plus on se retourve dans le losange de la mort... Bizarre, leur implémentation

    Je vais tenter d'implémenter l'exemple avec le Missile et le vaisseau spaciale avec les interfaces Drawable, Collidable et Chewable mais c'est pas gagné

    Je vous tiens au courant après

    EDIT :
    Voilà ce que j'ai fais mais après le bloque
    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
    #include <iostream>
     
    using namespace std;
     
    class IDrawable
    {
        public:
            virtual void draw() = 0;
    };
     
    class ICollidable
    {
        public:
            virtual void collision() = 0;
    };
     
    class IChewable
    {
        public:
            virtual void chew() = 0;
    };
     
    struct Entity
    {
        IDrawable* onDraw;
        ICollidable* onCollision;
        IChewable* onChew;
    };
     
    /*
    class Missile
        : public IDrawable, ICollidable
    {
     
    };
    */
    int main()
    {
        Entity* e = new Entity();
        //e->onDraw->draw();
     
        return 0;
    }
    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 !

  16. #36
    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
    J'ai dis qu'ils étaient similaires, pas équivalents

    Scott Bilas a implémenté un système qui se base sur l'héritage pour composer ses objets (c'est très tordu, et clairement pas une bonne idée ; en même temps, on était en 2002).

    [attention, je n'aime pas du tout ce que je vais dire]

    Je conseille plutôt de faire une liste<Interface*>, une fonction void visit_and_exec(InterfaceVisitor* visitor), et d'avoir toutes tes interfaces implémentables qui dérivent de Interface. Le pattern visiteur te permettra ensuite de retrouver une interface particulière et d'éxécuter le code qui va bien.

    Ceci dit, ça signifie aussi que tu va passer pas mal de temps à faire des recherches dans une liste d'interfaces (c'est ce qu'essaie d'éviter Bilas : les interfaces sont directement accessibles). Tu peux mitiger ce problème au prix d'un non respect du principe OCP : la classe Entity tient une instance de chaque interface (comme tu le fais).
    [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.

  17. #37
    Expert confirmé
    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
    Points : 4 388
    Points
    4 388
    Par défaut
    Merci à tous, j'en ai appris pas mal sur ce pattern
    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 !

+ Répondre à la discussion
Cette discussion est résolue.
Page 2 sur 2 PremièrePremière 12

Discussions similaires

  1. Réponses: 16
    Dernier message: 26/08/2011, 22h02
  2. [MySQL] Demande précisions sur LOCK TABLE
    Par renaud26 dans le forum PHP & Base de données
    Réponses: 8
    Dernier message: 15/04/2011, 13h49
  3. Demande de précision sur "Extends" ..
    Par Invité dans le forum Langage
    Réponses: 6
    Dernier message: 12/02/2006, 14h25
  4. Demande de précisions sur Backup/Restore et transactions
    Par lio33 dans le forum Connexion aux bases de données
    Réponses: 1
    Dernier message: 16/11/2005, 12h08
  5. [Observateur] Précisions sur le design pattern Observer [UML]
    Par joquetino dans le forum Design Patterns
    Réponses: 2
    Dernier message: 07/10/2004, 22h35

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