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

Développement 2D, 3D et Jeux Discussion :

Gestionnaire d'evenements? Besoin de conseils et suggestions


Sujet :

Développement 2D, 3D et Jeux

  1. #1
    Futur Membre du Club
    Profil pro
    Inscrit en
    Août 2007
    Messages
    13
    Détails du profil
    Informations personnelles :
    Âge : 33
    Localisation : France

    Informations forums :
    Inscription : Août 2007
    Messages : 13
    Points : 7
    Points
    7
    Par défaut Gestionnaire d'evenements? Besoin de conseils et suggestions
    Salut à tous,

    Je postes ici, car j'ai décidé il y a quelques temps de réaliser un jeu de rôle, dans la lignée de Fallout ou Arcanum (avec un aspect et un gameplay plus réaliste).
    Le jeu sera programmé en C++, et je compte utiliser la librairie SDL (pour la gestion graphique, et peut ête le son), FMOD (que je n'ai pas encore testé, pour le son afin de remplacer SDL, selon mon humeur) et Qt4( utilisé pour concevoir les éditeurs qui accompagnent le jeu).

    La particularité de mon projet réside dans le fait que plutôt que de concevoir un jeu brut et compacté, je veux permettre au developpeur, tant à l'utilisateur de modifier le jeu, grâce à de nombreux éditeurs divers (monde, objets/décors, quêtes/script, etc), et mit à part le fonctionnement du jeu lui même qui ne changera pas, l'univers pourra être complètement interchangeable.
    En soit, je cherches plus à faire une plateforme de jeu de rôle, que le jeu lui même. Mon intêret dans le projet est d'aquérir de l'expérience en C++ en produisant quelque chose le plus modulable possible.

    Je tient à dire que je suis encore débutant en C++ (j'ai au moins les bases), alors ce projet n'est pas pour le moment voué à la finition. Je connais déjà le manège ^^ (qu'on aille pas me dire que c'est trop ambitieux), je suis au courant des difficultés que ça encourt , et je ne cherches que l'aspect "educatif", pas le résultat final, s'il y en a un.

    Actuellement, j'ai établit sur papier les lignes de mon jeu, les classes de bases, ainsi que la structure du moteur.
    J'ai aussi réalisé l'éditeur de monde, presque fonctionnel à 100% (manque le moteur qui va avec).

    Côté graphisme, sonore, ou même conception des classes du jeu, je n'aurais pas trop de problèmes je penses pour la suite (ce n'est pas ma première expérience video ludique), cependant, je ne suis pas trop sûr de la route à suivre pour la conception du moteur (je penses que dans le contexte de mon projet, le terme est approprié).

    En effet, je n'ai jamais conçu de moteur, et bien que je sâches déjà les composants de bases, et que je conçoive la logique d'un moteur, je patauge encore un peu pour la mise en oeuvre. Heureusement en m'appuyant de l'excellent tutorial de Pierre Schwartz (dispo ici :http://khayyam.developpez.com/articl...?page=sommaire), je suis parvenu à monter la structure de base.


    A partir de là, jaurais su m'en sortir, seulement, une chose me pose encore problème: le gestionnaire d'evenements.
    Dans l'absolu, il n'est pas necessaire, et une routine de méthodes sur pointeurs fonctionne aussi bien, mais le gestionnaire présente l'avantage de ne pas exposer les différents moteurs à leur voisin; de plus, il faciliterai grandement la tâche pour la prise en charge de script (quêtes etc), qui selon moi découlerai tout simplement d'un gestionnaire d'évenement dérivé (une sorte d'écoute d'évenement qui l'intercepte avec le gestionnaire principal, et s'occupe d'executer la conséquence)

    Dans l'absolu, je vois comment peut être composé un gestionnaire d'évenement, et comment il est censé fonctionner, mais mon souçi vient à la définition d'un évenement même.
    Je ne sais pas de quelle façon l'initialiser sachant qu'un évenement pourra être un peu n'importe quoi (dans le sens, pas quelque chose d'absolu). Voila des exemples types auquels je compte m'attendre:

    -MyPersonnage,"at",X,Y
    -MyPersonnage,"picked up",MyObjet
    -MyPersonnage,"speaking with",MyPersonnage2
    -"Night time"

    Vous constaterez que j'ai moi même assigné un sorte de syntaxe dans ces evenements, afin de bien différencier les renvoi vers un certain objet, un mot clé de type string, ou des valeurs.

    J'ai lu quelque part qu'un std::map convenait peut être à ce genre de routine (messages composés), mais j'ai beau chercher, impossible de trouver d'infos (sur les spécificités de ce conteneur), outre les prototypes de fonctions d'un STL map.
    Pour ce qui est de la généricité de la classe, j'ai cru comprendre qu'un template était ce qu'il me fallait.

    Le truc est que je n'arrives pas à concevoir comment mettre tout cela en place. Je viens donc vous demander ici votre avis sur la question, une suggestion peut être, ou tout autre méthodes que vous préconisez, ou que vous avez testé avec succès.

    A votre avis, est-ce que ma définition d'un évenement vous parait viable? Si oui comment la mettre en oeuvre (je n'ai pas besoin de code, juste un fil directeur, afin de ne pas me noye ^^). Dans le cas, où ma solution n'est pas idéale, ce que VOUS préconiseriez.

    Merci d'avance !!

  2. #2
    Membre confirmé Avatar de LapinGarou
    Homme Profil pro
    R&D Developer
    Inscrit en
    Octobre 2005
    Messages
    341
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Localisation : France, Var (Provence Alpes Côte d'Azur)

    Informations professionnelles :
    Activité : R&D Developer
    Secteur : Aéronautique - Marine - Espace - Armement

    Informations forums :
    Inscription : Octobre 2005
    Messages : 341
    Points : 479
    Points
    479
    Par défaut
    Ca m'intéresse aussi (pour mon moteur 3D), j'allais poser à peu près la même question.

  3. #3
    Rédacteur
    Avatar de bafman
    Profil pro
    Développeur informatique
    Inscrit en
    Novembre 2003
    Messages
    2 574
    Détails du profil
    Informations personnelles :
    Âge : 39
    Localisation : France, Paris (Île de France)

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

    Informations forums :
    Inscription : Novembre 2003
    Messages : 2 574
    Points : 5 323
    Points
    5 323
    Par défaut
    si j'ai bien compris, ce que tu souhaite, c'est de pouvoir exécuter du code lorsque le jeu se retrouve dans certaines configurations. en gros, par exemple, si le joueur approche de la porte, elle s'ouvre.

    pour ça, il te faut définir l'ensemble des événements possibles.
    par exemple, tu peut créer des sortes de zones de détection qui réagirons quand le le joueur ou une autre entité entre dedans...
    ensuite, il te faut un mécanisme pour réagir à ces événements. Tu peut faire ça en C++, mais le plus flexible est probablement d'utiliser un langage de scripts du type Lua ou python, qui va te permettre de changer le comportement sans avoir à recompiler tout le projet.

    par exemple, tu attache un script à chaque "détecteur", et, quand le détecteur est activé, tu appel une fonction prédéfinie dans le script. l'avantage de cette méthode, c'est qu'en plus, tu peut très bien avoir plusieurs fonctions appelé selon le type d'événement. Par exemple, un détecteur peut réagir à l'entrée du joueur, mais aussi à l'entrée d'un ennemi.
    * Il est infiniment plus simple de faire rapidement un code qui marche que de faire un code rapide qui marche
    * pour faciliter les recherches, n'oubliez pas de voter pour les réponses pertinentes
    Mes articles

  4. #4
    Futur Membre du Club
    Profil pro
    Inscrit en
    Août 2007
    Messages
    13
    Détails du profil
    Informations personnelles :
    Âge : 33
    Localisation : France

    Informations forums :
    Inscription : Août 2007
    Messages : 13
    Points : 7
    Points
    7
    Par défaut
    Salut à vous

    En effet, bafman, tu as bien compris ce que j'essaye de faire, et le type de fonctionnement que tu explique est ce que j'avais en tête.
    Pour ce qui est du langage de script, je compte mettre en place ma propre syntaxe et organisation de fichiers scripts (je ne sais pas ce qu'un langage déjà existant me rapporterai de plus).

    Mon souçi est juste que pour l'interface entres les différents moteur, et pour des raisons de généricité du type représenté par l'évenement, je ne sais pas comment en définir un.
    En gros, une fois que j'aurais défini ce qu'est un "event", je saurais me débrouiller seul pour monter une classe qui transmet les events entres classes.

    Comme j'ai écrit dans mon premier message, un event bien qu'une information définie, n'a pas de valeur définie, si bien qu'elle peut être exprimée de plusieurs façons (voir les exemples types que j'ai donné).

    Mon incertitude revient donc à la représentation et la définition d'une donnée de type event, compte tenu que je veux simplifier au plus ma classe (dans le sens de la rendre le plus générique possible).

  5. #5
    Rédacteur
    Avatar de bafman
    Profil pro
    Développeur informatique
    Inscrit en
    Novembre 2003
    Messages
    2 574
    Détails du profil
    Informations personnelles :
    Âge : 39
    Localisation : France, Paris (Île de France)

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

    Informations forums :
    Inscription : Novembre 2003
    Messages : 2 574
    Points : 5 323
    Points
    5 323
    Par défaut
    en fait, un événement, du point de vue du moteur, c'est quelque chose qui a besoin d'exécuter du code. tu peut donc t'inspirer du pattern Command pour mettre en place ton système.

    en gros, tu a une classe de base (ici, GameplayEvent par exemple) qui définit une méthode virtuelle execute.
    Comme tu peut avoir plusieurs type d'événements, mais que ceux ci ne prennent pas les même paramètres, tu ne peut pas passer les paramètres à la méthode execute... mais tu peut très bien les stocker dans la classe qui implémente l'événementiel avant d'appeler celui ci.
    tu te retrouvera donc avec un truc du genre :
    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
     
    class GameplayEvent
    {
        virtual void execute() = 0;
    }
     
    class OnPlayerEnter : public GameplayEvent
    {
    private:
        Player* player;
        ZoneDeDetection* zone;
        int ageDuCapitaine;
    public:
        OnPlayerEnter(Player* paramPlayer, ZoneDeDetection* paramZone, int paramAgeDuCapitaine) : 
        player(paramPlayer),zone(paramZone),ageDuCapitaine(paramAgeDuCapitaine)
        {}
     
        void execute()
        {
            // appel le code du script en cas d'entrée dans la zone du joueur. 
            // Ici on peut passer les paramètres car il sont connu
        }
    }
    et voilà, tu peut faire un système facilement extensible, tout en gérant les différents types de paramètres requis par tes événements...
    * Il est infiniment plus simple de faire rapidement un code qui marche que de faire un code rapide qui marche
    * pour faciliter les recherches, n'oubliez pas de voter pour les réponses pertinentes
    Mes articles

  6. #6
    Futur Membre du Club
    Profil pro
    Inscrit en
    Août 2007
    Messages
    13
    Détails du profil
    Informations personnelles :
    Âge : 33
    Localisation : France

    Informations forums :
    Inscription : Août 2007
    Messages : 13
    Points : 7
    Points
    7
    Par défaut
    Waou, merci pour la réponse, c'était du rapide !!

    Je commence à y voir plus clair: en soit tu créé une classe dérivée de GameplaysEvent pour chaque evenement?
    Cette méthode me plait bien , et est très proche du fonctionnement que je recherchais, et ça parait assez simple à gérer.

    Maintenant, reste à savoir si c'est "léger" comme méthode, au niveau des perfs et ressources (je penses que ça dépant des attributs par event)?
    Est-ce que une classe "vide" necessite beaucoups de ressources?
    Je demandes cela, car bien que l'évenement n'est pas omniprésent, au lancement du programme, chaque classe en référence à un event doit être créée.

    De plus, avec cette méthode, je me demandes s'il est préférable implémenter les events à l'intérieur du moteur concerné (celui qui le detecte, ex: Position du joueur, dans le moteur de jeu, car il contient les données, etc...), ou de façon externe sur un fichier à part (c'est peut être plus propre, non?)

    En tout cas, tu viens de me filer une belle bouée , je conçois à présent mieu comment mettre en place le système que je prévoyais, merci beaucoups pour ton aide !!

  7. #7
    Membre éclairé Avatar de HanLee
    Profil pro
    Inscrit en
    Mai 2004
    Messages
    738
    Détails du profil
    Informations personnelles :
    Âge : 37
    Localisation : France, Rhône (Rhône Alpes)

    Informations forums :
    Inscription : Mai 2004
    Messages : 738
    Points : 871
    Points
    871
    Par défaut
    Citation Envoyé par bafman Voir le message
    en fait, un événement, du point de vue du moteur, c'est quelque chose qui a besoin d'exécuter du code. tu peut donc t'inspirer du pattern Command pour mettre en place ton système.
    C'est donc une fonction.
    Pourquoi ne pas faire plus simple et utiliser boost::function ou boost::signal par exemple ? Ils sont faits pour ça.

    Ca économiserait du code, en évitant de définir une nouvelle classe chaque fois qu'il y a un nouvel événement à créer.
    On écrit juste une fonction libre, on lie des arguments si nécessaire, et hop, c'est fini.

    Le pattern Command c'est typiquement pour un langage (souvent OO) qui ne considère pas les fonctions comme des types de données normaux ; en C++ on a la chance de pouvoir simuler ça très facilement.

    Par contre, il faudrait comparer les performances entre des appels virtuels et les appels de boost::function / boost::signal. Je pense que ça doit être comparable.
    La documentation précise qu'il y a maximum 2 déréférencements pour boost::function. C'est pareil pour les appels virtuels il me semble.

  8. #8
    Rédacteur
    Avatar de bafman
    Profil pro
    Développeur informatique
    Inscrit en
    Novembre 2003
    Messages
    2 574
    Détails du profil
    Informations personnelles :
    Âge : 39
    Localisation : France, Paris (Île de France)

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

    Informations forums :
    Inscription : Novembre 2003
    Messages : 2 574
    Points : 5 323
    Points
    5 323
    Par défaut
    Citation Envoyé par HanLee Voir le message
    C'est donc une fonction.
    pas forcement, l'objectif etant à terme d'utiliser un langage de script...
    Citation Envoyé par HanLee Voir le message
    Pourquoi ne pas faire plus simple et utiliser boost::function ou boost::signal par exemple ? Ils sont faits pour ça.
    je ne connais pas trop. A quel point permettent-il la réification du concept de passage de paramètres qui un objet à interface commune ?
    Citation Envoyé par HanLee Voir le message
    Ca économiserait du code, en évitant de définir une nouvelle classe chaque fois qu'il y a un nouvel événement à créer.
    On écrit juste une fonction libre, on lie des arguments si nécessaire, et hop, c'est fini.
    oui, mais l'intérêt de mon système est que les paramètres sont transporté avec l'objet, donc il sont toujours accessibles, y compris dans le langage de script.
    Citation Envoyé par HanLee Voir le message
    Le pattern Command c'est typiquement pour les langages (en général OO) qui ne peuvent se permettre de considérer les fonctions comme des types de données normaux ; en C++ on a la chance de pouvoir simuler ça très facilement.
    oui, mais des langages de scriptes qui gèrent un équivalent au templates, je n'en connais pas

    Citation Envoyé par HanLee Voir le message
    Par contre, il faudrait comparer les performances entre des appels virtuels et les appels de boost::function / boost::signal. Je pense que ça doit être comparable.
    La documentation précise qu'il y a maximum 2 déréférencements pour boost::function. C'est pareil pour les appels virtuels il me semble.
    c'est très probablement équivalent (et puis, ce ne sont pas des parties critiques d'un moteur de jeu)
    * Il est infiniment plus simple de faire rapidement un code qui marche que de faire un code rapide qui marche
    * pour faciliter les recherches, n'oubliez pas de voter pour les réponses pertinentes
    Mes articles

  9. #9
    Membre éclairé Avatar de HanLee
    Profil pro
    Inscrit en
    Mai 2004
    Messages
    738
    Détails du profil
    Informations personnelles :
    Âge : 37
    Localisation : France, Rhône (Rhône Alpes)

    Informations forums :
    Inscription : Mai 2004
    Messages : 738
    Points : 871
    Points
    871
    Par défaut
    je ne connais pas trop. A quel point permettent-il la réification du concept de passage de paramètres qui un objet à interface commune ?

    oui, mais l'intérêt de mon système est que les paramètres sont transporté avec l'objet, donc il sont toujours accessibles, y compris dans le langage de script.
    Ben, ça se fait facilement dans les langages fonctionnels.

    Tu peux réduire le nombre de paramètre d'une fonction en lui fixant certains de ses arguments en faisant une application partielle (curryfication pour les intimes ).

    Imagine que chaque commande est définie par une fonction void -> void :

    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    using boost::function;
    typedef function<void ()> command;
    Et qu'on a des fonctions censées exécuter du code, qui sont paramétrées par bien plus d'argument, alors on sait les transformer en fonctions qui ne prennent pas d'argument

    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    // Machin qui s'exécute quand le joueur arrive
    void OnPlayerEnter(Player* player, ZoneDeDetection* zone, int ageDuCapitaine);
     
    Player* p = ...;
    ZoneDeDetection* zone = ...;
    int age = 30;
     
    using boost::bind;
     
    // Application partielle de fonction...
    command cmd1 = bind(&OnPlayerEnter, p, zone, age);
     
    // Appel de la commande
    cmd1(); // Appelle OnPlayerEvent(p, zone, age)
    Evidemment, boost::function est copiable.

  10. #10
    Futur Membre du Club
    Profil pro
    Inscrit en
    Août 2007
    Messages
    13
    Détails du profil
    Informations personnelles :
    Âge : 33
    Localisation : France

    Informations forums :
    Inscription : Août 2007
    Messages : 13
    Points : 7
    Points
    7
    Par défaut
    Salut à toi HanLee ,

    Merci pour vos messages additionnels, ainsi que votre débat , j'ai l'embarras du choix maintenant !!

    J'avais déjà entrevu et entendu parler de boost, sans avoir eût encore besoin d'y recourir. Je viens de faire une courte recherche, et j'ai découvert les possibilités offertes.
    Pour ce qui est de ton idée, HanLee, du boost::signal, ça m'a l'air tout aussi faisable, que la solution proposée par bafman.
    Comme ce lien en témoigne, boost::signal semble accepter des paramètres pour le signal, puisqu'il ne s'agit que d'une fonction void.

    http://www.boost.org/doc/html/signals/tutorial.html

    De plus, en recherchant un peu plus sur boost, j'ai découvert plusieurs autres propriétés auquelles je comptais déjà faire appel, d'une manière ou d'une autre .

    Un grand merci pour votre aide donc, je m'attendais pas à des réponses aussi pertinentes et claires .

    Seule chose que je n'ai pas trop saisi est le rapport avec le transport d'objet :

    bafman: oui, mais l'intérêt de mon système est que les paramètres sont transporté avec l'objet, donc il sont toujours accessibles, y compris dans le langage de script.
    Si le signal est à la base un prototype de fonction (dans le cas du boost::signal donc), il doit être capable de transporter l'objet à la manière d'un pointeur dans ses paramètres non?
    Cela dit, l'event n'est pas forcément obligé de pouvoir manipuler ses paramètres, car à la différence de la conséquence, l'event doit être passif (la seule chose qui reste active pour l'event est son émission)

  11. #11
    Rédacteur
    Avatar de bafman
    Profil pro
    Développeur informatique
    Inscrit en
    Novembre 2003
    Messages
    2 574
    Détails du profil
    Informations personnelles :
    Âge : 39
    Localisation : France, Paris (Île de France)

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

    Informations forums :
    Inscription : Novembre 2003
    Messages : 2 574
    Points : 5 323
    Points
    5 323
    Par défaut
    Citation Envoyé par Althar Voir le message
    Seule chose que je n'ai pas trop saisi est le rapport avec le transport d'objet :



    Si le signal est à la base un prototype de fonction (dans le cas du boost::signal donc), il doit être capable de transporter l'objet à la manière d'un pointeur dans ses paramètres non?
    Cela dit, l'event n'est pas forcément obligé de pouvoir manipuler ses paramètres, car à la différence de la conséquence, l'event doit être passif (la seule chose qui reste active pour l'event est son émission)
    le problème, c'est de passer les objets entre les langages. Si tu fait ton propre langage, tu peut très bien faire un système qui va effectivement passer un handler vers les objets, mais ce n'est pas forcement possible/facile avec tous les langages de scripts.
    * Il est infiniment plus simple de faire rapidement un code qui marche que de faire un code rapide qui marche
    * pour faciliter les recherches, n'oubliez pas de voter pour les réponses pertinentes
    Mes articles

  12. #12
    Membre actif
    Profil pro
    Inscrit en
    Octobre 2005
    Messages
    267
    Détails du profil
    Informations personnelles :
    Localisation : France

    Informations forums :
    Inscription : Octobre 2005
    Messages : 267
    Points : 275
    Points
    275
    Par défaut
    Je profite de ce topic, car j'ai moi aussi des questions à ce sujet (et puis ça m'évite d'en ouvrir un nouveau et de donner celui-ci en lien...).

    Pour qu'un gestionnaire A fasse faire quelque chose à un gestionnaire B, on a 2 solutions:
    1) utiliser une fonction de B directement
    2) envoyer à B un message

    Je vois bien l'intéret de 2) dans le cas d'un jeu multijoueur.
    Y en a-t-il dans le cas d'un jeu monojoueur?

    Bon dans le cas de 2):
    La méthode fournit par bafman est très bien (rien à redire...).
    Par contre l'autre méthode (en utilisant boost) ne me convint pas (je ne vois pas le bénéfice qu'elle apporte).
    Citation Envoyé par HanLee
    Ca économiserait du code, en évitant de définir une nouvelle classe chaque fois qu'il y a un nouvel événement à créer.
    On écrit juste une fonction libre, on lie des arguments si nécessaire, et hop, c'est fini.
    Oui mais la classe n'est en fait qu'une fonction Execute() (plus quelques données membres). On n'économise vraiment pas grand chose...

    Toujours dans le cas 2):
    Au bout du compte, les évènements vont faire quelques petits trucs, puis lancer une fonction du manager B (non?). Donc pourquoi ne pas utiliser une fonction de B directement? (oui je saisn c'est redondant avec ma première question, mais bon...)

  13. #13
    Rédacteur
    Avatar de bafman
    Profil pro
    Développeur informatique
    Inscrit en
    Novembre 2003
    Messages
    2 574
    Détails du profil
    Informations personnelles :
    Âge : 39
    Localisation : France, Paris (Île de France)

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

    Informations forums :
    Inscription : Novembre 2003
    Messages : 2 574
    Points : 5 323
    Points
    5 323
    Par défaut
    Citation Envoyé par Albenejean Voir le message
    Je vois bien l'intéret de 2) dans le cas d'un jeu multijoueur.
    Y en a-t-il dans le cas d'un jeu monojoueur?
    en fait, l'avantage de cette méthode n'est pas tellement en multi joueurs, mais plutôt lorsqu'on a un moteur multi processus, ou les problèmes de synchronisation entre thread sont généralement plus facile à résoudre avec des envois de messages.
    * Il est infiniment plus simple de faire rapidement un code qui marche que de faire un code rapide qui marche
    * pour faciliter les recherches, n'oubliez pas de voter pour les réponses pertinentes
    Mes articles

  14. #14
    Membre actif
    Profil pro
    Inscrit en
    Octobre 2005
    Messages
    267
    Détails du profil
    Informations personnelles :
    Localisation : France

    Informations forums :
    Inscription : Octobre 2005
    Messages : 267
    Points : 275
    Points
    275
    Par défaut
    en fait, l'avantage de cette méthode n'est pas tellement en multi joueurs, mais plutôt lorsqu'on a un moteur multi processus, ou les problèmes de synchronisation entre thread sont généralement plus facile à résoudre avec des envois de messages.
    Effectivement... Merci...

  15. #15
    Membre éclairé Avatar de HanLee
    Profil pro
    Inscrit en
    Mai 2004
    Messages
    738
    Détails du profil
    Informations personnelles :
    Âge : 37
    Localisation : France, Rhône (Rhône Alpes)

    Informations forums :
    Inscription : Mai 2004
    Messages : 738
    Points : 871
    Points
    871
    Par défaut
    Une remarque : boost.signal n'est pas thread-safe, et je n'en ai aucune idée non plus pour boost.function.

    Citation Envoyé par Albenejean Voir le message
    Je profite de ce topic, car j'ai moi aussi des questions à ce sujet (et puis ça m'évite d'en ouvrir un nouveau et de donner celui-ci en lien...).

    Pour qu'un gestionnaire A fasse faire quelque chose à un gestionnaire B, on a 2 solutions:
    1) utiliser une fonction de B directement
    2) envoyer à B un message

    Je vois bien l'intéret de 2) dans le cas d'un jeu multijoueur.
    Y en a-t-il dans le cas d'un jeu monojoueur?

    Bon dans le cas de 2):
    La méthode fournit par bafman est très bien (rien à redire...).
    Par contre l'autre méthode (en utilisant boost) ne me convint pas (je ne vois pas le bénéfice qu'elle apporte).

    Oui mais la classe n'est en fait qu'une fonction Execute() (plus quelques données membres). On n'économise vraiment pas grand chose...
    Ben qu'est-ce que je peux répondre ?

    C'est juste que c'est plus naturel, que Boost.Signal est fait pour ça, et que normalement ce n'est pas le rôle d'une classe de faire ça, car comme tu le dis, ces classes ne sont que des fonctions. En programmation impérative, une action se modélise naturellement par une fonction.

    C'est cette méthode qu'on utilisait en C, pour la gestion d'événements (et qu'on continue à utiliser), mais avec des pointeurs de fonctions.

    Seulement techniquement le C ne permettait pas une grande souplesse pour manipuler des fonctions (curryfier notamment). OK, en C++ c'est pas la panacée, mais ça devient beaucoup plus souple avec le boulot de boost.bind (initié par la SL, dans le header functional on trouve std::bind1st, et std::bind2nd)

    Après, l'avantage d'avoir une hiérarchie d'actions (OnPlayerEnter n'est pas un événement, mais la réponse à un événement) sous forme de classe, c'est que l'ensemble des actions que l'on peut enregistrer au "gestionnaire" d'événements (au fond il ne fait qu'exécuter des actions) est fini.

    A part ça, c'est la même chose.

    C'est comme si pour un algorithme de tri, je te demandais un prédicat de comparaison sous forme d'un objet qui implémente l'interface IComparator en guise de relation d'ordre, alors que fondamentalement ça n'est qu'une fonction binaire qui renvoie un booléen.

    Implémenter l'interface IComparator ne met pas beaucoup plus de temps qu'écrire une fonction, mais ça ne reste qu'une voie détournée d'arriver à son but et au bout d'un moment, on s'en rend bien compte.

  16. #16
    Futur Membre du Club
    Profil pro
    Inscrit en
    Août 2007
    Messages
    13
    Détails du profil
    Informations personnelles :
    Âge : 33
    Localisation : France

    Informations forums :
    Inscription : Août 2007
    Messages : 13
    Points : 7
    Points
    7
    Par défaut
    Salut à tout les arrivants !

    Grâce à vos conseils, je suis parvenu à monter quelque chose de plus satisfaisant, cela dit, quelques problèmes résident ...

    Jusqu'à présent, j'ai définit une classe event (la classe de base), et je fais hériter toutes les classes qui définissent les différents évenements possible.

    Par la suite, chaque module peux à sa convenance émettre un de ces évenement (en spécifiant les paramètres s'il y en a), qui est ensuite envoyé dans le "event manager" (une nouvelle classe, qui stocke tout les évenements, et joue le rôle d'intermédiaire entre chaque module).

    Je ne veux pas de liaison directe entre l'event et son effet (m'a l'air d'être le cas du boost::signal), car la conséquence doit pouvoir être tout est n'importe quoi (sur le principe du "script manager" qui devance l'event manager, lorsqu'un évenement l'interresse, et qui s'occupe d'indiquer la conséquence, définie par le script, à executer).

    Chaque module possède son système d'écoute, et récupère donc les évenements qui l'intéresse. Une fois que chaque module a écouté la liste d'évenements, on peux la vider (un même event peut être capté par plusieurs modules, chacun ayant une tâche à effectuer).

    Jusque là donc, je n'ai pas de problème, et tout fonctonne nickel ...

    Le seul souçi est que je n'ai aucun moyen de récupérer les attributs de l'event: ce du au fait qu'un évenement est avant tout de la classe "event".

    En gros, le compileur me dira juste : 'class event' has no member named 'v1', si j'essaye de récupérer de l'evenement émit, qui lui contient bien ce v1, mais est detecté en tant que "event" tout court, et non en tant que lui même.

    C'est comme je vous le disais dans mon premier message, il est impossible de savoir ce que va être l'évenement (paramètres, nombre de paramètres), donc même si je peux au moins detecter la signature de l'evenement (via un membre public pour chaque event), je ne parviens pas à en extraire les paramètres (ce qui était le but...)

    Je ne me suis un peu penché sur les boost::function ou boost::bind, mais même après ce que j'ai lu, je n'ai toujours pas capté à quoi ça pourrait bien servir dans mon cas présent.
    Pour ce qui est du boost::signal, j'ai l'impression que c'est du type direct, et donc que je ne peux pas utiliser mon "event manager".

  17. #17
    Membre éclairé Avatar de HanLee
    Profil pro
    Inscrit en
    Mai 2004
    Messages
    738
    Détails du profil
    Informations personnelles :
    Âge : 37
    Localisation : France, Rhône (Rhône Alpes)

    Informations forums :
    Inscription : Mai 2004
    Messages : 738
    Points : 871
    Points
    871
    Par défaut
    Là j'ai pas trop le temps, mais je te dirais juste que tu confonds 2 choses distinctes :

    - l'événement en lui-même
    - la réponse à l'événement

    En fait la classe Event porte mal son nom. Elle désigne une action ! Elle devrait s'appeler OnEvent, c'est à dire la réponse à un événement.
    Regarde toutes les classes dérivées : elles ont toutes un nom qui commence par 'On'. Elles représentent la réponse à un événement, et non pas l'événement lui même, c'est pour ça qu'on peut choisir de les représenter par des fonctions tout simplement.

    C'est ça qui cause ton problème de design. Tu ne devrais pas chercher à accéder aux membres des classes dérivées de OnEvent, elles font parties du contexte de la fonction/action à exécuter.

    -----

    boost.signal c'est pareil que boost.function, sauf qu'un signal est connecté à plusieurs actions et pas boost.function. On peut le voir comme un conteneur d'actions. Utilise soit l'un, soit l'autre.

  18. #18
    Rédacteur
    Avatar de bafman
    Profil pro
    Développeur informatique
    Inscrit en
    Novembre 2003
    Messages
    2 574
    Détails du profil
    Informations personnelles :
    Âge : 39
    Localisation : France, Paris (Île de France)

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

    Informations forums :
    Inscription : Novembre 2003
    Messages : 2 574
    Points : 5 323
    Points
    5 323
    Par défaut
    Citation Envoyé par HanLee Voir le message
    Là j'ai pas trop le temps, mais je te dirais juste que tu confonds 2 choses distinctes :

    - l'événement en lui-même
    - la réponse à l'événement
    en fait, il y a tris concepts distincts :
    - la détection de l'événement (toto entre dans piece...)
    - la réponse à l'événement (spawn dragon)
    - le contexte de l'événement dont a besoin la réponse(toto est à tel position, la pièce fait 3m carré)

    le problème, c'est que rien de garantit que la réponse doit être fait au moment de la détection. Il faut donc pouvoir transporter le contexte, et avoir un moyen générique de dire à l'évènement "maintenant, exécute ta réaction".

    on se retrouve donc avec une architecture où on a une classe de base définissant l'évènement et sa méthode de réaction. Mais, le problème, c'est qu'au moment ou on veut exécuter l'évènement, on a bien le contexte stocké dans l'objet event, mais on ne peut plus y accéder car on ne connais pas son type... d'où l'utilité d'avoir notre méthode exécute, qui elle connais le contexte (normale, elle est redéfini dans la sous classe). On peut donc, depuis cette méthode, appeler du code spécifique à ce type d'évènement (via un pointeur de fonction, un foncteur, un signal... tout ce qu'on veut qui nous permet d'exécuter du code avec une signature spécifique à ce type d'évènement)...
    voilà une base d'archi :
    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
     
    class Event
    {
    public:
        virtual void execute() = 0; // comme avant ;-)
    }
     
    class OnPlayerOpenDoor
    {
    public: // je met en plublic pour simplifier
        Player* player;
        Door* door;
        Room* origine;
        Room* destination;
        boost::function<Player*,Door*, Room*, Room*> codeToExecute; // je sait pas si ca se fait comme ca...
        // le principe c'est que ça exécute une fonction qui prend en paramètres
        // le joueur, la porte et les deux salles
     
        void execute()
        {
            // ici, on a acces au paramètres de la classe, on peut donc appeler la
            // fonction pour executer l'action...
            codeToExecute(player,door,origine,destination);
            // ici, à la place, on peut aussi appeler une fonction d'un script au lieu d'une fonction en c++ ;-)
        }
    }
    comme ça, même si on souhaite stocker l'évènement pour l'exécuter plus tard, ça marche toujours, car c'est l'événement lui même qui sait comment il doit s'exécuter et non le manager (qui ne fait que déclencher l'exécution)
    * Il est infiniment plus simple de faire rapidement un code qui marche que de faire un code rapide qui marche
    * pour faciliter les recherches, n'oubliez pas de voter pour les réponses pertinentes
    Mes articles

  19. #19
    Membre éclairé Avatar de HanLee
    Profil pro
    Inscrit en
    Mai 2004
    Messages
    738
    Détails du profil
    Informations personnelles :
    Âge : 37
    Localisation : France, Rhône (Rhône Alpes)

    Informations forums :
    Inscription : Mai 2004
    Messages : 738
    Points : 871
    Points
    871
    Par défaut
    Citation Envoyé par bafman Voir le message
    en fait, il y a tris concepts distincts :
    - la détection de l'événement (toto entre dans piece...)
    - la réponse à l'événement (spawn dragon)
    - le contexte de l'événement dont a besoin la réponse(toto est à tel position, la pièce fait 3m carré)

    le problème, c'est que rien de garantit que la réponse doit être fait au moment de la détection. Il faut donc pouvoir transporter le contexte, et avoir un moyen générique de dire à l'évènement "maintenant, exécute ta réaction".

    on se retrouve donc avec une architecture où on a une classe de base définissant l'évènement et sa méthode de réaction. Mais, le problème, c'est qu'au moment ou on veut exécuter l'évènement, on a bien le contexte stocké dans l'objet event, mais on ne peut plus y accéder car on ne connais pas son type... d'où l'utilité d'avoir notre méthode exécute, qui elle connais le contexte (normale, elle est redéfini dans la sous classe). On peut donc, depuis cette méthode, appeler du code spécifique à ce type d'évènement (via un pointeur de fonction, un foncteur, un signal... tout ce qu'on veut qui nous permet d'exécuter du code avec une signature spécifique à ce type d'évènement)...
    voilà une base d'archi :
    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
     
    class Event
    {
    public:
        virtual void execute() = 0; // comme avant ;-)
    }
     
    class OnPlayerOpenDoor
    {
    public: // je met en plublic pour simplifier
        Player* player;
        Door* door;
        Room* origine;
        Room* destination;
        boost::function<Player*,Door*, Room*, Room*> codeToExecute; // je sait pas si ca se fait comme ca...
        // le principe c'est que ça exécute une fonction qui prend en paramètres
        // le joueur, la porte et les deux salles
     
        void execute()
        {
            // ici, on a acces au paramètres de la classe, on peut donc appeler la
            // fonction pour executer l'action...
            codeToExecute(player,door,origine,destination);
            // ici, à la place, on peut aussi appeler une fonction d'un script au lieu d'une fonction en c++ ;-)
        }
    }
    comme ça, même si on souhaite stocker l'évènement pour l'exécuter plus tard, ça marche toujours, car c'est l'événement lui même qui sait comment il doit s'exécuter et non le manager (qui ne fait que déclencher l'exécution)
    En fait, utiliser boost.function comme ça ne sert à rien, autant écrire le code dans execute() directement.

    boost.function sert à encapsuler tout ce qui est assimilable à une fonction, et c'est boost.bind qu'on utilise pour passer un contexte à la fonction.

    Toutes les classes qui s'appellent On_quelque_chose seront remplacées par des boost.function de type boost::function<void ()>.

    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    typedef boost::function<void ()> OnEvent;
    Et je crée la réponses "playerEnter" à l'événement, en passant un contexte à l'aide de boost.bind (fonction aura été définie auparavant...) :

    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    OnEvent onPlayerEnter = boost::bind(&playerEnter, player, door, origin, destination);
    "fonction" qui prenait au départ 4 arguments, s'est transformée en un foncteur qui prend 0 paramètre, parce que Boost.Bind lui a donné le contexte (elles sont passées par copie, voir la doc pour plus d'infos).

    En pratique, à chaque fois qu'on enregistre un événement concernant une personne, on aura un container de réponses à un événement, plutôt qu'une seule réponse.

    C'est un pattern Command implicite, on n'a pas de classe Command, avec son execute, mais entre command.execute() et command() la différence n'est que syntaxique.

    Un exemple trouvé sur internet : http://www.codeproject.com/library/B...ndFunction.asp

  20. #20
    Rédacteur
    Avatar de bafman
    Profil pro
    Développeur informatique
    Inscrit en
    Novembre 2003
    Messages
    2 574
    Détails du profil
    Informations personnelles :
    Âge : 39
    Localisation : France, Paris (Île de France)

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

    Informations forums :
    Inscription : Novembre 2003
    Messages : 2 574
    Points : 5 323
    Points
    5 323
    Par défaut
    Citation Envoyé par HanLee Voir le message
    En fait, utiliser boost.function comme ça ne sert à rien, autant écrire le code dans execute() directement.
    non, car ici, ce qu'on souhaite, c'est avoir un système générique pour exécuter des méthodes dans un langage de script. Dans mon exemple j'appel une fonction, mais idéalement, il faudrait appeler le langage de script, avec les bon paramètre pour le type d'événements...
    * Il est infiniment plus simple de faire rapidement un code qui marche que de faire un code rapide qui marche
    * pour faciliter les recherches, n'oubliez pas de voter pour les réponses pertinentes
    Mes articles

Discussions similaires

  1. Réponses: 4
    Dernier message: 20/05/2005, 14h30
  2. .htaccess - url rewriting, besoin de conseils
    Par giminik dans le forum Apache
    Réponses: 2
    Dernier message: 25/04/2005, 21h18
  3. [C#] [ADO.NET] Besoin de conseil
    Par djsbens dans le forum Accès aux données
    Réponses: 8
    Dernier message: 01/04/2005, 16h04
  4. Réponses: 3
    Dernier message: 24/12/2004, 13h21
  5. Réponses: 1
    Dernier message: 06/01/2003, 08h55

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