Bonne question! Avant de passer aux questions spécifiques que vous avez posées, je dirai: ne sous-estimez pas le pouvoir de la simplicité. Tenpn a raison. Gardez à l'esprit que ces approches ne font que chercher un moyen élégant de différer un appel de fonction ou de découpler l'appelant de l'appelé. Je peux recommander des coroutines comme un moyen étonnamment intuitif d'atténuer certains de ces problèmes, mais c'est un peu hors sujet. Parfois, il vaut mieux appeler simplement la fonction et vivre avec le fait que l'entité A est couplée directement à l'entité B. Voir YAGNI.
Cela dit, je suis satisfait du modèle signal / emplacement associé à la transmission simple de messages. Je l'ai utilisé en C ++ et Lua pour un titre iPhone assez réussi et dont le programme était très serré.
Pour le cas signal / slot, si je veux que l'entité A fasse quelque chose en réponse à quelque chose que l'entité B a fait (par exemple, déverrouiller une porte lorsque quelque chose meurt), il est possible que l'entité A souscrive directement à l'événement de mort de l'entité B. Ou peut-être que l'entité A souscrirait à chacune des entités d'un groupe, incrémenterait un compteur à chaque événement déclenché et déverrouillerait la porte après la mort de N d'entre elles. En outre, "groupe d'entités" et "N d'entre eux" seraient généralement définis par le concepteur dans les données de niveau. (En passant, c’est un domaine où les coroutines peuvent vraiment briller, par exemple WaitForMultiple ("Mourir", entA, entB, entC); door.Unlock ();)
Mais cela peut devenir fastidieux quand il s’agit de réactions étroitement liées au code C ++ ou à des événements de jeu intrinsèquement éphémères: infliger des dégâts, recharger des armes, déboguer, un retour d’information basé sur la localisation, dirigé par le joueur. C’est là que le passage du message peut combler les lacunes. Cela revient essentiellement à quelque chose comme: "Dites à toutes les entités de cette zone de subir des dégâts en 3 secondes" ou "chaque fois que vous terminez la physique pour déterminer qui j'ai tiré, dites-leur d'exécuter cette fonction de script". Il est difficile de comprendre comment faire cela en utilisant publication / abonnement ou signal / slot.
Cela peut facilement être exagéré (par rapport à l'exemple de Tenpn). Cela peut aussi être inefficace si vous avez beaucoup d'action. Mais malgré ses inconvénients, cette approche "messages et événements" se marie très bien avec le code de jeu scripté (par exemple en Lua). Le code de script peut définir et réagir à ses propres messages et événements sans que le code C ++ ne s'en soucie. De plus, le code de script peut facilement envoyer des messages qui déclenchent du code C ++, tels que la modification de niveaux, la reproduction de sons ou même le simple fait de laisser une arme définir les dégâts causés par le message TakeDamage. Cela m'a fait gagner beaucoup de temps, car je n'avais pas à m'amuser avec luabind. Et cela m'a permis de garder tout mon code luabind au même endroit, car il n'y en avait pas beaucoup. Lorsqu'il est correctement couplé,
De plus, mon expérience avec le cas d'utilisation n ° 2 est qu'il vaut mieux que vous le traitiez comme un événement dans l'autre sens. Au lieu de demander quel est l'état de santé de l'entité, déclenchez un événement / envoyez un message chaque fois que l'état de santé effectue un changement important.
En termes d'interfaces, d'ailleurs, j'ai eu trois classes pour implémenter tout cela: EventHost, EventClient et MessageClient. EventHosts crée des logements, EventClients s'y abonne / se connecte et MessageClients associe un délégué à un message. Notez que la cible déléguée d'un MessageClient n'a pas nécessairement besoin d'être le même objet que celui qui possède l'association. En d'autres termes, MessageClients peut exister uniquement pour transférer des messages vers d'autres objets. FWIW, la métaphore hôte / client est plutôt inappropriée. Source / Sink pourrait être de meilleurs concepts.
Désolé, j'ai un peu perdu la tête. C'est ma première réponse :) J'espère que cela a du sens.