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 :

Question conception d'un jeu 2d en C++


Sujet :

C++

  1. #1
    Membre confirmé
    Homme Profil pro
    Inscrit en
    Mai 2010
    Messages
    69
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Localisation : Canada

    Informations forums :
    Inscription : Mai 2010
    Messages : 69
    Par défaut Question conception d'un jeu 2d en C++
    Bonjour tout le monde,

    Je suis présentement en train de créer un petit jeu 2d de plateforme à la Mario Bros.
    Bien sûr avant de programmer, j’ai décidé de me lancer dans la conception de mon jeu. Je vous expose donc mes problèmes (interrogation) ou j’aimerai avoir un peu d’aide ou du moins une piste vers où je pourrai regarder pour pouvoir me débrouiller seul.

    1) Les collisions

    J’ai de la difficulté à voir comment je pourrai gérer les collisions. Je sais bien comment savoir si 2 choses ont une collision entre elles, cependant, je ne sais pas comment les faire interagir ensemble.
    Par exemple : le personnage touche un ennemi sur la tête. L’ennemi doit donc mourir, est- ce que je me crée une méthode ‘’Mort()’’ dans chaque classe ennemie que j’appelle lorsqu’il y a eu collision sur le dessus.
    De plus supposions, qu’un ennemi particulier à des piquants sur la tête. Donc quand notre personnage va sauter sur la tête de l’ennemi, il ne doit pas mourir, mais faire mal à notre personnage.

    J’aimerai avoir des pistes de solution sur comment gérer cela. J’ai donné mes exemples avec un personnage et un ennemi, mais cela s’applique pour tous les autres.
    (Perso/item, Ennemi/bloc, etc)

    2) Les items
    Chaque item fait quelque chose de particulier à notre personnage. (Par exemple : fleur permet de lancer des boules de feux, étoile rend invincible, etc)
    Comment gérer cela dans les classes ? Des boolean qui indiquent par exemple s’il est invisible, s’il peut lancer des boules de feux?

    3) J’aimerai faire mon jeu sur le modèle MVC pour pouvoir séparer l’affichage du reste.
    Cependant, j’ai quelque difficulté à voir comment l’implémenter. Pour l’instant dans mon modèle de personnage, j’ai un statut qui contient par exemple Walking, jumping, falling, etc. Qui signifie l’action que le personnage fait. Dans la vu que vais lire ce statut et afficher l’image en conséquence.
    Est-ce correct de s’y prendre ainsi? Avez-vous de meilleures solutions à proposer ?

    PS : si vous avez des liens parlant du modèle MVC et comment l’implémenter je suis preneur. J’ai trouvé quelque lien, mais il n’expliquer pas tous bien comment cela marche.


    Merci beaucoup à ceux qui vont bien vouloir m’aider et qui ont eu le courage lire ce long texte
    Pour l’instant j’aimerai principalement avoir des pistes sur les collisions puisque c’est ce qui me pose le plus de problème.

    Je poste sur le forum C++, car mon jeu sera fait en C++ avec SFML, si je ne suis pas dans le forum, merci de déplacer ce sujet

  2. #2
    Membre expérimenté Avatar de Dalini71
    Homme Profil pro
    Étudiant
    Inscrit en
    Février 2008
    Messages
    181
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Âge : 38
    Localisation : France, Paris (Île de France)

    Informations professionnelles :
    Activité : Étudiant
    Secteur : High Tech - Éditeur de logiciels

    Informations forums :
    Inscription : Février 2008
    Messages : 181
    Par défaut
    Bonjour,

    C'est une bonne chose de réfléchir à la conception avant de coder

    Je te conseil de te documenter sur les Design Pattern (State et Observer en particulier), ils répondront à pas mal de tes problèmes.

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


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

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

    Informations forums :
    Inscription : Mai 2008
    Messages : 27 066
    Billets dans le blog
    143
    Par défaut
    Bonjour,

    Pour l'instant vos réflexions semblent assez bonnes.
    La réponse de Dalini71 est une solution possible pour les collisions.
    Sinon, on peut faire une boucle dans la méthode update du monde, qui informe de la collision entre deux objets et qui en appelle des méthodes spécifiques des objets. Mais le observer peut aussi le faire, d'une manière surement plus belle.

    Sinon, juste pour information, car votre poste n'est pas spécialement mal placé, le forum possède une section 2D / 3D / Jeux vidéo et je vous invite à présenter votre jeu là bas .
    Vous souhaitez participer à la rubrique 2D/3D/Jeux ? Contactez-moi

    Ma page sur DVP
    Mon Portfolio

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

  4. #4
    Membre Expert

    Homme Profil pro
    Développeur informatique
    Inscrit en
    Septembre 2007
    Messages
    1 895
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Âge : 49
    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
    Par défaut
    Pour le coup, et contrairement à mes saines et figées habitudes, je préconise le contraire : laisser émerger le design à partir des complexités du code initial.

    Donc, pour faire plus clair, coder le système sans designer, puis généraliser, puis re-généraliser, etc, de manière à aboutir au design final.

    Sinon, une solution est d'utiliser un modèle basé sur les composants (on a N composants qui s'envoient des messages: par exemple, le composant X boradcast le message CheckCollision, le composant Y lui réponds CollideWithMe, et X appelle Y.handle_collision(this) pour traiter la collision (X peut être le PJ, une balle, ... bref; un composant qui se déplace ; Y peut être n'importe quel composant, y compris un composant passif ou un autre composant actif).
    [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.

  5. #5
    Rédacteur/Modérateur
    Avatar de JolyLoic
    Homme Profil pro
    Développeur informatique
    Inscrit en
    Août 2004
    Messages
    5 463
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Âge : 50
    Localisation : France, Yvelines (Île de France)

    Informations professionnelles :
    Activité : Développeur informatique
    Secteur : High Tech - Éditeur de logiciels

    Informations forums :
    Inscription : Août 2004
    Messages : 5 463
    Par défaut
    Tiens, pourrais tu développer les raisons qui provoquent en toi cette envie ?

    A part ça, pour tout ce qui est collision, un besoin récurrent et non couvert par le langage est la notion de multi-méthode.
    Ma session aux Microsoft TechDays 2013 : Développer en natif avec C++11.
    Celle des Microsoft TechDays 2014 : Bonnes pratiques pour apprivoiser le C++11 avec Visual C++
    Et celle des Microsoft TechDays 2015 : Visual C++ 2015 : voyage à la découverte d'un nouveau monde
    Je donne des formations au C++ en entreprise, n'hésitez pas à me contacter.

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

    Informations professionnelles :
    Activité : aucun

    Informations forums :
    Inscription : Octobre 2004
    Messages : 11 635
    Par défaut
    Salut,

    J'irais sans doute même plus loin qu'Emmanuel, mais en étant passé directement à la phase de généralisation (j'ai un peu réfléchi au problème )...

    De mon avis, le terme "collision" est suffisamment général pour s'appliquer à bien plus qu'entre toi et un "ennemi": il peut en survenir soit entre un élément "immobile" et un élément mobile, soit entre deux éléments mobiles, mais, quoi qu'il en soit, une collision sera... toujours une collision entre deux objets.

    Cela implique qu'il y aura "interaction" entre ces deux objets, et que le résultat dépendra essentiellement de la nature de chacun d'eux

    Il y a en effet énormément de chances pour qu'un "ennemi" réagisse différemment à une collision avec un boulet de canon ou avec un "bonus" que toi, et que tu réagiras toi même différemment à une collision avec un mur qu'un boulet de canon rentrant en collision avec le même mur.

    Et nous pourrions en dire de même pour le deuxième "objet" intervenant dans la collision, étant donné que j'ai fait remarquer qu'il s'agissait d'une inter action (chaque élément agissant d'une manière particulière sur l'autre )

    Nous pourrions donc partir d'une situation dans laquelle il existe trois grande hiérarchies de classes distinctes représentant les différents objets susceptibles de rentrer en collision :
    • les joueurs (toi, les PNJ éventuels (les personnages qui ne sont pas tes ennemis, mais qui font juste "partie du décors" ), et les ennemis)
    • Les objets mouvants non joueurs (les bonus qui s'écartent, les obus de canon, etc)
    • les objets immobiles (tout ce qui fait partie du décors et qui ne bouge pas)
    Comme j'ai dit que rien ne ressemble plus à une collision qu'une autre collision, il faudrait faire en sorte que la collision soit en mesure de fournir le "contexte" dans lequel elle s'est produite. Cela revient à faire en sorte que la collision soit en mesure d'indiquer à chacun des deux éléments qui se sont rencontrés le genre d'élément qu'il a rencontré

    Nous aurions donc une quatrième grande hiérarchie reprenant les cinq possibilités de collision à envisager:
    • joueur avec joueur
    • joueur avec objet mouvant
    • joueur avec objet immobile
    • objet mobile avec objet mobile
    • objet mouvant avec objet immobile
    pour chaque type de collision, tu peux prévoir un ordre de priorité dans les réactions : le joueur réagira avant un objet mobile qui réagira lui-même avant un objet immobile.

    Tu pourrais d'ailleurs décider d'approfondir cette notion de priorité en disant que le joueur (toi) a priorité sur les ennemis qui ont eux meme priorité sur les PNJ, voire (mais on sort du contexte "mario") de baser le calcul de priorité complexe (basé sur le niveau, la rapidité, la force ou... l'age du capitaine), lorsque deux éléments "similaires" (faisant partie de la même hiérarchie de classes) entrent en collision.

    Le système qui s'occupe de détecter les collision aurait alors la lourde responsabilité de créer la collision "ad hoc" et de l'envoyer à l'élément ayant la priorité de réaction la plus élevée.

    Chaque élément réagirait comme un visiteur à la collision (qui jouerait le role de "visité"), à ceci près que l'on n'a pas *vraiment* besoin de partir de l'objet visité.

    Au final, cela pourrait ressembler à quelque chose comme
    /* la hiérarchie "Collision" */
    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
    class Collision /* la classe de base */
    {
        public:
            virtual ~Collision() = 0; // seule fonction réellement indispensable
                                  // virtuelle pure pour qu'il n'y ait pas moyen de 
                                  // l'instancier
     
             void transmit(){doTransmit();}
         private : 
             virtual void doTransmit()
             {
             }
    };
    /* un petit coup de template pour se faciliter l'écriture :D */
    template <typename FirstType, SecondType = FirstType>
    class TCollision
    {
        public:
            TCollision(FirstType * f, SecondType * s):first_(f), second_(s){}
            virtual ~TCollision(){}
            /* l'accès à l'élément ayant la priorité de réaction la plus élevée */
            FirstType & first() {return first_;}
            /* l'accès à l'élément ayant la priorité de réaction la moins élevée */
            SecondType & first() {return second_;}
     
        private:
            FirstType * first_;
            SecondType * second_;
            virtual void doTransmit()
            {
               first_->collide(this);
               second_->collide(this);
            }
    };
    /* les typedef prenant les différentes possibilités en compte, pour
     * faciliter l'utilisation
     */
    typedef TCollision<Player> CollisionTwoPlayers;
    typedef TCollision<Player, MovingObject> CollisionPlayerMovingObject;
    typedef TCollision<Player, ImmobileObject> CollisionPlayerImmobileObject;
     
    typedef TCollision<MovingObject> CollisionTwoMovingObject;
    typedef TCollision<MovingObject, ImmobileObject> CollisionMobileObjectImmobileObject;
    /* une collision entre deux objets immobiles n'arrivera jamais :D
    La hiérarchie de classes Player prendrait donc la forme d'un visiteur de collisions utilisant le pattern NVI (un peu aménagé pour éviter de devoir implémenter chaque fois les cinq fonctions, même si un joueur ne fait rien dans certains cas ) et ressemblerait à
    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
    class Player
    {
        public:
            Player();
            virtual ~Player() = 0;
            /* la fonction a appeler par le système de détection de collisions */
            void collide(Collision * col)
            {
                doCollide( *col);
            }
        /* d'autres fonctions, sans doute :D */
        protected : 
            /* pour la classe de base, on ne fait rien par défaut 
             * il n'y a, a priori, pas à traiter le cas d'une collision
             * entre un objet mobile et un objet immobile, ni
             * entre deux objets mobiles ;)
             */
            virtual void doCollide(CollisionTwoPlayers &){}
            virtual void doCollide(CollisionPlayerMovingObject &){}
            virtual void doCollide(CollisionPlayerImmobileObject &){}
            virtual void doCollide(CollisionTwoMovingObject&){}
    };
    class Gamer : public Player
    {
        public:
            Gamer (/* ... */);
            virtual ~Player();
            void collide(Collision * col)
            {
                doCollide( *col);
            }
        /* d'autres fonctions, sans doute :D */
        protected : 
            /* il ne faut réimplémenter que les fonctions qui 
             * nécessite une réaction de notre part :D
             * l'idée générale étant de mettre en place
             * la logique qui devra etre suivie :D
             */
            virtual void doCollide(CollisionTwoPlayers &)
            {
                /*...*/
            }
            virtual void doCollide(CollisionPlayerMovingObject &)
            {
                /*...*/
            }
            virtual void doCollide(CollisionPlayerImmobileObject &)
            {
                /*...*/
            }
            virtual void doCollide(CollisionTwoMovingObject&)
            {
                /*...*/
            }
    };
    /* idem pour les ennemis et les pnj, s'il y en a */
    Les hiérarchies d'objets mobiles et d'objet immobiles suivraient le même principe en veillant à ne fournir le pattern nvi que pour les collisions qui sont susceptibles d'arriver parmis les cinq cas que l'on a définis plus haut

    Il nous manquerait, peut etre, enfin, une "fabrique de collision" qui permettrait au détecteur de collision de ne pas s'occuper lui-même de la création de celles-ci (le fameux principe ORP (One Responsability Principle), et il (le détecteur de collision) pourrait donc ressembler à quelque chose qui serait fort 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
    class CollisionDetector
    {
         public:
            /* j'aime les template pour éviter de devoir réécrire
             * vingt fois la même chose :D
             template <typename FirstType, SecondType>
             static void detect(FirstType * one, SecondType * second)
             {
                 /*logique de détection de collision */
                /* logique de sélection de priorité */
               sendCollision(mostPriorItem, secondPriorItem);
             }
        private:
              template <typename FirstType, SecondType>
             void sendCollision(FirstType * mostPriorItem, SecondType * secondPriorItem)
             {
                 Colllision * col = CollisionFactory::createCollection(mostPriorItem, secondPRior);
                 col->transmit();
             }
    };
    Ce n'est qu'une idée qui mériterait plus grande réflexion, mais je crois que tu as là une base qui devrait pouvoir évoluer assez rapidement...

    A ceci près qu'il pourrait même être intéressant de prévoir se supprimer la responsabilité de visiteur des hiérarchies de classes, car les élements qui entrent en collision ont très certainement déjà une responsabilité qui leur est propre, et cela devrait leur suffire
    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

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


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

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

    Informations forums :
    Inscription : Mai 2008
    Messages : 27 066
    Billets dans le blog
    143
    Par défaut
    Je vais me permettre une remarque

    Sur ce code
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    virtual void doCollide(CollisionTwoPlayers &){}
    virtual void doCollide(CollisionPlayerMovingObject &){}
    virtual void doCollide(CollisionPlayerImmobileObject &){}
    virtual void doCollide(CollisionTwoMovingObject&){}
    Le jour ou vous allez vouloir gérer une nouvelle collision, entre 2 FlyingObject par exemple, vous allez devoir rajouter des fonctions partout dans votre code (ce qui peut être lourd, non?)
    Alors oui, vous aurez le compilateur comme aide pour vous dire que vous oublier une implémentation, donc y a cette avantage. Mais je trouve pour autant qu'après quelques types de collision, cela va vraiment alourdir le code.

    N'y a t-il pas une façon plus dynamique de faire. Notamment, il se peut qu'il y ai duplication de code si deux méthodes doivent implémenter le même comportement (sauf si le cast est possible entre CollisionPlayerImmobile et CollisionPlayerMoving, mais cela ne me semble pas castable).

    Pour le système de message, cela peut être bien (genre, on apprend qu'il y a eu collision entre un objet et un autre. Le premier objet recevant le message, connaitra l'objet qui collide avec le pointeur sur l'autre). Par contre, c'est souvent très dangereux au niveau de la mémoire et de la validité des pointeurs (donc, pointeurs intelligent à la rescousse, j'imagine)

    Voilà pour mon point de vue (moi je suis une des entités qui peut se tromper )
    Vous souhaitez participer à la rubrique 2D/3D/Jeux ? Contactez-moi

    Ma page sur DVP
    Mon Portfolio

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

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

    Informations professionnelles :
    Activité : aucun

    Informations forums :
    Inscription : Octobre 2004
    Messages : 11 635
    Par défaut
    Citation Envoyé par LittleWhite Voir le message
    Je vais me permettre une remarque

    Sur ce code
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    virtual void doCollide(CollisionTwoPlayers &){}
            virtual void doCollide(CollisionPlayerMovingObject &){}
            virtual void doCollide(CollisionPlayerImmobileObject &){}
            virtual void doCollide(CollisionTwoMovingObject&){}
    Le jour ou vous allez vouloir gérer une nouvelle collision, entre 2 FlyingObject par exemple, vous allez devoir rajouter des fonctions partout dans votre code (ce qui peut être lourd, non?)
    Alors oui, vous aurez le compilateur comme aide pour vous dire que vous oublier une implémentation, donc y a cette avantage. Mais je trouve pour autant qu'après quelques types de collision, cela va vraiment alourdir le code.
    Le fait est que ton FlyingObject sera soit un FlyingPlayer (héritant de Player si c'est un joureur qui peut voler), soit un FlyingObject (héritant de MovingObject)...

    De plus, la collision est là pour apporter le contexte du moment où elle est survenue, il n'y aura donc à priori pas énormément de cas à rajouter, car la collision n'a même pas besoin de savoir exactement quel est le type (dynamique) des objets qui entrent en collision.

    Ceci est l'exemple parfait de l'importance que peut revêtir le fait de choisir correctement la granularité de tes classes de base :

    J'aurais pu regrouper la classe "joueur" et la classe "objet mobile" au prétexte que tous les deux sont capables de bouger, mais ce serait alors devenu une hiérarchie bien plus importante et j'aurais sans doute été tenté de multiplier les contextes de collision là où il n'y a pas vraiment lieu de le faire

    En partant du principe qu'on a le vivant, le non vivant mais mobile et l'immobile (et non vivant), on arrive à couvrir tous les cas de figure, en n'ayant que cinq contextes potentiels (une fois retiré celui qui n'arrivera jamais ), dont certains ne regarderont jamais une hiérarchie donnée

    Le fait que la collision d'un objet mobile avec un mur fasse apparaitre un bonus (mobile) ne fera pas intervenir le joueur dans la collision en elle-même, et c'est quand le joueur rentrera en collision avec le bonus qu'une autre collision (impliquant un objet mobile et un joueur) sera créée afin de gérer le cas

    De cette manière, si même tu trouves la possibilité de créer 150 type de personnage, autant d'éléments de décors fixe et encore autant d'éléments de décors mobiles, tu en restera toujours à... cinq contextes reprenant l'ensemble des possibilités
    N'y a t-il pas une façon plus dynamique de faire. Notamment, il se peut qu'il y ai duplication de code si deux méthodes doivent implémenter le même comportement (sauf si le cast est possible entre CollisionPlayerImmobile et CollisionPlayerMoving, mais cela ne me semble pas castable).
    Tu penses sans doute au fait de pousser sur un champignon ou de casser un tronçon de mur et de voir apparaitre un bonus de vie ou autres...

    Je te rassure, le cas est prévu : la collision n'agit que sur deux objets particuliers. Le fait que, en réaction à la collision, l'objet "passif" (ou plutôt subissant la collision, par opposition à l'objet qui la provoque / qui en tire profit) libère un autre objet, d'une hiérarchie potentiellement différente, n'impliquera rien d'autre que le fait de créer une nouvelle collision adaptée au moment où le jour atteindra l'objet libéré
    Pour le système de message, cela peut être bien (genre, on apprend qu'il y a eu collision entre un objet et un autre. Le premier objet recevant le message, connaitra l'objet qui collide avec le pointeur sur l'autre). Par contre, c'est souvent très dangereux au niveau de la mémoire et de la validité des pointeurs (donc, pointeurs intelligent à la rescousse, j'imagine)
    (ca me fait que j'ai oublié un delete dans la fonction doTransmit ) Les pointeurs intelligents sont une solution, une relation "collection / élément" avec une classe responsable de la durée de vie des différents objets issus d'une hiérarchie donnée (voir une variable globale plus ou moins adroitement géguisée) peut en être une autre : Ce n'est, de toutes manière, ni à la collision, ni à l'objet qui est entré en collision avec un autre, ni au détecteur de collision de veiller à la libération des ressources autres que les siennes...

    Par contre, on peut (et on devrait) prévoir le fait que certains comportements de nos objets sont susceptibles d'en provoquer "la mort" (le fait qu'il ne soit plus utile de les garder en mémoire, parce qu'ils sont mort ou qu'ils ont été "cassés").

    Lorsque le comportement arrive dans une situation où l'objet n'est effectivement plus utile (car cassé ou mort), c'est l'objet lui-même qui signale qu'il est devenu inutile au gestionnaire de ressources dont il dépend, qui décidera de le détruire (au sens de appeler delete ) où de le garder "pour un usage ultérieur" (en remettant certains membre "à place" )
    Voilà pour mon point de vue (moi je suis une des entités qui peut se tromper )
    Mais il est très instructif aussi... et je n'ai jamais dit que je ne me trompais pas non plus, mais, l'un dans l'autre, je crois sincèrement être dans la bonne logique, et avoir envisagé, si pas tous les cas de figure, au moins une grande partie
    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

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


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

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

    Informations forums :
    Inscription : Mai 2008
    Messages : 27 066
    Billets dans le blog
    143
    Par défaut
    N'est ce pas un peu lourd ?

    Dans le sens, vos fonctions sont appelées même si elles ne font rien. On va avoir beaucoup d'objets actifs. Si j'ai un allié et que je suis le Gamer, la fonction de contact des deux joueurs va devoir ne rien faire, mais elle sera tout de même appelé deux fois (une fois pour l'objet prioritaire, une fois dans le cas inverse (non prioritaire) (si je ne me trompe pas)).

    Un autre point important, c'est à quel moment allons nous choisir de changer l'état d'un élément suite à la collision.
    Donc, joueur tue un ennemi, d'une manière ou d'une autre. La collision apparait pour le joueur, avec en contact un ennemi. Ici, je ne vais pas changer l'état de l'ennemi, car ce n'est pas à la classe Joueur de le faire. Donc il faudra bien que la collision soit déclencher dans l'autre sens (Ennemie collision un joueur). Et là, l'ennemi va changer son état pour être mort.

    Cela me fait penser aussi, qu'il faut aussi transporter un état de collision. Prenons Mario (car nous sommes radicalement parti sur ce jeu, alors qu'un shoot em up, ce gère différemment). Si je saute au dessus d'un koopa, il va mourir. Par contre, si je le touche par dessous, c'est moi qui meurt.
    Le problème, c'est que ce test (dessus / dessous) est à faire pour toute les collisions, et dans les deux sens. Pourquoi (d'après moi), car si on essayait de limiter la casse, et que l'on passe l'information avec la collision, le calcul serait fait pour n'importe quel élément du jeu, ce qui est fortement inutile (les éléments immobile ne s'intéresse pas à cette histoire de dessus/dessous). Par contre, si je suis le principe, cela veut donc dire que cette information, je vais la trouver dans mon doCollide des objets en collision. Donc c'est fait deux fois (répétition de code?).
    Vous souhaitez participer à la rubrique 2D/3D/Jeux ? Contactez-moi

    Ma page sur DVP
    Mon Portfolio

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

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

    Informations professionnelles :
    Activité : aucun

    Informations forums :
    Inscription : Octobre 2004
    Messages : 11 635
    Par défaut
    Citation Envoyé par LittleWhite Voir le message
    N'est ce pas un peu lourd ?

    Dans le sens, vos fonctions sont appelées même si elles ne font rien. On va avoir beaucoup d'objets actifs.
    Je ne penses pas :

    Pour savoir si un objet doit interagir avec un autre, tu aurais besoin de savoir de quel type réel sont les deux objets, ce qui t'obligerait à envisager toutes les combinaisons possibles, et il y en a potentiellement beaucoup (tu ne sais pas combien de classes dériveront au final de Player ou de Mobile) !

    De plus, ce n'est pas la responsabilité du détecteur de collision, et tu aurais donc besoin d'une classe supplémentaire pour y arriver.

    Tu perdrais donc surement beaucoup plus de temps à vérifier si l'un des objets doit réagir (et à plus forte raison si les deux doivent réagir) qu'à simplement appeler une fonction qui ne fera rien.
    Si j'ai un allié et que je suis le Gamer, la fonction de contact des deux joueurs va devoir ne rien faire
    Ou cela peut modifier l'état de ton allier, en diminuant son appréciation vis à vis de toi, et qu'il finisse par t'attaquer...
    <snip>

    Un autre point important, c'est à quel moment allons nous choisir de changer l'état d'un élément suite à la collision.
    Donc, joueur tue un ennemi, d'une manière ou d'une autre. La collision apparait pour le joueur, avec en contact un ennemi. Ici, je ne vais pas changer l'état de l'ennemi, car ce n'est pas à la classe Joueur de le faire.
    Non, mais ce peut etre ennemi qui, d'une manière ou d'une autre, réagit à la réaction de joueur, et se place lui meme dans un état de non réactivité :

    Si ma réaction au contact d'un ennemi est d'attaquer et de retirer lui six points de vie, j'aurai ennemi->defend(degats);(degat étant une classe transportant le contexte propre au dégats (type d'attaque, force, modificateurs éventuels), l'ennemi va calculer le résultat effectif, basé sur ses propres résistances et faiblesses (ou du moins appeler une logique qui s'en chargera ), et se retrouvera avec un nombre de point de vie à soustraire de ceux qu'il lui reste.

    Si la soustraction fait que l'enemi n'est plus capable de réagir (vie < 1), il ne réagira simplement pas, mais cela, ce sera à placer justement dans les points de suspention de doCollide (quitte à ce que ce soit une fonction "alive() qui renvoie vrai ou faux, car l'état "incapable de réagir" sera surement utilisé par ailleurs)
    Donc il faudra bien que la collision soit déclencher dans l'autre sens (Ennemie collision un joueur). Et là, l'ennemi va changer son état pour être mort.
    comme je l'ai dit, l'état est à changer en réaction à la réaction de l'objet prioritaire, et à tester au moment où c'est à l'objet de réagir à la collision
    Cela me fait penser aussi, qu'il faut aussi transporter un état de collision. Prenons Mario (car nous sommes radicalement parti sur ce jeu, alors qu'un shoot em up, ce gère différemment). Si je saute au dessus d'un koopa, il va mourir. Par contre, si je le touche par dessous, c'est moi qui meurt.
    Tu auras remarqué que la première chose dont j'ai parlé, c'est de la priorité d'action, que j'ai donné des exemples simples (basés sur le type de hiérarchie) mais que j'ai aussi dit que cela pouvait être un calcul beaucoup plus complexe...

    Cela pourrait parfaitement être basé sur "celui qui arrive par au dessus sur l'autre"
    Le problème, c'est que ce test (dessus / dessous) est à faire pour toute les collisions, et dans les deux sens. Pourquoi (d'après moi), car si on essayait de limiter la casse, et que l'on passe l'information avec la collision, le calcul serait fait pour n'importe quel élément du jeu, ce qui est fortement inutile (les éléments immobile ne s'intéresse pas à cette histoire de dessus/dessous).
    Les éléments immobiles renvoie d'office une priorité basse, mais il n'est pas exclu que, si ton joueur collide, effectivement par en dessous, que ce soit justement un cas où ton joueur devra réagir différemment que s'il était arrivé par au dessus ou par le coté (en cassant l'élément immobile, par exemple).

    C'est pour cela qu'il me semble important de partir du cas de base "ne rien faire" et de se baser sur des états pour que la réaction adaptée soit mise en oeuvre

    Par contre, si je suis le principe, cela veut donc dire que cette information, je vais la trouver dans mon doCollide des objets en collision. Donc c'est fait deux fois (répétition de code?).
    Pas forcément, cela peut n'être qu'un état "neutre / agressif / defensif", à déterminer d'une manière ou d'une autre.

    D'ailleurs, fondamentalement, la collision pourrait aussi mettre le joueur et le PNJ dans un état de "communication" (question / réponse, commerce, indifférent ... )

    Tout cela est à prévoir "par ailleurs", je n'ai donné que la base pour la gestion des collisions en elles même, mais il y a toute la gestion du gameplay à faire autour .

    je n'ai pas le temps maintenant de donner l'exemple de code, car je pars au boulot, mais je tacherai de le faire ce soir
    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

  11. #11
    Membre Expert

    Homme Profil pro
    Développeur informatique
    Inscrit en
    Septembre 2007
    Messages
    1 895
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Âge : 49
    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
    Par défaut
    Citation Envoyé par JolyLoic Voir le message
    Tiens, pourrais tu développer les raisons qui provoquent en toi cette envie ?
    1/ parce que l'OP est relativement débutant en design (sinon, il ne poserais pas la question )

    2/ parce que le design est émergent lorsqu'on crée des applications complexes, que c'est une application complexe, et que donc, il va voir ses erreurs émerger, la correction possible, bref : évoluer en même temps qu'il produit son jeu.

    3/ parce qu'il va voir de visu ce qu'on entends pas système ouvert à l'extension et fermé à la modification

    4/ parce que c'est un bon exercice de design, et de correction de design, qui intervient très tôt dans le cycle de développement, et qui est donc corrigeable rapidement.

    Il doit bien y avoir d'autres raisons, mais c'était hier, et entre temps, j'ai dormi et je n'ai plus la même perception des choses
    [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.

  12. #12
    Inactif  


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

    Informations professionnelles :
    Secteur : Santé

    Informations forums :
    Inscription : Novembre 2008
    Messages : 5 288
    Par défaut
    @koala01
    Il y a 2 choses qui me gène avec ta solution :

    - Au final, on ne supprime pas totalement la nécessité d'utiliser des switch/if et de connaître le type réel des objets. Imaginons par exemple que pour le personnage, on a un état associé + un type d'arme et que pour l'ennemi, on a un état et une armure (on n'est plus totalement dans le cas d'un mario), on va arriver dans le doCollide à faire un switch pour tester les différentes possibilité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
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    virtual void doCollide(CollisionTwoPlayers &player)
    {
       switch (this->arme().type()) // ok, on a un problème d'encapsulation
       { 
          case EPEE: 
          { 
             player.defend(degats);
          } 
          case LASER: 
          { 
             switch (player->armure().type())
             { 
                case BOUCLIER_MIRROIR: 
                { 
                   this->defend(degats);
                } 
                case DEFAULT: 
                { 
                   player.defend(degats);
                } 
             } 
          } 
       } 
    }
    Je vois pas par exemple comment virer le switch pour éviter de devoir tester les types d'armes et le type d'armure et du coup l'interaction entre eux aussi en évitant le switch. On arrive forcement à une hiérarchie très complexe dès que l'on a plusieurs éléments à prendre en compte en même temps pour déduire le type d'interaction qu'il faut appliquer.

    - Le plus gros problème est que la structure est forcement statique : si on veut ajouter un nouveau type d'interaction qui fait autre chose que faire des dégâts, on est obliger de modifier le code C++. Et il n'est pas possible d'ajouter des nouvelles interactions par scripts. Ce qui est parfois dommage pour les jeux.


    Pour le moment, je teste la solution suivante : un dispatcher, c'est finalement une suite de critères qui détermine un action précise. Chaque type de critère (type d'objet, type d'arme, état, etc. peut importe) est contenu dans une collection et identifié par un type ou un id unique dans la collection (un certain nombre de types par défaut sont définit "en dur" mais il est possible d'en ajouter par script). Idem pour les actions possible. Ensuite, je garde dans une table 2D (ou un arbre) la liste des id pour les critères et l'id (ou les id) de l'action à déclencher. La table peut être créé dynamique (c'est un simple vecteur de unsigned), sauvegarder, scripté, etc.

    En pseudo code, on a quelque chose comme ça :
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    36
    37
    38
    39
    40
    41
    42
    43
    // création des critères
    PlayerCollection players;
    players.append(new Gamer);
    players.append(new Mob);
    players.append(new Mob);
    players.append(new Mob);
     
    WeaponCollection weapons;
    weapons.append(new Épée);
    weapons.append(new Marteau);
     
    StateCollection states;
    states.append(new Attente);
    states.append(new Patrouille);
    states.append(new Attaque);
     
    PerceptionCollection perceptions;
    perceptions.append(new Entendre);
    perceptions.append(new Voir);
    perceptions.append(new Toucher);
    // autant de critères que l'on veut
     
    // Création des actions
    ActionCollection actions;
    actions.append(new Patrouiller);
    actions.append(new Attaquer);
    actions.append(new Frapper);
    actions.append(new Assommer);
    actions.append(new Désarmer);
     
    // création de la table
    // la table est un tableau contenant dans l'ordre player_type, weapon_type, state_type et perception_type 
    // mais il n'est pas nécessaire d'avoir tous les critères
    Dispatcher dispatch;
    // Si le joueur fait du bruit et que le mob est en attente, il commence à pattrouiller
    dispatcher.append(GameType, MobType, AllType, AttenteType, EntendreType, PattrouillerAction);
    // Si le mob voit le joueur, il l'attaque
    dispatcher.append(GameType, MobType, AllType, AllType, VoirType, AttaquerAction);
    // Si le mob peut toucher le joueur, il frappe
    dispatcher.append(GameType, MobType, AllType, AttaqueType, ToucherType, FrapperAction);
    // Si en plus, le mob a un marteau, il peut assommer le joueur
    dispatcher.append(GameType, MobType, MarteauType, AttaqueType, ToucherType, FrapperAction);
    //etc.
    La classe Perception permet de définir plusieurs types de perception (par exemple, une zone de toucher circulaire de 1m, une zone d'écoute circulaire de 10m, un zone de vue en cône de 20m à 45°, etc.) mais également d'activer les actions en allant chercher dans le dispatcher les actions à activer en fonction des critères.

    Forcement, c'est pas très "advanced C++" mais c'est très dynamique. Il ne faut modifier le code que si on ajoute une nouvelle collection de critère. Le coût de la création du tableau et des classes n'intervient qu'a l'initialisation (soit directement dans le script d'initialisation, soit dans un fichier de sauvegarde de la table). Le coût pour trouver les actions a lancer n'est pas énorme non plus (surtout que c'est pour du game design, les calculs complexe s'il en a sont quand même en C++ et pas en script... sauf si on le veut ; en gros, il faut un map::find par critère).

    Je sais pas ce que vous pensez de cette structure ?

  13. #13
    Membre confirmé
    Homme Profil pro
    Inscrit en
    Mai 2010
    Messages
    69
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Localisation : Canada

    Informations forums :
    Inscription : Mai 2010
    Messages : 69
    Par défaut
    Bonjour,

    Tous d’abord merci beaucoup d’avoir pris le temps de réfléchir à mon problème.

    @koala01 : Ta solution me semble intéressante, cependant je me posais quelque question :
    Quelle est la différence entre tous les types aux niveaux du code (à part les noms des classes) ?
    Car si j’ai bien compris tous les types d’objet ont besoin des fonctions DoColide et Colide.

    Ensuite, où dois-je placer le test de collision dans ta structure? Dans la classe TColision ?

    Pour le sens de la collision (dessus, dessous, etc) j’avais pensé à me crée un enum du type :

    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
    Enum ColisionSens
    {
    Haut,
    Bas,
    Gauche,
    Droite,
    None
    }

    Ensuite, la fonction qui teste s’il y a une collision renvoie le sens de la collision (en se fiant sur l’objet principal pour dire le sens) ou None s’il n’y a pas de collision. Je passerai cet enum au fonction DoColide et gèrerai les différents, cas avec un switch case sur l'enum.

    J’en reviens toujours à une question. Par exemple si l’ennemi a des piquants qui font mal au personnage lorsqu’il saute dessus, où est-ce que je place la fonction Personnage.Hurt() ? Je crois que cela devrait être dans l’ennemi puisque seul lui sait s’il a des piquants ou non, mais alors il faudrait que je fasse attention de ne pas tuer l’ennemi avant dans le DoColide du personnage puisque lui ne sait pas qu’il se fera mal en sautant dessus (je ne sais pas si vous comprenez bien ce que je veux dire, j’ai un peu de difficulté à l’expliquer).

    De plus, une autre petite difficulté par rapport à vos classes de type. Un ennemi est de type MovingObject tous comme un bloc pourrait l’être. Alors, comment utiliser les fonctions spécifiques aux ennemis par exemple, puisque l’objet qu’on reçoit en paramètre dans le DoColide est un objet de type MovingObjet et non de type ennemi.


    1/ parce que l'OP est relativement débutant en design (sinon, il ne poserais pas la question )
    Effectivement je suis relativement débutant en design et conception, cependant je ne suis pas débutant en programmation, donc pour ce qui est des questions de code je ne devrai pas avoir trop de problème


    Merci encore pour votre aide.

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

    Informations professionnelles :
    Activité : aucun

    Informations forums :
    Inscription : Octobre 2004
    Messages : 11 635
    Par défaut
    Citation Envoyé par gbdivers Voir le message
    @koala01
    Il y a 2 choses qui me gène avec ta solution :

    - Au final, on ne supprime pas totalement la nécessité d'utiliser des switch/if et de connaître le type réel des objets. Imaginons par exemple que pour le personnage, on a un état associé + un type d'arme et que pour l'ennemi, on a un état et une armure (on n'est plus totalement dans le cas d'un mario), on va arriver dans le doCollide à faire un switch pour tester les différentes possibilité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
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    virtual void doCollide(CollisionTwoPlayers &player)
    {
       switch (this->arme().type()) // ok, on a un problème d'encapsulation
       { 
          case EPEE: 
          { 
             player.defend(degats);
          } 
          case LASER: 
          { 
             switch (player->armure().type())
             { 
                case BOUCLIER_MIRROIR: 
                { 
                   this->defend(degats);
                } 
                case DEFAULT: 
                { 
                   player.defend(degats);
                } 
             } 
          } 
       } 
    }
    Je vois pas par exemple comment virer le switch pour éviter de devoir tester les types d'armes et le type d'armure et du coup l'interaction entre eux aussi en évitant le switch. On arrive forcement à une hiérarchie très complexe dès que l'on a plusieurs éléments à prendre en compte en même temps pour déduire le type d'interaction qu'il faut appliquer.
    Tu ne vois pas comment éviter le switch parce que tu donne trop de responsabilités à ta fonction (et donc à ta classe) !!!

    Le joueur doit déjà gérer ses propres états, c'est déjà amplement suffisant, il n'y a aucune raison pour qu'il doive, en plus, gérer les états des autres !!!

    Ce n'est pas au joueur qui attaque de décider du nombre de dégats que l'adversaire va effectivement subir... Et ce n'est surtout pas son rôle d'essayer de savoir quel type d'armure l'adversaire porte!

    Le role de l'attaquant est simplement de déterminer (grace à ses états propres) le nombre et le type de dégats qu'il est capable d'occasionner.

    C'est à l'adversaire de savoir s'il porte une armure, et, si c'est le cas, il devra demander à l'armure (ou aux différents éléments d'armure qu'il porte !) de modifier les dégats subits en fonction de leurs résistances propres.

    Du coté de l'attaquant, les dégats qu'il occasionne sont fonction de son arme (mais une arme est une arme, et c'est donc l'arme qui aura une fonction damage() qui créera une instance de la classe Damage en fonction de sa propre décoration ) et de certains "modificateurs" qui sont propre à son état de santé, à sa force ou à sa race.

    tu commencerais donc par, lorsque la réaction est d'attaquer, par demander à ton arme ou à ton sort (selon l'état dans lequel tu te trouve) de te fournir les dégats "de base", que tu modifie selon les modificateurs "type" propres à ta race ou à ton état de santé.

    Puis, une fois que tu as déterminé avec exactitude les dégats que tu es capable d'occasionner, tu les transmet à ton adversaire, à charge pour lui de savoir comment il va réagir aux dégats que tu essaye d'occasionner

    Cela donnerait quelque chose comme
    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
    class Player
    {
        private:
            virtual void doCollide(CollisionTwoPlayers &player)
            {
                /* on commence par savoir si je suis le premier ou le deuxième
                 * dans la priorité de collision car je dois attaquer l'autre
                 * (je ne peux quand meme pas m'attaquer moi-même :D )
                 */
                Player * other = player.first();
                if(other == this )
                    other = player.second();
                /* et je réagit en fonction de ma posture */
                switch (state_->postureType())
                {
                    /* je peux être dans une phase de commerce... */
                    case postureTrading : 
                       other->trade(this);
                       break;
                    /* je peux être endormi */
                    case postureSleeping:
                        break;
                     /* je peux être dans une posture de défense */
                    case postureDefend : 
                        break;
                     /* je peux être dans une posture d'attaque avec sort */
                    case postureAttackWithSpell:
                        attackWithSpell(other);
                        break;
                     /* ou dans une posture d'attaque avec arme */
                    case postureAttackWithWeapon:
                        attackWithWeapon(other);
                }
            }
            /* virtual */ void postureAttackWithWeapon(Player * ennemy)
            {
               /* je n'ai qu'à créer les dégats que j'inflige, à charge
                * de mon ennemi de voir comment il y réagit :D
                */
               /* je pars des dégats que mon arme me permet d'infliger */
              Damage acurate = weapon_->damages();
              /* je les modifie en fonction de mon état général (force, santé,
               * maladies éventuelles, ou que sais-je);
               */
              acurate = modifyDamage(acurate);
              /* puis je les envoies à l'ennem
               */
              other->defend(acurate);
            }
     
            /* virtual */ void postureAttackWithSpell(Player * ennemy)
            {
               /* je n'ai qu'à créer les dégats que j'inflige, à charge
                * de mon ennemi de voir comment il y réagit :D
                */
               /* je pars des dégats que mon arme me permet d'infliger */
              Damage acurate = spell_->damages();
              /* je les modifie en fonction de mon état général (force, santé,
               * maladies éventuelles, ou que sais-je);
               */
              acurate = modifyDamage(acurate);
              /* puis je les envoies à l'ennemi, à charge pour lui de voir comment
               * il y réagit :D
               */
              other->defend(acurate);
            }
    };
    Dans le pire des cas, tu devra t'inquiéter de savoir s'il s'agit de dégats à distance ou de dégats de contact, car, s'il s'agit de dégats à distance, tu n'es, a priori, pas dans un contexte de collision, mais tu va lancer ton sort (ou ton arme de jet) vers l'adversaire, et créer un "contexte de collision espéré" entre ce que tu as lancé et l'adversaire.

    En choisissant la granularité qui va bien, tu arrive à une situation dans laquele tout ne doit réagir (en cas d'attaque) qu'à un certain nombre (finalement très restreint ) de type de dégats pour en atténuer les effets.
    on pourrait citer :
    • les différents dégâts dus aux différents types de sorts (de terre, d'eau, de feu, d'air, de froid, élementaires, et "mysthiques")
    • les différents dégâts dus aux différents types de coups (contondants, percutant, tranchants, à main nue)
    Et cela peut parfaitement tenir dans un simple tableau d'entiers qui représente l'atténuation des dégats (on peut même en arriver à faire en sorte qu'une armure puisse augmenter les dégats du à un type d'attaque en mettant une valeur négative )

    Tout ce qu'il te faut, c'est une énumération reprenant les types générique de dégats envisagés (le fait que le sort de feu s'appelle "tempête de feu" ou "ouragan incandescent" n'a pas beaucoup d'importance ici ) 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
    enum DamageType
    {
        /* les dégats non magiques */
        noDamage = 0, // aucun dégats
        damageFreeHand , // dégats à main nue
        damageBlunt , // dégats contondants
        damageCrushing, // dégats percutants
        damageCutting, // dégats coupants
       /* les dégats magiques */
        damageFire, // dégats de feu
        damageWater, // dégat d'eau
        damageFrost, // dégats de froid
        damageHeart, // dégats de terre
        damageAir, // degats d'air
        /* les autres types de dégats */
        damageElemental, //degats élémentaires
        damageMisthysism, // dégats mistiques
    };
    Tu aurais une classe damage qui ressemblerait à
    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
    class Damage
    {
        public:
            Damage(int v, DamageType t)
            DamageType type() const{return type_;}
            int value() const{return value_;}
            /* je passe sur la partie qui permet de gérer facilement
             * la liaison de plusieurs dégats successifs (une épée de
             * feu occasionnant des dégats coupants puis des dégats
             * de feu :D ) pour rester simple 
             */
        private:
     
            int value_;
            DamageType type_;
    };
    Tout ce qui est susceptible de diminuer (ou d'augmenter) les dégats présentera une interface proche de
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    class LaClass
    {
        public:
            Damage resistTo(Damage const &);
    };
    Pour une armure, cela pourrait etre proche de
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
     
    Damage Armor::resistTo(Damage const & toResist):
    {
        /* éventuellement gérer les dégats infligés à l'armure elle-même */
        return Damage(resistances_[toResist.type()],toResist.type());
    }
    et prendrait, pour un joueur, la forme 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
    Damage Armor::resistTo(Damage const & toResist):
    {
       /* on part de ma résistance naturelle à un type donné de dégats */
       int resist = resistances_[toResist.type];
       /* qui peut être modifiée par la "posture" dans laquelle je me trouve */
       switch (state->posture().type() )
       {
           case postureAsleep : // si je dors, ma résistance naturelle est vraiment
                                // minime
               resist /=4;
               break;
           case postureTrading : // quand je fais du commerce, elle l'est un peu
                            // moins, mais je reste plus vulnérable que la normale
                resist /= 3;
                break;
            case postureAttacking : // quand j'attaque, je suis encore moins
                                  // vulnérable mais quand meme plus que si je me 
                                  // défendais
                 resist /=2;
                 break;
            case postureDefending : //et quand je me défend, j'ai un bonus de
                                            // protection
                 resist *=2;
                 break;
       }
       return Damage(toResist.value()-resist, toResist.type());
    }
    et tout ce qui est susceptible d'augmenter les dégats aurait une interface proche de Damage augmentDamage(Damage const & );Quand on invoque la fonction "defend" d'un joueur, il commence simplement par modifier les degats qu'on tente de lui occasionner sous une forme proche de
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
    void Player::Defend(Damage const & originalDamage)
    {
        Damage real(originalDamage);
        if(armor_) // si j'ai une armure
           real = armor_->resistTo(real); // l'armure encaisse le choc et l'amoindri
        real = resistTo(real); // mes résistances naturelles entrent en jeu
        live_ -= real.value(); // et je prend le reste dans les gencives  
    }
    Evidemment, j'ai pris le cas particulier d'un adversaire susceptible d'être attaqué, mais ce doit etre considéré comme un comportement adaptable (fonction virtuelle, voire NVI)

    - Le plus gros problème est que la structure est forcement statique : si on veut ajouter un nouveau type d'interaction qui fait autre chose que faire des dégâts, on est obliger de modifier le code C++. Et il n'est pas possible d'ajouter des nouvelles interactions par scripts. Ce qui est parfois dommage pour les jeux.
    Absolument pas...

    A partir du moment où tu as la granularité adéquate pour tes objets (un sort est un sort, quel qu'il soit, une arme, fusse-t-elle enchantée ou à deux mains est une arme et une piece d'armure est une piece d'armure, que ce soit un bracelet, un casque ou un plastron), et où tu te donnes l'occasion d'en décorer certains (histoire de pouvoir avoir l'épée du bon dieu qui est une rapière en crystal sertie de telle et de telle pierre), il ne te manque qu'une fabrique et une collection dans laquelle ranger tous les objets en attendant de les utiliser pour être en mesure de scripter ton jeu!

    Bien sur, il te faut plusieurs gestionnaires distincts (en gros, un par (sous) hiérarchie envisagé), bien sur, l'inventaire n'est pas "simplement" une collection de "objects" mais est composé d'une collection de "Weapons", d'une autre de "Potions", d'une troisième de "Spells" et d'une quatrième de "ArmorItem", mais cela te facilitera au contraire grandement la vie, ne serait-ce que pour pouvoir présenter ton inventaire sous la forme d'onglets, ou pour pouvoir déterminer si tu peux mettre tel ou tel objet à tel ou tel endroit de l'équipement

    Arrange toi pour accéder (éventuellement de manière indirecte) à ta fabrique et à tes gestionnaires dans ton script, et le tour est joué
    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. #15
    Inactif  


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

    Informations professionnelles :
    Secteur : Santé

    Informations forums :
    Inscription : Novembre 2008
    Messages : 5 288
    Par défaut
    En voyant ta réponse, je réalise que je n'ai pas été forcement très clair et que l'exemple choisit n'est pas idéal. En effet, pour l'exemple avec le type d'arme et le type d'armure, j'aurais fait comme toi. Cette configuration ne pose pas de problème.
    Je pensais plus à quelque chose comme le grappin dans zelda, qui pouvait attraper des choses, casser une armure (dans certain cas je crois), assommer quelqu'un (dans certain aussi je crois), déplacer le personnage, activer des interrupteurs ou faire des dégâts.
    Mais il est vrai que je n'ai pas réfléchi non plus à fond à la structure qui réalisait tout ça. Peut être qu'elle est effectivement aussi simple.

    Pour le second point, lorsque je parlais de statique, je ne pensais pas du tout au fait de paramétrer un objet (les dégâts que font une arme, les dégâts encaissés par une armure, etc.) mais à ajouter de nouvelles "fonctionnalités" dans le script. Pour être plus précis, pour éviter que si un game designer à une idée, il faille modifier le code pour l'implémenter.
    Justement, ton exemple d'associer des éléments aux dégâts me correspond à ce qui me chagrine. Pour ajouter cette fonctionnalité, tu as du modifier le code. Si un game designer te dit qu'il veut ajouter des dégâts dans le temps, a priori, le code actuel ne le permet pas. Il faut le modifier. Idem pour toutes nouvelles fonctionnalités. (je sais que j'en demande beaucoup )

    Bref, ta solution est ce que j'aurais fait aussi. Mais je cherchais une plus grande souplesse d'où l'idée que j'ai proposé. Je testerai et je verrai à l'utilisation.

    PS : il me semble que ce problème à déjà était abordé par Emmanuel sur son blog, non ?

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

    Informations professionnelles :
    Activité : aucun

    Informations forums :
    Inscription : Octobre 2004
    Messages : 11 635
    Par défaut
    Citation Envoyé par gbdivers Voir le message
    En voyant ta réponse, je réalise que je n'ai pas été forcement très clair et que l'exemple choisit n'est pas idéal. En effet, pour l'exemple avec le type d'arme et le type d'armure, j'aurais fait comme toi. Cette configuration ne pose pas de problème.
    Je pensais plus à quelque chose comme le grappin dans zelda, qui pouvait attraper des choses, casser une armure (dans certain cas je crois), assommer quelqu'un (dans certain aussi je crois), déplacer le personnage, activer des interrupteurs ou faire des dégâts.
    Mais il est vrai que je n'ai pas réfléchi non plus à fond à la structure qui réalisait tout ça. Peut être qu'elle est effectivement aussi simple.
    Pour le grappin de Zelda, il y a de fortes chances pour que ce soit le type d'objet frappé par lui (ou ses décorations) qui choisisse si le grappin s'y accroche ou non, si le personnage est assommé ou blessé!

    Le reste en découle : si le grappin s'accroche, lorsqu'il se "rembobine", il "tire avec lui" ce qui y est accroché, dans un sens ou dans l'autre
    Pour le second point, lorsque je parlais de statique, je ne pensais pas du tout au fait de paramétrer un objet (les dégâts que font une arme, les dégâts encaissés par une armure, etc.) mais à ajouter de nouvelles "fonctionnalités" dans le script. Pour être plus précis, pour éviter que si un game designer à une idée, il faille modifier le code pour l'implémenter.
    Justement, ton exemple d'associer des éléments aux dégâts me correspond à ce qui me chagrine. Pour ajouter cette fonctionnalité, tu as du modifier le code. Si un game designer te dit qu'il veut ajouter des dégâts dans le temps, a priori, le code actuel ne le permet pas. Il faut le modifier. Idem pour toutes nouvelles fonctionnalités. (je sais que j'en demande beaucoup )
    Attends, tu changes fondamentalement les besoins, il est donc normal que l'analyse générale doive évoluer!!!

    On passe d'un scénario où il n'y a qu'à réagir à la collision à un scénario où l'on doit prendre différents types d'armes, de sorts et de réactions en fonction de différentes situations! ce n'est pas le genre de changement tout à fait anodin!

    Ce qui est sur, c'est que si tu partage correctement les responsabilités et que tu garde une granularité correcte dans tes hiérarchies de classes (que tu évites de partir d'un god object qui va regrouper les personnages, les armes les sorts et les éléments de décors), tu auras beaucoup plus facile à faire évoluer les choses!

    Et, une fois que tu as les bonnes choses au bon endroit, si le game designer décide de donner à un ennemi une arme suprême, il n'y aura qu'à donner les caractéristiques adéquates dans ton script

    En outre, je n'ai fait que préciser un code que j'avais remplacé par des points de suspension à la base, même si cela a nécessité l'ajout de fonctions adaptées (qu'il n'était pas nécessaire de préciser dans mon premier code )

    Après tout, lorsque tu vas démarrer l'analyse de ton jeu, il y a de fortes chances pour qu'elle contienne dans les premières pages :
    Un personnage peut disposer d'un inventaire, dont les différents éléments sont
    • les armes
    • les pieces d'armures
    • les objets à "usage unique" (potion, ...)
    • les objets de quête
    • ...
    et peut disposer de sorts parmi lesquels on peut distinguer les sorts d'attaque et les sorts de défense

    Le personnage peut décider d'utiliser soit une arme, soit un sort lorsqu'il interagit avec quelque chose
    car ca fait partie du gameplay global et général (c'est ce qui fait la différence fondamentale entre un mario et un Ghotic )
    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. #17
    Inactif  


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

    Informations professionnelles :
    Secteur : Santé

    Informations forums :
    Inscription : Novembre 2008
    Messages : 5 288
    Par défaut
    Citation Envoyé par koala01
    Attends, tu changes fondamentalement les besoins, il est donc normal que l'analyse générale doive évoluer!!!
    J'avoue

  18. #18
    Membre confirmé
    Homme Profil pro
    Inscrit en
    Mai 2010
    Messages
    69
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Localisation : Canada

    Informations forums :
    Inscription : Mai 2010
    Messages : 69
    Par défaut
    Bonjour,

    J'aimerai bien avoir quelque explication par rapport à mon post précédant, ce sont des interrogations que je me pose toujours.

    merci beaucoup

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

    Informations professionnelles :
    Activité : aucun

    Informations forums :
    Inscription : Octobre 2004
    Messages : 11 635
    Par défaut
    Citation Envoyé par marcolo21 Voir le message
    Bonjour,

    J'aimerai bien avoir quelque explication par rapport à mon post précédant, ce sont des interrogations que je me pose toujours.

    merci beaucoup
    oui, excuses nous...
    Citation Envoyé par marcolo21 Voir le message
    Bonjour,

    @koala01 : Ta solution me semble intéressante, cependant je me posais quelque question :
    Quelle est la différence entre tous les types aux niveaux du code (à part les noms des classes) ?
    La différence fondamentale est justement leur nom, et surtout la sémantique que tu donne à ce nom!!

    L'héritage est la relation la plus forte qui puisse exister en POO, c'est à dire que c'est celle qui doit être employée le moins souvent possible: elle correspond à la relation EST UN, dans toute la sémantique du terme.

    Tu peux très bien trouver 150 types différents ayant tous une interface identique sans arriver à trouver une classe de base qui soit commune à tous (sans arriver à dire pour chacun des 150 type "un objet de tel type EST UN objet de la classe de base spécialisé)
    Car si j’ai bien compris tous les types d’objet ont besoin des fonctions DoColide et Colide.
    Seules les classes de base ont besoin de la fonction collide (les autres en hériteront !), toutes les classes ont besoin de la fonction doCollide, implémentée en fonction des besoins
    Ensuite, où dois-je placer le test de collision dans ta structure? Dans la classe TColision ?
    Non, dans une structure tout à fait séparée (je l'avais appelée CollisionDetector ) qui va tester la position des différents éléments et voir s'il entrent en collision
    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
    Pour le sens de la collision (dessus, dessous, etc) j’avais pensé à me crée un enum du type :
    
    
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    12345678
    Enum ColisionSens
    {
    Haut,
    Bas,
    Gauche,
    Droite,
    None
    }
    Ensuite, la fonction qui teste s’il y a une collision renvoie le sens de la collision (en se fiant sur l’objet principal pour dire le sens) ou None s’il n’y a pas de collision. Je passerai cet enum au fonction DoColide et gèrerai les différents, cas avec un switch case sur l'enum.
    l'idée de créer une énumération est toujours bonne, l'utilisation dans un switch me semble hasardeuse... je réfléchirai plus longuement quand j'aurai cinq minutes
    J’en reviens toujours à une question. Par exemple si l’ennemi a des piquants qui font mal au personnage lorsqu’il saute dessus, où est-ce que je place la fonction Personnage.Hurt() ? Je crois que cela devrait être dans l’ennemi puisque seul lui sait s’il a des piquants ou non,
    Exactement, tu l'appelle dans sa propre fonction doCollide
    mais alors il faudrait que je fasse attention de ne pas tuer l’ennemi avant dans le DoColide du personnage puisque lui ne sait pas qu’il se fera mal en sautant dessus (je ne sais pas si vous comprenez bien ce que je veux dire, j’ai un peu de difficulté à l’expliquer).
    je comprends très bien ce que tu veux dire ;-) tu n'auras qu'à faire en sorte que ton ennemi ne meure pas lorsque tu appelle la fonction de réaction (à l'action du joueur) (react )
    De plus, une autre petite difficulté par rapport à vos classes de type. Un ennemi est de type MovingObject tous comme un bloc pourrait l’être. Alors, comment utiliser les fonctions spécifiques aux ennemis par exemple, puisque l’objet qu’on reçoit en paramètre dans le DoColide est un objet de type MovingObjet et non de type ennemi.
    Tu penses là aux boulets de canon et autres saloperie du genre, sans doute tu peux très bien créer une classe MovingObjectEnnemi qui hérite de MovingObject, mais pas de EnnemyPlayer (qui elle hérite de Player) qui, lorsqu'il entrera en collision avec (toi ) sera en mesure de te tuer
    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

  20. #20
    Membre confirmé
    Homme Profil pro
    Inscrit en
    Mai 2010
    Messages
    69
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Localisation : Canada

    Informations forums :
    Inscription : Mai 2010
    Messages : 69
    Par défaut
    Citation Envoyé par koala01 Voir le message
    oui, excuses nous...
    Pas de problème, pendant une seconde j'ai cru que mon message ne s'était pas envoyé

    Citation Envoyé par koala01 Voir le message
    La différence fondamentale est justement leur nom, et surtout la sémantique que tu donne à ce nom!!
    Donc en gros les 4 classes sont pareil, ils ne contiennent que des fonctions virtuel (virtuel pure ?). Je peux donc faire un copier coller et changer le nom pour crées les 4 classes.

    Citation Envoyé par koala01 Voir le message
    Non, dans une structure tout à fait séparée (je l'avais appelée CollisionDetector ) qui va tester la position des différents éléments et voir s'il entrent en collision
    Ok tu doit parler de 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
     
    class CollisionDetector
    {
         public:
            /* j'aime les template pour éviter de devoir réécrire
             * vingt fois la même chose :D
             template <typename FirstType, SecondType>
             static void detect(FirstType * one, SecondType * second)
             {
                 /*logique de détection de collision */
                /* logique de sélection de priorité */
               sendCollision(mostPriorItem, secondPriorItem);
             }
        private:
              template <typename FirstType, SecondType>
             void sendCollision(FirstType * mostPriorItem, SecondType * secondPriorItem)
             {
                 Colllision * col = CollisionFactory::createCollection(mostPriorItem, secondPRior);
                 col->transmit();
             }
    };
    Je remarque que tu utilise une Factory. Je ne sais pas exactement ce que c'est (j'avais trouver un lien sur developpez qui l'expliquait un peu), aurais-tu quelque lien qui explique comment c'est fait et à quoi cela sert ?

    De plus, est-ce que c'est la fonction qui teste s'il y a une collision qui doit appeler la fonction sendCollision ?

    Citation Envoyé par koala01 Voir le message
    l'idée de créer une énumération est toujours bonne, l'utilisation dans un switch me semble hasardeuse... je réfléchirai plus longuement quand j'aurai cinq minutes
    Pour l'instant je crois que je vais y allez avec l'enumeration, faute de meilleur idée. Si tu a une meilleur idée, n'hésite pas à la poster.

    Citation Envoyé par koala01 Voir le message
    je comprends très bien ce que tu veux dire ;-) tu n'auras qu'à faire en sorte que ton ennemi ne meure pas lorsque tu appelle la fonction de réaction (à l'action du joueur) (react )
    Alors, je dois crée une fonction pour chaque réaction ? Exemple: Ennemis.ReactJumping(), Ennemis.ReactAttacking(), etc. ???
    Qu'est-ce que je passerai en paramètre à cette fonction ?

    Citation Envoyé par koala01 Voir le message
    Tu penses là aux boulets de canon et autres saloperie du genre, sans doute tu peux très bien créer une classe MovingObjectEnnemi qui hérite de MovingObject, mais pas de EnnemyPlayer (qui elle hérite de Player) qui, lorsqu'il entrera en collision avec (toi ) sera en mesure de te tuer
    En faite je parlai plus dans le sens de si je suis dans la classe personnage et que je suis dans la fonction :

    Code : Sélectionner tout - Visualiser dans une fenêtre à part
     virtual void doCollide(CollisionPlayerMovingObject &)
    Si l'objet reçu en paramètre est un MovingObject, Comment savoir si ce MovingObject est un ennemi et pouvoir utiliser les fonctions spécifiques au ennemis par exemple ? Puisque si je comprend bien les seuls fonctions que MovingObject a son les DoColide, donc s'ils sont des Ennemis, je n'ai pas accès au fonction des ennemis.

    Merci beaucoup

Discussions similaires

  1. Conception d'un jeu de tennis
    Par b Oo dans le forum Développement 2D, 3D et Jeux
    Réponses: 5
    Dernier message: 17/01/2007, 22h19
  2. Difference dates et questions conception
    Par lolo_bob2 dans le forum Modélisation
    Réponses: 2
    Dernier message: 23/11/2006, 13h23
  3. Conception d'un jeu de course
    Par zooffy dans le forum Développement 2D, 3D et Jeux
    Réponses: 5
    Dernier message: 03/11/2006, 19h29
  4. [Conception] Concevoir le jeu Pierre Feuille Ciseau
    Par websurfeur dans le forum Général Java
    Réponses: 14
    Dernier message: 17/03/2006, 19h26
  5. [VB] Aide pour la conception d'un jeu
    Par superbruno dans le forum VB 6 et antérieur
    Réponses: 12
    Dernier message: 17/01/2006, 18h01

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