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++

Vue hybride

Message précédent Message précédent   Message suivant Message suivant
  1. #1
    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 [C++] Utilisation du pattern strategie (ou pont)
    Bonjour

    J'ai deux choses à demander ^^

    1ère chose :

    J'ai étudié le pattern Strategie grâce au livre "Design Pattern - Tete la Première". Cependant, quelque chose me chiffonne quand je parcours le Web et quand j'ai découvert le pattern Bridge (pont) qui ressemble beaucoup au pattern stratégie :
    Le pont est un pattern Strcucturel:
    - Pont: Sépare une abstraction de son implémentation de sorte que les deux puissent varier indépendamment.

    Le stratégie est un pattern comportemental:
    - Stratégie: Définit une famille d'algorithmes, encapsule chacun, et les rend interchangeables. La Stratégie permet à un algorithme de varier indépendamment des clients qui l'utilisent.
    Maintenant, dans le livre pour le pattern Stratégie, j'ai l'impression que c'est un mixte du pattern Pont et Strategie. Voilà le diagramme UML en pièce jointe.

    Voilà je voulais votre avis sur la question

    2ème chose :

    Je souhaiterais appliquer l'un des deux patterns (je pense le Strategie) à ma situation dans mon jeu 2D :
    Soit les interfaces IEntity, ISprite ainsi que des interfaces *Behaviours

    • La classe Player et Enemy dérivent de l'interface IEntity.
    • La classe AnimatedSprite et ClassicalSprite dérivent de l'interface ISprite.
    • IEntity est composé d'un ISprite (composition) et d'interfaces de Behaviours (non implémentées encore).
    • La classe World (qui gère le monde en entier) possède un ensemble d'entités (dans un vector pour l'instant).


    Donc grâce au polymorphisme je peux mettre à jour et dessiner toutes mes entites en parcourant le vector.

    Pas de problème jusqu'à la

    MAIS si je veux rajouter dans les classes Player et Enemy des attributs spécifiques voires des méthodes spécifiques, je ne pourrais jamais les invoquer à partir de World (car il ne manipule que des IEntity).

    De même, les Behaviours vont devoir manipuler des attributs des classes Player, Enemy par exemple quand un ennemie va perdre de la vie, il faudra bien baisser son attribut heath (qui sera privé dans la classe Enemy).

    Pour être plus clair, prenons un exemple de Behaviours : IColissionBehaviour (celui qui gère les collisions entre les entités). Imaginons une classe HitBehaviour qui implémente (dérive en C++) de IColissionBehaviour.
    La classe HitBehaviour à besoin de connaitre des infos sur toutes les Entity qui implémentent ce comportement !

    Et hop, je suis coincé !! Je ne vois pas comment faire...

    J'ai vraiment besoin d'aide, je n'arrive plus à avancer dans mon design

    Merci en tout cas, et bonne vacances
    Images attachées Images attachées  
    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 !

  2. #2
    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 à toi.

    Sur "bridge vs strategy" d'abord.
    * Première différence : leur but. L'un (bridge) est un pattern structurel et concerne donc la façon dont ton programme est écrit, tandis que l'autre (strategy) est un pattern comportemental qui concerne la façon dont ton programme s'exécute. Typiquement, la stratégie utilisée par un objet est appelée à varier au cours de l'exécution alors qu'on utilisera plutôt bridge pour une implémentation choisie au démarrage.
    * Seconde différence : typiquement, avec "bridge", l'abstraction est presque entièrement bâtie par-dessus cette implémentation et offre souvent une interface proche de cette dernière. A l'inverse, une stratégie est plutôt une partie seulement de son consommateur. Dans ton cas, tu as ainsi plusieurs familles de stratégie par client, et ce dernier assure aussi d'autres services qui ne dépendent d'aucune stratégie.
    * Troisième différence : le couplage est typiquement plus fort avec le pattern "strategy". Dans le cas de figure où "bridge" est utiliser pour s'interfacer avec différents SGBD, on peut écrire autant d'implémentations que l'on veut sans rien changer à l'abstraction. A l'inverse, si tu écris une stratégie pour une AI en mode berserk, tu as alors besoin de modifier ton client pour ajouter un code de sélection de l'AI berserk.



    Sur la 2nde partie.
    * Pourquoi World aurait-il besoin de manipuler les attributs spécifiques à certaines classes ? Si des traitements requièrent une connaissance détaillée de ces classes, ils doivent être effectués par ces classes en question et non pas par World. Cf. le Dependency Inversion Principle.

    * Sur le reste, je ne pourrais pas t'aider davantage sans savoir de quel type de jeu il s'agit mais, grosso modo, je dirais que tu dois définir des interfaces additionnelles (IHasHealth pour donner un probablement mauvais exemple) ainsi qu'une façon de savoir si un objet en question implémente telle ou telle interface. HitBehaviour n'aura ainsi besoin que de manipuler une certaine interface et n'aura pas besoin de connaître tous les détails de l'objet. Alternativement, ces interfaces pourraient être implémentées par les comportements eux-mêmes plutôt que par les entités.

  3. #3
    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
    Merci pour ta réponse, donc il s'agit bien d'une stratégie dans mon livre

    Donc en fait, je fais un remake de Zelda en 2D :
    http://www.developpez.net/forums/d98...ake-zelda-gbc/

    En fait, j'ai du mal à me dire si je dois séparer le Player (qui est une entité puisqu'il a un Sprite et des Behaviours) des autres entités car un Player est un peu spécial, contrairement aux autres entités (porte, trou, panneau, ennemies, murs, objets...) il se déplace grâce au joueur et il possède un inventaire qui lui même possède des armes.

    Donc, faut-il faire hérité Player de IEntity ou le gérer à part ? Ou mettre la classe Inventory et Item et comment les lier ?
    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. #4
    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
    Voilà un projet bien sympathique.

    Commençons par le point le plus simple : je pense que l'inventaire devrait effectivement être totalement indépendant, je ne vois aucune raison pour en faire un sous-élément de Link. Qui plus est pas besoin d'une classe item : l'inventaire devra simplement avoir des membres "numBombes", "numFlèches", etc, ce sera plus facile à gérer. Le seul cas où un item devrait avoir une instance associée ce sera une entité dans le cas où il est par terre, attendant d'être ramassé.

    Maintenant, sur la question de savoir si Link doit être traité comme une entité comme les autres, ma réponse est positive quand je regarde ce qui les unit :
    * AI : le contrôle par le joueur n'est qu'une forme d'AI. Tous auront besoin de services communs comme "puis-je aller en (x, y) ?"
    * Collisions. Les réactions (comportements) seront différentes mais, fondamentalement, les mécanismes sont les mêmes. On a des collisions joueur-projectile, ennemi-projectile, joueur-mur, ennemi-mur, joueur-ennemi et même ennemi-ennemi (si on pense à ces statues amovibles : si on en déplace une elle peut pousser un monstre).
    * Comportements communs : par exemple une projection en arrière suite à un coup, durée pendant laquelle l'AI ou le joueur ne peuvent rien faire. Ou le gel des AI et de Link lors d'une transition d'écran.
    * Gestion de la vie et détection de la mort.

    A mes yeux ça fait beaucoup de choses en commun alors que les différences peuvent très bien être gérées par des comportements différents.

    A priori je partirais sur une hiérarchie de base de la sorte :
    Entity (avec entre autres propriétés IsMovable, IsItem, IsSpiky, IsPlayer, IsCreature)
    * Movable (statues déplaçables : comportements identiques, seul le graphisme change)
    * Item (bombes, fées, etc : service d'instanciation rapide, comportements identiques)
    * Spiky (contient notamment un flag de faction IsLinkSided et des dommages au contact)
    ** Projectile (flèches, boules de feu, rochers qui tombent, etc)
    ** Killable (contient notamment des PV)
    *** Player
    *** Creature

  5. #5
    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
    Salut,

    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

    Pour ce qui est de l'inventaire, un raisonnement similaire peut parfaitement etre suivi:

    Rien ne t'empêche de considérer ton inventaire comme étant une entité affichable et de le gérer en tant que tel pour tout ce qui a trait à l'affichage, mais, d'un autre coté, il faudra le gérer en tant qu'inventaire pour toute une série de situation.

    La responsabilité de l'inventaire sera, d'ailleurs, la gestion des objets qu'il contient (il agira comme une collections d'objets )

    Ce qui tombe bien, c'est que l'on peut estimer que, chaque joueur ayant son propre inventaire, on peut estimer que c'est un candidat idéal pour lui déléger la gestion de celui-ci

    En réalité, une bonne partie de la gestion de l'inventaire en tant que tel sera sans doute déléguée au système d'événements de celui-ci, étant donné que beaucoup d'actions effectuées par le joueurs auront trait à la gestion de l'inventaire
    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

  6. #6
    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
    D'accord, j'ai implémenté le Player comme étant une entité tout comme un ennemie (donc Player et Enemy héritent de IEntity).

    Voici un diagramme fait à la main (très moche désolé) :
    http://www.tutoworld.com/temp/uml_zelda.jpg

    Citation Envoyé par koala01 Voir le message
    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!
    Justement en faisant hériter Player de IEntity, vu que le World ne manipule que des IEntity, je ne pourrais jamais appeler des méthodes ou avoir accès à des attributs propre à la classe Player, d'où mon problème.

    Voilà les classes (simplifiées) :
    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
    87
    88
    89
    90
    91
    92
    93
    94
    95
    96
    97
    98
    class World
    {
        private:
            std::vector<IEntity*> entities; // ensemble des entités de la zone de jeu
            Core::XmlManager* pXmlManager; // gestion du fichier xml de config du jeu
     
            IVect2D pos; // position de la zone dans la map courante
            int idCurrent; // id de la zone courante dans la map courante
            int idCurrentMap; // id de la map courante (une map contient plusieurs zones)
            int idCurrentMask; // id du masque correspondant à la map courante
     
        public:
            World();
            ~World();
     
            bool load();
            bool draw(const Core::IRenderer& r);
            bool update(int direction, int buttons);
    };
     
    // implémentation de la classe World :
    World::World()
    {
    .....
    }
     
    World::~World()
    {
    ....
    }
     
    bool World::load()
    {
        int idTemp = LOAD_FAILED;
     
    // des chargements
    ....
     
        idCurrentMap = IM->loadImage("data/map/overworld/1.bmp", false);
        idCurrentMask = IM->loadImage("data/map/overworld/1m.bmp", false);
     
        // chargement du sprite via le ImageManager
        idTemp = IM->loadImage("data/link_mouvement.bmp");
     
        // création d'un sprite
        if (idTemp != LOAD_FAILED)
        {
            AnimatedSprite* aS = new AnimatedSprite(
                IM->getImageSizeById(idTemp),
                IVect2D(100,10), idTemp, USize2D(16*3,16*3), true, 10);
     
            Player* p = new Player(aS);
     
            idTemp = IM->loadImage("data/1.bmp", true);
     
            AnimatedSprite* cS = new AnimatedSprite(
                IM->getImageSizeById(idTemp),
                IVect2D(100,10), idTemp, USize2D(16,16), false, 10);
     
            Enemy* e = new Enemy(cS);
     
            entities.push_back(e);
            entities.push_back(p);
     
            return true;
        }
        else return false;
    }
     
    bool World::draw(const Core::IRenderer& r)
    {
        bool bOk = true;
     
        // dessine le terrain
        //r.drawSurface(idCurrentMap, IVect2D(0,16), Rect(pos, USize2D(320, 256)));
     
        std::vector<IEntity*>::const_iterator it;
        for (it = entities.begin(); it != entities.end(); ++it)
        {
            bOk &= (*it)->draw(r);
        }
     
        return bOk;
    }
     
    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;
    }
    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
    class ISprite
        {
            protected:
                USize2D size; // sa taille
                IVect2D pos; // sa position
                int idImage; // son image
                int currentDir; // direction courante (haut, bas, gauche ou droite) du sprite
     
            public:
                ISprite(USize2D _size, IVect2D _pos, int _idImage) : size(_size), pos(_pos), idImage(_idImage), currentDir(0) {};
                virtual ~ISprite() {};
     
                virtual bool update(int direction, int buttons) = 0;
                virtual bool draw(const IRenderer& r) = 0;
     
                IVect2D getPos() const { return pos; }
                USize2D getSize() const { return size; }
                int getIdImage() const { return idImage; }
     
                void setPos(int x, int y) { pos = IVect2D(x,y); }
                void setDir(int dir) { currentDir = dir; }
        };
    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
    class IEntity
    {
        protected:
            Core::ISprite* pSprite; // pointeur sur son sprite
            IMoveBehaviour* pMoveBehaviour; // pointeur sur son type de déplacement
            bool bVisible;
     
        public:
            IEntity(Core::ISprite* _pSprite)
                : pSprite(_pSprite), bVisible(false)
            {
            };
     
            virtual ~IEntity() { delete pSprite; delete pMoveBehaviour; };
     
            virtual bool update(int directions, int buttons)
            {
                pSprite->update(directions, buttons);
                return true;
            }
     
            bool move(int directions)
            {
                return pMoveBehaviour->onMove(*pSprite, directions);
            }
     
            bool draw(const Core::IRenderer& r)
            {
                return pSprite->draw(r);
            }
    };
    1ère question : Est ce que cela choque (ou brise un des principes OO) de passer une référence du Sprite dans le "MoveBehaviour" ? car pour effectuer un déplacement, j'ai besoin d'avoir accès à des attributs du sprite...

    Interface IMoveBehaviour et ses 3 implémentations :
    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
    87
    88
    89
    90
    91
    92
    93
    94
    95
    96
    97
    98
    99
    100
    101
    102
    103
    104
    105
    106
    107
    108
    109
    110
    111
    112
    113
    114
    115
    116
    117
    118
    119
    120
    121
    122
    123
    124
    125
    126
    127
    128
    129
    130
    131
    132
    133
    134
    135
    136
    // Interface IMoveBehaviour
    class IMoveBehaviour
    {
        private:
     
        public:
            IMoveBehaviour() {};
            virtual ~IMoveBehaviour() {};
     
            virtual bool onMove(Core::ISprite& pSprite, int directions) = 0;
     
    };
     
    // pas de déplacement : entité immobile
    class NoMoveBehaviour
        : public IMoveBehaviour
    {
        private:
     
        public:
            NoMoveBehaviour() {};
            ~NoMoveBehaviour() {};
     
            bool onMove(Core::ISprite& pSprite, int directions)
            {
                (void) pSprite;
                (void) directions;
     
                return true;
            }
    };
     
    // déplacement manuel : le Player avec son clavier ou son joystick
    class ManualMoveBehaviour
        : public IMoveBehaviour
    {
        private:
     
        public:
            ManualMoveBehaviour() {};
            ~ManualMoveBehaviour() {};
     
            bool onMove(Core::ISprite& pSprite, int directions)
            {
                int tempX = pSprite.getPos().x;
                int tempY = pSprite.getPos().y;
                int dirTmp = 0;
     
                // si on appuie sur une direction
                if (directions != Core::InputManager::D_NONE)
                {
                    if (Core::InputManager::isPressed(directions, Core::InputManager::D_RIGHT))
                    {
                        tempX++;
                        dirTmp= 2;
                    }
                    if (Core::InputManager::isPressed(directions, Core::InputManager::D_LEFT))
                    {
                        tempX--;
                        dirTmp = 0;
                    }
                    if (Core::InputManager::isPressed(directions, Core::InputManager::D_UP))
                    {
                        tempY--;
                        dirTmp = 3;
                    }
                    if (Core::InputManager::isPressed(directions, Core::InputManager::D_DOWN))
                    {
                        tempY++;
                        dirTmp = 1;
                    }
     
                    // controles des bords simples pour l'instant
                    if (tempX >= 0 && tempY >= 0 && tempX < SCREEN_W && tempY < SCREEN_H)
                    {
                        // on set la nouvelle position
                        pSprite.setPos(tempX, tempY);
                    }
     
                    pSprite.setDir(dirTmp);
                }
     
                return true;
            }
    };
     
    // déplacement automatique : IA pour les ennemies par exemple
    class AutoMoveBehaviour
        : public IMoveBehaviour
    {
        private:
            int tempAlea;
            int dirTmp;
     
        public:
            AutoMoveBehaviour() {};
            ~AutoMoveBehaviour() {};
     
            bool onMove(Core::ISprite& pSprite, int directions)
            {
                (void)directions;
     
                int tempX = pSprite.getPos().x;
                int tempY = pSprite.getPos().y;
     
                // sélection de la direction aléatoirement
                // si il a choisie une direction,
                // il doit avancer de 2 carrés au moins soit 2*16 pixels
                if (tempAlea >= 2*16)
                {
                    tempAlea = 0;
     
                    // choisie une nouvelle direction
                    dirTmp = rand()%4;
                }
     
                if (dirTmp == 0) tempX--;
                if (dirTmp == 1) tempY++;
                if (dirTmp == 2) tempX++;
                if (dirTmp == 3) tempY--;
     
                // controles des bords simples pour l'instant
                if (tempX >= 0 && tempY >= 0 && tempX < SCREEN_W && tempY < SCREEN_H)
                {
                    pSprite.setPos(tempX, tempY);
                    pSprite.setDir(dirTmp);
                    tempAlea++;
                }
                else // de même si il sort de l'écran
                {
                    tempAlea = 2*16;
                }
     
                return true;
            }
    };
    2ème question : Etant donné que j'utilise une map "masque" pour savoir si une entité peut se déplacer ou pas (exemple : couleur blanc = sol, couleur rouge = murs, bleu = trou...), vais-je être obliger passer l'identifiant de cette map en paramètre depuis IEntity jusqu'à *MoveBehaviour ?

    3ème question : Maintenant, le problème que j'ai c'est pour gérer les collisions, étant donné que j'ai besoin de savoir pour lé déplacement des entités si j'ai des collisions ou pas (avec un murs, une autre entité, un trou ou autre), comment vais-je faire ?

    J'aurais vraiment besoin d'un coup de pouce pour me débloquer

    Merci
    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. #7
    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
    Petite remarque préliminaire : justement, ce n'est pas à World de déplacer le joueur.
    Pour ma part je te recommande de créer un PlayerBehaviour qui se substituera à l'IA pour la classe Player. D'autre part, ta fenêtre devrait mettre à jour à chaque frame une classe "Controller" dans laquelle seront stockés les états des différents boutons (A, B, haut, bas, etc... Vrai si pressé durant la frame, faux sinon). PlayerBehaviour n'interagira qu'avec Controller et aura la responsabilité de déplacer Link en fonction des boutons pressés. Controller est un singleton avec un membre statique exposant l'unique instance.
    Note : si le fps est faible, mieux vaut envisager une variante qui prendrait en compte la durée pendant laquelle le bouton a été pressé, la pompe à messages étant alors dans un thread de plus haute priorité.

    1./ Ce qui serait préférable à mon avis c'est qu'Entity ait lui-même une position et qu'il mette à jour la position de Sprite au début de draw (le système de coordonnées des deux positions peut être différent d'ailleurs). Il n'y aurait aucun problème à passer ton entité en argument à tes comportements (un comportement n'est couplé qu'aux classes entités de base, sauf peut-être les comportements très spécifiques liés à une classe-entité spécifique).

    2./ Ta carte devrait selon moi être encapsulée dans un singleton Map (ou un singleton ScreenMap pour une carte bornée à l'écran visible) afin d'éviter d'avoir à la passer sans cesse.

    3./ Si tu fais un déplacement au pixel près, Entity doit typiquement exposer un getter pour un bounding rectangle. Si tu te contentes d'un déplacement par case (je ne parle pas de l'animation qui sera toujours au pixel près, je parle du fait de savoir si une pression infime sur la touche "haut" fait déplacer Link d'un pixel ou amorce un glissement d'une case), la position suffit.
    Note1 : en réalité, je viens de réaliser que le sprite de Link, par exemple, grandit quand il frappe. Donc il faut dans tous les cas exposer un bounding rectangle.
    Note2 : une entité peut être composée de plusieurs sprites, attention.
    Note3 : retour sur la note 1, un contact avec l'épée n'est pas identique à un contact avec le corps. Du coup, une entité doit exposer plusieurs rectangles de collisions avec chacun un comportement différent.

    Maintenant, pour le détail, chaque entité va d'abord scanner les autres entités pour voir si l'une d'elle est en collision. Vu le peu d'entités, la force brute conviendra très bien. Quant aux collisions avec les murs, il faut pour ça interroger la map et cela peut être fait à plusieurs moments et de plusieurs façons (soit on vérifie à chaque déplacement envisagé s'il est licite, soit on stocke les déplacements envisagés sans vérifier et, juste avant le dessin, on réalise le déplacement avec les éventuelles corrections nécessaires : annulation, rebond, changement d'orientation, etc).
    Note : un cas intéressant à considérer est celui d'un serpent composé de plusieurs sprites qui bute contre un mur et rebrousse chemin.

    Autre chose à trancher : les collisions sont-elles détectées deux fois (A détecte sa collision avec B et ce dernier en fait de même de son côté), ou bien une seule fois, ce qui implique dans ce cas une forme de négociation entre A et B pour savoir lequel des deux effectue le traitement (typiquement on commence avec l'un des deux, qui passe ou non le relais à l'autre) ?

    EDIT : Ajout de notes importantes.

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