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

Langage C++ Discussion :

Assigner une fonction à une classe? Pas simple!


Sujet :

Langage C++

  1. #1
    Membre du Club
    Homme Profil pro
    Développeur de jeux vidéo
    Inscrit en
    Juin 2006
    Messages
    122
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Âge : 40
    Localisation : France

    Informations professionnelles :
    Activité : Développeur de jeux vidéo

    Informations forums :
    Inscription : Juin 2006
    Messages : 122
    Points : 59
    Points
    59
    Par défaut Assigner une fonction à une classe? Pas simple!
    Bonjour chers amis z'et amies!

    J'arrive en cette belle journée ensoleillée de fin Aout avec un problème pour vous! Oui! Rien que pour vous! (Bon ok, surtout pour moi. :] )

    Problème que voici :

    Mise en situation :

    Je développe en ce moment un shoot'em up vertical tout à fait oldschool. J'ai mon vaisseau qui balance plein de jolie tirs sur plein d'ennemis qui font boom§

    J'ai modélisé ça avec un ensemble de classe que voici :

    GameObject :
    la classe la plus basique pour mes objets. Elle contient l'image qui représente mes objets sur l'écran de jeu, les positions x et y sur mon plan 2D et une durée de vie. (Elle contient en réalité bien plus, mais je donne les donnée membres qui nous intéresse pour simplifier. =p)

    Ennemy :
    Les ennemis du jeu, qui héritent de GameObject. Ils ont comme donnée membre supplémentaire des points de vie.

    PlayerShot :
    Les tirs du joueur, qui héritent eux aussi de GameObject et ont comme donnée membre un bool "has_hit" pour savoir si le tir à touché un ennemy ou non.

    Pour schématiser, ça se présente comme ça :
    Nom : fonction1.png
Affichages : 73
Taille : 4,0 Ko

    Lorsque le jeu tourne, chaque objet est créé à un instant précis : soit suivant un script qui les ajoute à la scène, soit via certains événements, comme l'appui sur la touche de tirs pour les tirs du joueur, la destruction d'un ennemi pour les explosions et les éventuels bonus à ramasser.

    Tout ce qui vie fini par mourir.

    C'est vrai ici aussi, sinon je mon jeu va calculer les déplacements de tous les objets du niveau, ce qui va demander énormément de mémoire et faire inutilement ramer l'application. Je dois donc les détruire.

    La destruction des objets est gérée par ma plus grosse classe : la classe du niveau (nommée Stage, vu que dans les shoot'em up on parle souvent de stage =p). J'ai donc fait une fonction pour que mes objets puisse dire à mon niveau si ils sont encore utiles ou bon pour la casse.

    J'ai donc rajouté une fonction EOL() pour "fin de vie" à chacun de mes objets. Je l'ai faite virtuelle pour bénéficier de l'héritage :

    Nom : fonction2.png
Affichages : 69
Taille : 6,9 Ko

    On complique le truc :

    Voila j'ai donc mes objets qui peuvent être effacés. Mais ça ne suffit pas!

    En avançant dans ma réflexion, je me suis rendu compte que les conditions pour effacer un objet peuvent être différentes pour chaque instances d'objet.

    Par exemple : certains GameObject sont effacé lorsqu'ils sortes de l'écran de jeu. D'autres lorsqu'ils arrivent en fin de vie uniquement (car ils sont immobiles et ne sortirons jamais de l'écran), d'autres encore qui disparaissent lorsque l'animation de leur image (composée parfois de plusieurs sprites) se termine.

    Je me retrouve donc avec plusieurs conditions différentes, que j'ai besoin de prendre en compte individuellement ou a plusieurs... Et ça complique encore car je n'en ai plus besoin seulement par objet, mais par instances d'objet!

    Je dois donc trouver un moyen de dire à mes instances d'objet de quelle manière ils vont être détruits. J'ai donc cherché un peu, j'ai eu l'idée des pointeurs sur fonction. Vite fais, comme ça, ça paraissait génial.

    Hop je m'exécute. Je rajoute à mon GameObject un pointeur sur fonction, et ce pointeur sera utilisé par ma fonction EOL() pour renvoyer le bool qui convient, et j'y fais un constructeur approprié pour assigner quelque chose à mon pointeur:

    Nom : fonction3.png
Affichages : 72
Taille : 8,5 Ko

    Bon, c'est là que les problèmes commence : comment préparer mes fonctions de condition?

    Elles doivent pouvoir accéder aux donnée membre de l'objet auquel elles sont appliquées, alors je les fourres connement dans la classe sur laquelle elles doivent faire des vérification.

    Aller j'en fais 4 :
    2 pour le GameObject -> OutOfScreen() pour ceux qui sortent de l'écran, et NoMoreLife() pour ceux qui arrivent en fin de vie.
    1 pour l'Ennemy -> IsDestroyed() pour les ennemis dont la vie arrive à 0 ou en dessous.
    1 pour le shot -> HasHit() pour les tirs qui ont touché un ennemi et ont laissé leur impact.

    Nom : fonction4.png
Affichages : 64
Taille : 10,3 Ko

    Et puis on essaye !

    Jme créé un ptit objet et j'y colle la fonction que je veux :
    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
     
    #include "Ennemy.h"
    #include "PlayerShot.h"
     
    int main{
     
    // On se fait un ptit ennemy qui meur simplement par destruction
    objects.push_back(new Ennemy(&Ennemy::IsDestroyed());
     
    // On s'en fait un invincible qui meur en sortant de l'écran
    objects.push_back(new Ennemy(&GameObject::OutOfScreen());
     
    // Hop pour la forme on fait un PlayerShot
    objects.push_back(new PlayerShot(&PlayerShot::HasHit());
     
    return EXIT_SUCCESS;
    }
    J'y test... BAH CA COMPILE PAS§§

    Bon je demande à un pote qui s'y connais mieux que moi, il me dis c'est pas possible, t''instancies pas tes fonction.
    Citation Envoyé par moi
    Gnééé? Ça s'instancie des fonctions?
    Bon alors il m'embarque sur une solution qui passe par des foncteur, mais j'y comprend pas plus que ça, puis il me parle d'héritage multiple, mais là c'est lui qui s'y perd.

    Je me tourne donc vers TOI cher ami qui a eu le malheur de cliquer sur ce post! Connais-tu une solution à mon problème?

    Pour résumer, je cherche à trouver une solution pour :
    - Assigner une fonction pour la suppression de mes objet à chaque instance d'objet
    - Les fonctions doivent avoir accès d'une manière ou d'une autre aux données membres nécessaire de mes objets. (soit directement, soit par accèsseur)
    - Les fonctions doivent reconnaitre si il s'agit d'un GameObject, un Ennemy ou un PlayerShot afin d'accéder à la bonne donnée membre.
    - Et la cerise sur le gâteau, je dois pouvoir assigner la méthode de suppression de l'objet via son constructeur.

    Ca peut être par un pointeur de fonction, des foncteur, héritage multiple, ça peut être quelques chose de plus simple, de plus compliqué, peut m'importe mais j'aimerai quelques chose de plus élégant que de poser une énumération et faire un grooooos switch.

    Et bien sur je reste disponible si vous voulez plus d'info ou avez des questions sur mon problème. =p

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

    Informations professionnelles :
    Activité : aucun

    Informations forums :
    Inscription : Octobre 2004
    Messages : 11 614
    Points : 30 626
    Points
    30 626
    Par défaut
    Salut,

    Ne trouverais tu pas plus logique que:
    1. ce soit le stage qui décide, en fonction des circonstances, de créer un des GameObject particuliers
    2. ce soit GameObject qui se charge de signaler au stage qu'il est en fin de vie
    3. lorsque le stage reçoit un signal de fin de vie, il supprime le GameObject "émetteur"


    Tout cela pour dire que, plutôt que de mettre une fonction EOL dans GameObject , il est sans doute préférable de permettre à tes GameObject d'appeler une fonction équivalente dans ton stage

    En effet, chacune des classes dérivée de GameObject dispose de toutes les informations qui sont leur sont nécessaires pour décider du moment de leur fin de vie:

    La seule différence qu'il y a entre les différentes classes, c'est... les circonstances dans lesquelles elles décideront de signaler qu'elles meurent
    • Un objet qui ne doit pas survivre à la sortie de la surface affichée dispose de sa position sur cette surface et enverra donc le signal si, après un déplacement, sa position est en dehors de la surface affichée (ou s'il entre en collision avec un autre objet, auquel il fera perdre des points de vie par la même occasion )
    • Un objet qui meure quand il n'a plus de point de vie dispose... du nombre total de point de vie que l'on peut lui retirer et enverra le signal si, après avoir perdu des points de vie, leur nombre est inférieur ou égal à 0
    • etc
    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

  3. #3
    Membre du Club
    Homme Profil pro
    Développeur de jeux vidéo
    Inscrit en
    Juin 2006
    Messages
    122
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Âge : 40
    Localisation : France

    Informations professionnelles :
    Activité : Développeur de jeux vidéo

    Informations forums :
    Inscription : Juin 2006
    Messages : 122
    Points : 59
    Points
    59
    Par défaut
    J'ai du mal me faire comprendre avec mon pavé. (Ou bien j'ai mal compris ta réponse!)

    Citation Envoyé par koala01
    1. ce soit le stage qui décide, en fonction des circonstances, de créer un des GameObject particuliers
    C'est ce qui se passe. Le stage, en fonction d'un scénario ou suivant certaines conditions/évènements, créé les GameObject (sous forme de classe héritée ou pas), les ajoute à sa liste de GameObject et les affiche.
    2. ce soit GameObject qui se charge de signaler au stage qu'il est en fin de vie
    Le stage, dans sa fonction deleteObject() vérifie que l'objet est bien en fin de vie en appelant la fonction EOL() de l'objet concerné. =p
    3. lorsque le stage reçoit un signal de fin de vie, il supprime le GameObject "émetteur"
    C'est ce qui se passe. Après pour moi, un "signal", c'est un simple bool retourné par EOL(), et c'est dans EOL qu'on fait les vérification. Si parle "signal" tu penses à autre chose, je veux bien en apprendre plus. :]
    Je vois pas trop où tu veux en venir étant donné que visiblement je procède déjà comme ça, ou alors tu penses à quelque chose de vraiment différent. =p

    Pour résumer ce qui se passe dans mon stage, j'ai une fonction Calculate() qui se charge de faire les ajout, modification et suppression de GameObject :
    void Calculate()
    {
    // Du blabla pour modifier
    // Du blabla pour ajouter
    // Et le blabla pour supprimer :
    if(mon_objet.EOL())
    {
    delete mon_objet;
    }
    }
    Avant, la fonction EOL était une simple fonction qui regroupe 1 condition par classe. Pour exemple, pour GameObject, ça donnait ça :
    bool GameObject::EOL()
    {
    return x < 0 || x > window.GetLargeur() || y < 0 || y > window.GetHauteur();
    }
    Mais il se peut que j'ai besoin plus tard d'objets avec des condition un peu plus spécifiques, alors j'ai voulu passer par un pointeur, j'aurai donc eu :
    bool GameObject::EOL()
    {
    return (*condition)();
    }
    Et à la construction, je donne à mon pointeur condition la fonction qui va permettre de déterminer si l'objet est en fin de vie ou pas. (voir l'exemple au dessus avec les constructeurs)

    Mon souhait c'est de pouvoir indiquer à la création de l'objet (à son instanciation) quelle méthode il va utiliser pour déterminer si il envoit le signal de fin de vie au niveau.

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

    Informations professionnelles :
    Activité : aucun

    Informations forums :
    Inscription : Octobre 2004
    Messages : 11 614
    Points : 30 626
    Points
    30 626
    Par défaut
    Visiblement, d'après ce que j'ai compris, du moins, je te propose en fait d'inverser l'endroit où se trouvent les différentes fonctions, ce qui revient à rétablir les responsabilités

    En effet, si j'ai bien compris ton laïus, tu es parti sur quelque chose proche de
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    class GameObject
    {
        public:
            GameObject();
            virtual ~GameObject();
            virtual bool EOL() const = 0;
    };
    class Enemy : public GameObject
    {
        public:
            Enemy();
            virtual ~Enemy();
            virtual bool EOL() const;
    };
    class PlayerShoot : public GameObject
    {
        public:
            PlayerShoot();
            virtual  ~PlayerShoot();
            virtual bool EOL() const;
    };
    class Stage
    {
     
        public:
            /*...*/
        private:
            std::vector<GameObject*> items_;
    };
    ce qui implique que c'est à ton instance de Stage de demander aux différents GameObject s'ils sont en fin de vie

    Or, ce que je te conseille de faire, c'est exactement l'inverse: que ce soient les objets dérivés de GameObject qui préviennent l'instance de Stage qu'ils sont en fin de vie

    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
    /* les 8 directions possible de mouvements */
    enum eMovement
    {
        mUp,
        mDown,
        mLeft,
        mRight,
        mUpLeft,
        mUpRight,
        mDownLeft,
        mDownRight
    };
    class GameObject
    {
        public:
            GameObject( Stage* ); /* ( 1 ) */
            virtual ~GameObject();
            /* tous les objets peuvent se déplacer */
            void move(eMovement );
            /* un mouvement peut provoquer une collision avec un autre */
            virtual void collisionWith(GameObject*);
            virtual GameObject* clone() const =0; /* ( 2 ) */
        private:
            Stage* stage_; /* ( 1 ) */
    };
    class Enemy : public GameObject
    {
        public:
            Enemy();
            virtual ~Enemy();
            /* un mouvement peut provoquer une collision avec un autre */
            virtual void collisionWith(GameObject*);
            virtual Enemy * clone() const;
            void damages( int);
    };
    class PlayerShoot : public GameObject
    {
        public:
            PlayerShoot();
            virtual  ~PlayerShoot();
            virtualvoid collisionWith(GameObject*);
            virtual Enemy * clone() const;
    };
    class Stage
    {
        public:
            Stage();
            ~Stage();
            void registerItem(GameObject*);
            void deathOf(GameObject*);
       private:
            std::vector<GameObject*> items_;
    };
    /* ( 2 ) */
    class Factory
    {
        public:
            static Factory& instance( );
            GameObject* create(std::string const &);
        private:
            std::map<std::string, GameObject*> protos_;
    };
    ( 1 ) j'ai placé un pointeur vers le Stage afin que l'objet puisse lui envoyer un message, mais nous pourrions envisager l'utilisation d'un singleton

    ( 2 ) L'idéal, c'est d'utiliser un pattern "Factory" (associé à des prototypes) pour la création des différents objets

    De cette manière, lorsque, en cours de jeu, ton shoot bouge, il vérifie s'il est encore dans la surface visible et s'il entre en collision collision avec un ennemi.

    S'il sort de la surface, il envoie directement le message deathOf(this) au stage.

    S'il entre en collision avec un ennemi, il envoie un message damage( points de vie retirés) à l'ennemi et un message deathOf(this) au stage.

    Lorsque l'ennemi reçoit un message damage, il calcule (si nécessaire) les dommages réellement subits et, si ces dommages le font mourrir (vie inférieur ou égale à 0), il envoie le message deathOf(this) au stage.

    Le stage demande, sur base du script, à la fabrique (Factory) de créer les différents objets et les enregistre une fois obtenus

    Lorsqu'il reçoit un message deathOf, il efface l'objet qui s'est déclaré mort

    Enfin, la fabrique se charge de créer les différents objets et de les enregistrer auprès du stage.
    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

  5. #5
    Nouveau membre du Club
    Profil pro
    Inscrit en
    Septembre 2004
    Messages
    30
    Détails du profil
    Informations personnelles :
    Localisation : Suisse

    Informations forums :
    Inscription : Septembre 2004
    Messages : 30
    Points : 29
    Points
    29
    Par défaut
    Citation Envoyé par Spidyy Voir le message
    En avançant dans ma réflexion, je me suis rendu compte que les conditions pour effacer un objet peuvent être différentes pour chaque instances d'objet.

    Par exemple : certains GameObject sont effacé lorsqu'ils sortes de l'écran de jeu. D'autres lorsqu'ils arrivent en fin de vie uniquement (car ils sont immobiles et ne sortirons jamais de l'écran), d'autres encore qui disparaissent lorsque l'animation de leur image (composée parfois de plusieurs sprites) se termine.
    A mon avis c'est là où tu fais une erreur, tu veux que les conditions soient spécifiques à chaque objet alors que, visiblement, elles sont spécifiques à chaque famille d'objets: ceux qui meurent quand ils sortent de l'écran, ceux qui sont immobiles, etc. Comment ça se traduit? Par de nouvelles sous-classes de tes classes existantes.

    En plus, si tu définis un attribut (fonction) pour chaque objet, tu en auras plus d'un qui aura exactement la même valeur (pointeur sur fonction). D'où redondance d'informations: pas bien.

    Mais si tu veux (dois) persister dans ta voie, utilise au moins des objets pour définir tes conditions (pas des pointeurs sur fonctions). Style,

    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
     
    class GameObject
    {
      Condition& condition;
     
    public:
      GameObject(Condition& c) : condition(c) {}
     
      bool EOL()
      {
        return condition.canDelete(this);
      }
    }
     
    class Condition
    {
    public:
      virtual bool canDelete(GameObject &go) = 0;
    }
     
    class ConditionImmobile : public Condition
    {
      ConditionImmobile() { }
    public:
      static ConditionImmobile condition; // singleton
      bool canDelete(GameObject &go) { return false; }
    }
     
    class ConditionOutOfSpace : public Condition
    {  
       /* ... */
    }

  6. #6
    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 : 49
    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
    Points : 16 213
    Points
    16 213
    Par défaut
    Tu as déclaré ton pointeur de fonction comme pointant sur une fonction libre, et tu essaye de lui passer une fonction membre, ce n'est pas pareil. Passer une fonction statique de classe pourrait passer, mais dans ce cas, tu ne pourrais pas accéder aux données membre de tes objets.

    Le plus simple, pour commencer, c'est probablement d'utiliser le design pattern strategy (qui est assez proche des foncteurs). Tu définis une classe de base DyingStrategy, et des classes dérivées qui représentes les diverses façons de mourir. Et tu fais posséder à ta classe GameObjet une de ces stratégies. Il est probable aussi qu'une stratégie aura un pointeur sur un GameObject afin d'en soutirer les informations qui lui sont pertinentes.
    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.

  7. #7
    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 : 49
    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
    Points : 16 213
    Points
    16 213
    Par défaut
    Citation Envoyé par cyberpi Voir le message
    A mon avis c'est là où tu fais une erreur, tu veux que les conditions soient spécifiques à chaque objet alors que, visiblement, elles sont spécifiques à chaque famille d'objets: ceux qui meurent quand ils sortent de l'écran, ceux qui sont immobiles, etc. Comment ça se traduit? Par de nouvelles sous-classes de tes classes existantes.
    Pas forcément. Il est souvent plus flexible de définir ainsi des stratégies qui sortent de la hiérarchie de base. Trois avantages :
    - Chaque classe ne fait plus qu'une chose. Il y a moins de risque d'erreur, le test unitaire est plus simple à mettre en place.
    - La combinatoire : Si on a 3 GameObject, 3 stratégies de mort, 3 stratégies de déplacement, 3 stratégies de tir, on a développé 12 classes (+4 classes de base), et on a 81 combinaisons.
    - Le dynamisme : On peut pendant un temps attribuer une stratégie à un objet, puis au bout d'un moment, ou quand survient un évènement, basculer vers une autre stratégie.
    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.

  8. #8
    Nouveau membre du Club
    Profil pro
    Inscrit en
    Septembre 2004
    Messages
    30
    Détails du profil
    Informations personnelles :
    Localisation : Suisse

    Informations forums :
    Inscription : Septembre 2004
    Messages : 30
    Points : 29
    Points
    29
    Par défaut
    Citation Envoyé par JolyLoic Voir le message
    Pas forcément. Il est souvent plus flexible de définir ainsi des stratégies qui sortent de la hiérarchie de base. Trois avantages :
    - Chaque classe ne fait plus qu'une chose. Il y a moins de risque d'erreur, le test unitaire est plus simple à mettre en place.
    - La combinatoire : Si on a 3 GameObject, 3 stratégies de mort, 3 stratégies de déplacement, 3 stratégies de tir, on a développé 12 classes (+4 classes de base), et on a 81 combinaisons.
    - Le dynamisme : On peut pendant un temps attribuer une stratégie à un objet, puis au bout d'un moment, ou quand survient un évènement, basculer vers une autre stratégie.
    Oui, c'est l'éternel débat entre héritage et délégation. C'est vrai que dès que le nombre de classes impliquées risque de devenir grand il est préférable de faire de la délégation, mais, dans les cas simples (a priori ici, où il n'était question que d'un seul type de stratégie) je préfère l'héritage car il ne multiplie pas les objets, permet de bénéficier directement de la liaison dynamique (sans indirection vers une stratégie) et définit exactement le comportement de chaque objet.

    Mais la solution avec le modèle de stratégie que nous avons proposé toi et moi (avec quelques variantes) me convient tout aussi bien.

  9. #9
    Membre du Club
    Homme Profil pro
    Développeur de jeux vidéo
    Inscrit en
    Juin 2006
    Messages
    122
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Âge : 40
    Localisation : France

    Informations professionnelles :
    Activité : Développeur de jeux vidéo

    Informations forums :
    Inscription : Juin 2006
    Messages : 122
    Points : 59
    Points
    59
    Par défaut
    J'ai opté pour la solution du pattern Strategy. :p (Et ça m'a appris les singleton au passage!)

    En regardant de plus prêt c'est bien ce qu'il me fallait.

    Mes objets sont simple dans mon exemple, mais en réalité ils ont beaucoup plus de classe héritées que ça, et oui le nombre de mes stratégies risque de grandir très vite (notament avec l'IA, les déplacements, les pattern de tirs, etc).

    C'est bien sque je voulais. Pouvoir définir un comportement à mon objet sans avoir à réécrire une énième classe intermédiaire. =) Merci cyberpi et JolyLoic!


    Par contre Koala1, je comprend à peu près le principe d'inverser mes appels entre le stage et les objets, mais étant donnée que si je change dans ce sens, ça va me faire retravailler une grosse quantité de code en profondeur, j'aimerai que tu me détails les avantages à procéder comme tu le propose comparé à ce que je fais déjà. =p

    Dans mon jeu, le stage jou le rôle de la classe factory. J'ai une classe Game qui regroupe tout le contenu de mon jeu (images, sons, GameObjets et classe héritantes...) et le Stage prend un des objets et en insère une copie dans ma scène de jeu, copie qui devient indépendante dans son comportement.

  10. #10
    Membre du Club
    Homme Profil pro
    Développeur de jeux vidéo
    Inscrit en
    Juin 2006
    Messages
    122
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Âge : 40
    Localisation : France

    Informations professionnelles :
    Activité : Développeur de jeux vidéo

    Informations forums :
    Inscription : Juin 2006
    Messages : 122
    Points : 59
    Points
    59
    Par défaut
    Jvais profiter du post, j'ai essayé d'appliquer le pattern strategy à partir de l'exemple plus haut, mais j'ai un soucis de compilation.

    Voici le EOLCondition.h fait tel qu'on me l'a montré : (Je racourcie les fichiers pour qu'on ai un aperçue uniquement des parties qui nous intéresses.)

    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
     
    #ifndef EOLCONDITION_H
    #define EOLCONDITION_H
     
    #include <SFML/Graphics.hpp>
     
    class GameObject;
    class Ennemy;
    class PlayerShot;
    //class DamageZone;
     
    class EOLCondition
    {
    	public:
    		virtual bool CanDelete(GameObject &obj, sf::RenderWindow &app) = 0;
    		virtual bool CanDelete(Ennemy &nmy, sf::RenderWindow &app) = 0;
    		virtual bool CanDelete(PlayerShot &ps, sf::RenderWindow &app) = 0;
    };
     
    class EOLNone : public EOLCondition
    {
    		EOLNone() {}
    	public:
    		static EOLNone condition;
    		virtual bool CanDelete(GameObject &obj, sf::RenderWindow &app) { return false; }
    		virtual bool CanDelete(Ennemy &nmy, sf::RenderWindow &app) { return false; }
    		virtual bool CanDelete(PlayerShot &ps, sf::RenderWindow &app) { return false; }
    };
     
    class EOLOutOfRange : public EOLCondition
    {
    		EOLOutOfRange() {}
    	public:
    		static EOLOutOfRange condition;
    		virtual bool CanDelete(GameObject &obj, sf::RenderWindow &app);
    		virtual bool CanDelete(Ennemy &nmy, sf::RenderWindow &app);
    		virtual bool CanDelete(PlayerShot &ps, sf::RenderWindow &app);
    };
     
    #endif
    Et son EOLCondition.cpp
    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
    #include "EOLCondition.h"
     
    #include "GameObject.h"
    #include "Ennemy.h"
    #include "PlayerShot.h"
     
    // GameObject
    bool EOLOutOfRange::CanDelete(GameObject &obj, sf::RenderWindow &app)
    {
    	return obj.GetX() < 0 || obj.GetX() > app.GetWidth() || obj.GetY() < 0 || obj.GetY() > app.GetHeight();
    }
     
    bool EOLOutOfRange::CanDelete(Ennemy &nmy, sf::RenderWindow &app)
    {
    	return nmy.GetX() < 0 || nmy.GetX() > app.GetWidth() || nmy.GetY() < 0 || nmy.GetY() > app.GetHeight();
    }
     
    bool EOLOutOfRange::CanDelete(PlayerShot &ps, sf::RenderWindow &app)
    {
    	return ps.GetX() < 0 || ps.GetX() > app.GetWidth() || ps.GetY() < 0 || ps.GetY() > app.GetHeight();
    }
    J'ai ensuite modifié mes classes avec une référence vers une condition et rajouté le paramètre au constructeur :

    GameObject.h

    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
     
    #ifndef GAME_OBJECT_H
    #define GAME_OBJECT_H
     
    #include <SFML/Graphics.hpp>
    #include <math.h>
    #include "constantes.h"
    #include "Gfx.h"
     
    #include "EOLCondition.h"
     
    class Stage;
    class GameObject
    {	
    	protected:
    		Gfx * gfx;
    		float x;
    		float y;
    		EOLCondition & condition;
     
    	public:
    		GameObject();
    		virtual ~GameObject();
    		GameObject(const GameObject &obj);
    		GameObject(Gfx *gfx, EOLCondition &condition, float x = 0.f, float y = 0.f, float angle = 0.f, float speed = 0.f, bool linked_rotation = false, float duration = 0.f);
    		GameObject & operator=(const GameObject &obj);
    		float GetX() const;
    		float GetY() const;
    		bool EOL(sf::RenderWindow &app);
     
    };
     
    #endif
    Et son GameObject.cpp
    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
    #include "GameObject.h"
     
    GameObject::GameObject() : gfx(NULL), x(0.f), y(0.f), angle(0.f), speed(0.f), duration(0.f), linked_rotation(false), stage(NULL), condition(EOLNone::condition)
    {
    }
     
    GameObject::GameObject(const GameObject &obj) : gfx(NULL), condition(obj.condition), x(obj.x), y(obj.y), angle(obj.angle), speed(obj.speed), duration(obj.duration), linked_rotation(obj.linked_rotation), hitboxs(obj.hitboxs), stage(obj.stage)
    {
    	if (obj.gfx)
    		gfx=obj.gfx->Clone();
    	life.Reset();
    }
     
    GameObject::GameObject(Gfx *gfx, EOLCondition &condition, float x, float y, float angle, float speed, bool linked_rotation, float duration) : gfx(gfx), condition(condition), x(x), y(y), angle(angle), speed(speed), duration(duration), linked_rotation(linked_rotation), stage(NULL)
    {
    	gfx->SetPosition(x, y);
    	if(linked_rotation)
    		gfx->SetRotation(angle - 90);
    }
     
    GameObject::~GameObject()
    {
    	if (gfx)
    	{
    		delete gfx;
    	}
    }
     
    GameObject & GameObject::operator=(const GameObject &obj)
    {
    	if(obj.gfx)
    		gfx=obj.gfx->Clone();
    	hitboxs = obj.hitboxs;
    	x = obj.x;
    	y = obj.y;
    	angle = obj.angle;
    	speed = obj.speed;
    	linked_rotation = obj.linked_rotation;
    	stage = obj.stage;
    	condition = obj.condition;
    	return *this;
    }
     
    bool GameObject::EOL(sf::RenderWindow &app)
    {
    	return condition.CanDelete(*this, app);
    }
    Et enfin, une classe qui me pose problème, Ennemy.h :
    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
    #ifndef ENNEMY_H
    #define ENNEMY_H
     
    #include <SFML/Audio.hpp>
    #include "GameObject.h"
     
    class Ennemy : public GameObject
    {
    		float health_point;
    		bool alive;
    		sf::Sound *sound;
    		GameObject *explosion;
    		unsigned int explosion_number;
    	public:
    		Ennemy();
    		virtual ~Ennemy() {};
    		Ennemy(const Ennemy &ennemy);
    		Ennemy(Gfx * gfx, EOLCondition &condition, sf::Sound *sound, GameObject *explosion, float health_point, unsigned int explosion_number, float x = 0.f, float y = 0.f, float angle = 0.f, float speed = 0.f, bool linked_rotation = false);
    		void TakeDamage(float damage);
    		float GetHealth() const;
    		bool IsAlive() const;
    		void SetAlive(bool alive);
    		virtual bool EOLCondition();
    		void PlaySound();
    		void Destroy();
    };
     
    #endif
    Et son Ennemy.cpp
    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
    #include "Ennemy.h"
    #include "Stage.h"
     
    Ennemy::Ennemy() : GameObject(), health_point(0.f), alive(true), sound(NULL), explosion(NULL), explosion_number(0)
    {
    }
     
    Ennemy::Ennemy(const Ennemy &ennemy) : GameObject(ennemy), health_point(ennemy.health_point), alive(true), sound(ennemy.sound), explosion(ennemy.explosion), explosion_number(ennemy.explosion_number)
    {
    }
     
    Ennemy::Ennemy(Gfx * gfx, EOLCondition &condition, sf::Sound *sound, GameObject *explosion, float health_point, unsigned int explosion_number, float x, float y, float angle, float speed, bool linked_rotation) :
    GameObject(gfx, condition, x, y, angle, speed, linked_rotation, 0.f),
    health_point(health_point),
    alive(true),
    sound(sound),
    explosion(explosion),
    explosion_number(explosion_number)
    {
    }
     
    void Ennemy::TakeDamage(float damage)
    {
    	health_point -= damage;
    	if(health_point <= 0.f)
    	{
    		Destroy();
    		PlaySound();
    	}
    }
     
    float Ennemy::GetHealth() const
    {
    	return health_point;
    }
     
    bool Ennemy::IsAlive() const
    {
    	return alive;
    }
     
    void Ennemy::SetAlive(bool alive)
    {
    	this->alive = alive;
    }
     
     
     
    void Ennemy::PlaySound()
    {
    	sound->Stop();
    	sound->Play();
    }
     
    void Ennemy::Destroy()
    {
    	SetAlive(false);
    	if(explosion != NULL && explosion_number != 0)
    	{
    		for(unsigned int i = 0; i < explosion_number; i++)
    		{
    			float randomScale = sf::Randomizer::Random(1.f, 1.5f);
    			explosion->GetGfx()->SetPosition(x + sf::Randomizer::Random(-gfx->GetWidth()/4.f, gfx->GetHeight()/4.f), y + sf::Randomizer::Random(-gfx->GetWidth()/4.f, gfx->GetHeight()/4.f));
    			explosion->GetGfx()->SetRotation(sf::Randomizer::Random(0.f, 360.f));
    			explosion->GetGfx()->SetScale(randomScale, randomScale);
    			stage->AddGameObject(*explosion);
    		}
    	}
    }
    Je boss sous VisualStudio 2009 et j'obtiens ça comme erreur pour Ennemy.cpp :
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    1>------ Build started: Project: Eve Shmup SFML, Configuration: Release Win32 ------
    1>Compiling...
    1>Ennemy.cpp
    1>z:\dev\copie de eve shmup sfml\eve shmup sfml\DamageZone.h(13) : error C2061: syntax error : identifier 'EOLCondition'
    1>z:\dev\copie de eve shmup sfml\eve shmup sfml\Blaster.h(15) : error C2061: syntax error : identifier 'EOLCondition'
    1>.\Ennemy.cpp(12) : error C2061: syntax error : identifier 'EOLCondition'
    1>.\Ennemy.cpp(12) : error C2511: 'Ennemy::Ennemy(Gfx *)' : overloaded member function not found in 'Ennemy'
    1>        z:\dev\copie de eve shmup sfml\eve shmup sfml\Ennemy.h(7) : see declaration of 'Ennemy'
    1>.\Ennemy.cpp(71) : fatal error C1004: unexpected end-of-file found
    1>Build log was saved at "file://z:\dev\copie de eve shmup sfml\eve shmup sfml\Release\BuildLog.htm"
    1>Eve Shmup SFML - 5 error(s), 0 warning(s)
    ========== Build: 0 succeeded, 1 failed, 0 up-to-date, 0 skipped ==========
    Dans l'erreur, on remarque l'apparition de DamageZone.h et Blaster.h, qui sont des classé héritant de PlayerShot.

    DamageZone.h
    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
    #ifndef DAMAGE_ZONE_H
    #define DAMAGE_ZONE_H
     
    #include "PlayerShot.h"
     
    class DamageZone : public PlayerShot
    {
    		float delay;
    		sf::Clock delay_clock;
    	public:
    		DamageZone();
    		virtual ~DamageZone() {};
    		DamageZone(Gfx * gfx, EOLCondition &condition, float damage, float delay, float duration, bool linked_rotation = true, sf::Sound *sound = NULL);
    		DamageZone(const DamageZone & dz);
    		virtual void Hit(Ennemy &ennemy);
    		virtual PlayerShot* Clone() const;
    };
     
    #endif
    Et Blaster.h
    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
    #ifndef BLASTER_H
    #define BLASTER_H
     
    #include "Ennemy.h"
    #include "DamageZone.h"
     
    class Stage;
     
    class Blaster : public PlayerShot
    {
    		 PlayerShot *zone;
    	public:
    		Blaster();
    		virtual ~Blaster() {};
    		Blaster(Gfx * gfx, EOLCondition &condition, float speed, float damage, bool linked_rotation = true, sf::Sound *sound = NULL, PlayerShot * zone = NULL);
    		Blaster(const Blaster & bl);
    		virtual void Hit(Ennemy &ennemy);
    		virtual PlayerShot* Clone() const;
    };
     
    #endif
    En gros, bien que le EOLCondition soit inclue dans le GameObject, et que Ennemy hérite de GameObject, il ne le reconnais pas et je n'arrive pas du tout à comprendre pourquoi.

  11. #11
    Nouveau membre du Club
    Profil pro
    Inscrit en
    Septembre 2004
    Messages
    30
    Détails du profil
    Informations personnelles :
    Localisation : Suisse

    Informations forums :
    Inscription : Septembre 2004
    Messages : 30
    Points : 29
    Points
    29
    Par défaut
    Citation Envoyé par Spidyy Voir le message
    En gros, bien que le EOLCondition soit inclue dans le GameObject, et que Ennemy hérite de GameObject, il ne le reconnais pas et je n'arrive pas du tout à comprendre pourquoi.
    Oui mais EOLCondition n'a pas l'air d'être inclue dans DamageZone.

  12. #12
    Membre du Club
    Homme Profil pro
    Développeur de jeux vidéo
    Inscrit en
    Juin 2006
    Messages
    122
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Âge : 40
    Localisation : France

    Informations professionnelles :
    Activité : Développeur de jeux vidéo

    Informations forums :
    Inscription : Juin 2006
    Messages : 122
    Points : 59
    Points
    59
    Par défaut
    Ha mince j'ai oublié de donner PlayerShot.h =p

    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
    #ifndef PLAYER_SHOT_H
    #define PLAYER_SHOT_H
     
    #include <SFML/Audio.hpp>
    #include "GameObject.h"
    #include "Ennemy.h"
     
    class PlayerShot : public GameObject
    {
    	protected:
    		float damage;
    		bool has_hit;
    		sf::Sound *sound;
    		GameObject *impact;
     
    	public:
    		PlayerShot();
    		virtual ~PlayerShot() {};
    		PlayerShot(Gfx * gfx, EOLCondition &condition, float damage, float speed, bool linked_rotation, sf::Sound *sound = NULL, GameObject *impact = NULL, float duration = 0.f);
    		PlayerShot(const PlayerShot & );
    		void SetDamage(float damage);
    		float GetDamage() const;
    		sf::Image & GetImage() const;
    		virtual bool EOLCondition();
    		virtual void Hit(Ennemy &ennemy);
    		bool HasHit() const;
    		virtual PlayerShot* Clone() const;
    };
     
    #endif
    Donc EOLCondition est inclus dans DamageZone car DamageZone inclus PlayerShot qui inclus GameObject qui inclus EOLCondition.
    On est d'accord ou j'ai loupé un truc? :s

    J'ai demandé au pote que j'ai cité dans le premier paragraphe, d'après lui il y a un problème avec mes includes, mais comme moi il voit pas où.

  13. #13
    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 : 49
    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
    Points : 16 213
    Points
    16 213
    Par défaut
    Tes includes sont cycliques. Il te faut briser le cycle en déclarant sans la définir une classe quelque part :
    http://cpp.developpez.com/faq/cpp/?p...erence_croisee
    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.

  14. #14
    Membre du Club
    Homme Profil pro
    Développeur de jeux vidéo
    Inscrit en
    Juin 2006
    Messages
    122
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Âge : 40
    Localisation : France

    Informations professionnelles :
    Activité : Développeur de jeux vidéo

    Informations forums :
    Inscription : Juin 2006
    Messages : 122
    Points : 59
    Points
    59
    Par défaut
    Pourtant j'ai vérifié encore et encore que mes include ne soit pas cyclique justement, je me suis fait un ptit dessin, il n'y a qu'à EOLCondition qu'il y aurai cyclicité mais je l'ai déjà cassé. :/

  15. #15
    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 : 49
    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
    Points : 16 213
    Points
    16 213
    Par défaut
    Désolé, en regardant rapidement, j'ai confondu .h et .cpp...

    Tu pourrais mettre en attachement un projet minimaliste qu'on puisse compiler pour reproduire le problème ? parce que comme ça, textuellement, c'est pas facile à voir (enlève tout ce qui est graphique, qu'on puisse compiler chez nous).

    Autre idée : Ton erreur ressemble aussi à ce qu'on a quand on oublie un ';' à la fin de la déclaration d'une classe. Mais pas moyen de vérifier, puisque le code que tu nous montre a été nettoyé.
    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.

  16. #16
    Membre du Club
    Homme Profil pro
    Développeur de jeux vidéo
    Inscrit en
    Juin 2006
    Messages
    122
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Âge : 40
    Localisation : France

    Informations professionnelles :
    Activité : Développeur de jeux vidéo

    Informations forums :
    Inscription : Juin 2006
    Messages : 122
    Points : 59
    Points
    59
    Par défaut
    J'ai trouvé le problème en préparant la version minimale du code, simplement que j'ai un résidue de
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    virtual bool EOLCondition();
    dans Ennemy.h et PlayerShot.h, conflit de nommage.

    Bon pour finir, je comprend pas comment assigner mes conditions à mes constructeurs.

    J'ai mes constructeurs qui se présentent comme ça :
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    GameObject(EOLCondition &condition, ...)
    J'essaye d'assigner mon objet condition comme ça :
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    GameObject objet(EOLOutOfRange::condition)
    Ca me met un
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1>Game.obj : error LNK2001: unresolved external symbol "public: static class EOLOutOfRange EOLOutOfRange::condition" (?condition@EOLOutOfRange@@2V1@A)
    Je tente de donner un nom différent à mes objets dans la déclaration de mes condition :
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
    class EOLOutOfRange : public EOLCondition
    {
    		EOLOutOfRange() {}
    	public:
    		static EOLOutOfRange condition_out_of_range;
    ...
     
    };
    Je l'assigne comme ça :
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    GameObject objet(condition_out_of_range)
    Et il me fait :
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1>.\Game.cpp(306) : error C2065: 'condition_out_of_range' : undeclared identifier
    (J'ai bien fais mon #include "EOLCondition.h")

    J'ai pas essayé d'y appeler le constructeur, ça serai passer à côté de l'utilité des singletons, donc what? Qu'est-ce que j'ai raté?

    EDIT : Etttt trouvé, ils sont pas initialisé dans le EOLCondition.h...

  17. #17
    Nouveau membre du Club
    Profil pro
    Inscrit en
    Septembre 2004
    Messages
    30
    Détails du profil
    Informations personnelles :
    Localisation : Suisse

    Informations forums :
    Inscription : Septembre 2004
    Messages : 30
    Points : 29
    Points
    29
    Par défaut
    Citation Envoyé par Spidyy Voir le message
    Bon pour finir, je comprend pas comment assigner mes conditions à mes constructeurs.

    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
    class EOLOutOfRange : public EOLCondition
    {
    		EOLOutOfRange() {}
    	public:
    		static EOLOutOfRange condition_out_of_range;
    ...
     
    };
    Il faut initialiser ton attribut statique. Rajoute, en dehors de la classe:

    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
     
    EOLOutOfRange EOLOutOfRange::condition_out_of_range;

  18. #18
    Membre du Club
    Homme Profil pro
    Développeur de jeux vidéo
    Inscrit en
    Juin 2006
    Messages
    122
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Âge : 40
    Localisation : France

    Informations professionnelles :
    Activité : Développeur de jeux vidéo

    Informations forums :
    Inscription : Juin 2006
    Messages : 122
    Points : 59
    Points
    59
    Par défaut
    Yep c'est sque j'ai fais et ça compile! Encore quelques bug à régler avant de dire si ça marche mais c'est déjà ça. :] Merci !

  19. #19
    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 : 49
    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
    Points : 16 213
    Points
    16 213
    Par défaut
    Citation Envoyé par Spidyy Voir le message
    J'ai trouvé le problème en préparant la version minimale du code
    J'avais oublié de préciser : C'est généralement ce qui arrive, et quand je bloque sur un point, il m'arrive souvent de préparer un programme minimaliste pour le reproduire, et ça fait des miracles
    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.

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

    Informations professionnelles :
    Activité : aucun

    Informations forums :
    Inscription : Octobre 2004
    Messages : 11 614
    Points : 30 626
    Points
    30 626
    Par défaut
    Excuses moi, ce n'est qu'en relisant toute la discussion que j'ai remarqué avoir laissé passer une question qui m'était posée
    Citation Envoyé par Spidyy Voir le message

    Par contre Koala1, je comprend à peu près le principe d'inverser mes appels entre le stage et les objets, mais étant donnée que si je change dans ce sens, ça va me faire retravailler une grosse quantité de code en profondeur, j'aimerai que tu me détails les avantages à procéder comme tu le propose comparé à ce que je fais déjà. =p
    Le fait est qu'il "ne sert à rien" de vouloir demander à un objet s'il est en fin de vie ou non:s'il est mort, il est mort et ne peut donc pas répondre

    Par contre, si, en appliquant éventuellement une stratégie qui te permet de constater à un moment donné qu'un objet arrive en fin de vie, tu lui donne la "responsabilité" de le signaler à ton stage (qui le supprime dés qu'il reçoit le message), tu es sur de n'avoir à un instant T, que les objets qui sont réellements "vivants"

    Cela permet d'éviter un échange de communication du genre de
    (stage) Hé, l'objet là bas, es tu viviant
    (objet) ben non, je suis mort
    (stage) Ha, bon, ben... tant pis, je passe à un autre... C'est moche, j'aurais voulu te demander quelque chose
    En effet, c'est, normalement, ton stage qui devrait se charger de la communication entre les différents objets du stage...Il agit, en somme, comme un médiateur: c'est la tour de contrôle qui s'assure que tous les messages arrivent bien aux différents avions dont elle a la responsabilité
    Dans mon jeu, le stage jou le rôle de la classe factory. J'ai une classe Game qui regroupe tout le contenu de mon jeu (images, sons, GameObjets et classe héritantes...) et le Stage prend un des objets et en insère une copie dans ma scène de jeu, copie qui devient indépendante dans son comportement.
    Fais peut être attention à ne pas donner trop de responsabilités à ta classe Stage...

    Plus tu lui donnera de responsabilités, plus tu auras un couplage fort entre les différents objet qu'il doit gérer.

    Quand on y pense, ton stage ne devrait travailler qu'avec des GameObject et essayer de leur envoyer des messages "de base" (va à gauche, va à droite, ...)

    A coté de cela, il ne devrait avoir comme autre responsabilité à leur sujet que:
    • d'en demander la création (mais la création réelle devrait échoir à "quelque chose d'autre" (une fabrique qui ne fait que cela) )
    • Les détruire lorsqu'ils sont en fin de vie (raison pour laquelle je te conseille d'inverser les responsabilités à ce sujet )
    Il devrait, effectivement, être en mesure de dialoguer avec d'autres classes, mais ce n'est pas à lui de gérer ni l'IA, ni le son, ni l'affichage, ni les entrées utilisateur ni...

    Toutes ces responsabilités devraient être dévolues à des classes qui ne font à chaque fois qu'une chose bien particulière.

    Le stage se contentant de recevoir des messages émis par ces différentes classes et d'assurer le "passage de l'information" vers elles
    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

+ Répondre à la discussion
Cette discussion est résolue.
Page 1 sur 2 12 DernièreDernière

Discussions similaires

  1. appliquer une fonction à une fonction
    Par stracoma dans le forum C++
    Réponses: 6
    Dernier message: 20/03/2015, 16h35
  2. une fonction n'attend pas la fin de la précedente
    Par Romalafrite dans le forum Général JavaScript
    Réponses: 7
    Dernier message: 30/01/2007, 15h05
  3. Réponses: 7
    Dernier message: 24/01/2007, 10h01
  4. Assigné une fonction à une touche du clavier
    Par jay1234 dans le forum C++
    Réponses: 5
    Dernier message: 10/10/2006, 22h30
  5. passer en paramettre d'une fonction une fonction
    Par RoM3Ro dans le forum Général JavaScript
    Réponses: 4
    Dernier message: 23/06/2006, 15h54

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