Quelle technique dois-je utiliser pour faciliter la communication entre les XNA GameComponents (ou entre les composants de tout type dans un jeu)?

9

Je commence mon premier projet de jeu «approprié» et j'ai inévitablement heurté un bloc en essayant de décider comment les composants du jeu dans XNA devraient communiquer.

À partir des événements de programmation GUI (Java) précédents, les gestionnaires et les écouteurs semblaient être la voie à suivre. J'aurais donc une sorte de bus d'événements qui accepte les inscriptions d'événements et les classes abonnées à ces événements, avec des gestionnaires pour les gérer. Par exemple (pseudocode):

class SpriteManager
    Update(){
        if(player.collidesWith(enemy)
             // create new 'PlayerCollisionEvent'
    }

class HUDManager
    onPlayerCollisionEvent(){
        // Update the HUD (reduce lives etc)
    }

Cependant, je ne suis pas sûr de la configuration du code (en C #) qui serait nécessaire pour accomplir pleinement cela. Qu'est-ce qui garde la trace des événements (une sorte de bus?), Et comment est-il structuré?

Il semble également y avoir beaucoup de choses sur les services de jeu, où vous pouvez enregistrer un GameComponent dans votre classe Game.cs principale, puis le récupérer de n'importe où dans votre code qui a une référence à l'objet principal `` Game ''. J'ai essayé cela avec mon objet SpriteBatch et cela semble très facile .. cependant, je ne vois pas que cela soit aussi flexible qu'un modèle d'événement.

Prenez par exemple quand un ennemi meurt. Nous voulons mettre à jour le score du match. En utilisant les services, je peux obtenir une référence à mon objet StateManager créé dans Game1 et ajouté en tant que service, puis définir «score» sur la nouvelle valeur. J'aurais pensé qu'un événement `` onEnemyDeath '', qui peut être géré différemment par une multitude de classes, mais initié par 1 ligne de code dans la section `` détection de mort ennemie '', serait mieux que de lancer individuellement chaque GameComponent requis puis d'appeler ce que des méthodes sont nécessaires.

Ou ces stratégies sont-elles inférieures à autre chose?

Je me rends compte que c'est en partie ma mauvaise connaissance de C # autant que les paradigmes de communication de jeu, mais j'aimerais vraiment bien comprendre cette chose fondamentale.

Mise à jour

Ayant examiné les services est plus détaillé, je suis moins convaincu - il s'agit essentiellement de passer une variable globale (d'après ce que je comprends).

Mise à jour 2

Après avoir regardé ce didacticiel de base sur la gestion des événements et le test de l'exemple de code, il semble que les événements seraient un choix logique pour ce dont je discute. Mais je ne peux pas en utiliser beaucoup dans les échantillons que j'ai vus. Y a-t-il une raison évidente pour laquelle on ne devrait pas?

codinghands
la source
Je vous recommande d'essayer d'abord FlatRedBall. Il se trouve au sommet de XNA et rend la vie vraiment facile.
ashes999
3
J'aimerais vraiment comprendre les bases de ce qui se passe avant de passer à un moteur.
codinghands
Je pense que votre échec ici est vraiment un manque d'informations sur C #. C # utilise généralement des événements pour faciliter la communication entre les classes. C'est vraiment à vous de savoir comment le faire - XNA fournit une classe SpriteBatch, et la plupart du travail est à vous d'écrire. Un moteur vous donnera une idée solide du fonctionnement des choses à un niveau supérieur avant de plonger dans les détails.
ashes999
1
Je suis d'accord dans une certaine mesure, mais d'après ce que j'ai lu, la plupart des communications interclasse entre GameComponents peuvent être effectuées à l'aide des Services. J'ai l'habitude d'utiliser des événements. Je ne sais pas quelle est la meilleure, ou s'il existe des alternatives valides (singletons, classes statiques se faisant passer pour des globales comme 2 autres méthodes que j'ai omises)
codinghands

Réponses:

4

À partir des événements de programmation GUI (Java) précédents, les gestionnaires et les écouteurs semblaient être la voie à suivre. J'aurais donc une sorte de bus d'événements qui accepte les inscriptions d'événements et les classes abonnées à ces événements, avec des gestionnaires pour les gérer.

La raison pour laquelle vous ne trouverez pas grand-chose sur les bus d'événements en C # est parce que je ne pense pas que quiconque en dehors de Java appelle une telle chose un «bus», selon mon expérience. Les termes les plus courants peuvent être file d'attente de messages, gestionnaire d'événements, signaux / emplacements, publication / abonnement, etc.

Vous n'avez besoin d'aucun de ceux-ci pour faire un jeu fonctionnel, mais si c'est le type de système avec lequel vous êtes à l'aise, il vous sera également utile ici. Malheureusement, personne ne peut vous dire exactement comment en faire un car tout le monde les roule un peu différemment, s’ils les utilisent même du tout. (Je ne le fais pas, par exemple.) Vous pouvez commencer avec un simple System.Collections.Generic.List<Event>pour la file d'attente, avec vos différents événements étant des sous-classes d'Evénement, et une liste d'abonnés pour chaque type d'événement. Pour chaque événement de la file d'attente, obtenez la liste des abonnés et pour chaque abonné, appelez- handle(this_event)le. Retirez-le ensuite de la file d'attente. Répéter.

Il semble également y avoir beaucoup de choses sur les services de jeu, où vous pouvez enregistrer un GameComponent dans votre classe Game.cs principale, puis le récupérer de n'importe où dans votre code qui a une référence à l'objet principal `` Game ''. J'ai essayé cela avec mon objet SpriteBatch et cela semble très facile .. cependant, je ne vois pas que cela soit aussi flexible qu'un modèle d'événement.

Ce n'est probablement pas aussi flexible, mais c'est plus facile à déboguer. Les événements et les messages sont délicats car ils dissocient la fonction appelante de la fonction appelée, ce qui signifie que vos piles d'appels ne sont pas très utiles lors du débogage, les actions intermédiaires peuvent provoquer la rupture de quelque chose qui fonctionne normalement, les choses peuvent échouer silencieusement lorsqu'elles ne sont pas correctement connectées , etc. La méthode explicite de «saisir un composant et appeler ce dont vous avez besoin» fonctionne bien quand il s'agit de détecter les erreurs tôt et d'avoir un code clair et explicite. Cela a tendance à conduire à un code étroitement couplé et à de nombreuses dépendances complexes.

Je me rends compte que c'est en partie ma mauvaise connaissance de C # autant que les paradigmes de communication de jeu, mais j'aimerais vraiment bien comprendre cette chose fondamentale.

C # est à peu près un langage identique à Java. Tout ce que vous avez fait en Java peut être fait en C #, juste avec une syntaxe différente. Si vous avez du mal à traduire un concept, c'est probablement parce que vous ne connaissez que son nom spécifique à Java.

Kylotan
la source
Merci @Kylotan. Par curiosité, quel système utilisez-vous vous-même pour la communication interclasse - l'approche Services ou autre chose?
codinghands
AFAIK, les bus d'événements sont similaires aux trucs de file d'attente de messagerie, mais pour les événements.
ashes999
2
@codinghands, je n'utilise normalement aucune méthode de communication formelle interclasse. Je garde les choses simples - si une classe doit en appeler une autre, elle a généralement déjà une référence à la deuxième classe en tant que membre, ou la deuxième classe est passée en argument. Parfois, cependant, lorsque je travaille avec le moteur Unity, qui a une approche similaire à ce que vous appelez l'approche des «services», en ce sens que je rechercherais un objet par son nom qui a le composant dont j'ai besoin, recherchez le composant sur l'objet, puis appelez des méthodes sur ce composant.
Kylotan
@ ashes999 - Les messages et les événements sont à peu près la même chose dans ce contexte. Si vous mettez un événement dans une file d'attente, ou dans un bus, ou quoi que ce soit, ce que vous stockez vraiment est un message qui indique qu'un événement a eu lieu. De même, placer un message dans une file d'attente implique qu'un événement s'est produit pour générer ce message. Différentes façons de voir un appel de fonction asynchrone.
Kylotan
Désolée, je n'ai pas encore marqué cela comme «répondu», mais je pensais qu'il y aurait plus de points de vue étant donné que chaque jeu doit communiquer entre les composants d'une manière ou d'une autre, quelle que soit la langue ... Avons-nous couvert les principales possibilités?
codinghands
1

Avez-vous essayé d'utiliser simplement des événements statiques simples? Par exemple, si vous vouliez que votre classe de score sache quand un ennemi est mort, vous feriez quelque chose comme ceci:

Score.cs

class Score
{
    public int PlayerScore { get; set; }

    public Score()
    {
        EnemySpaceship.Death += new EventHandler<SpaceshipDeathEventArgs>(Spaceship_Death);
    }

    private void Spaceship_Death(object sender, SpaceshipDeathEventArgs e)
    {
        PlayerScore += e.Points;
    }
}

EnemySpaceship.cs

class EnemySpaceship
{
    public static event EventHandler<SpaceshipDeathEventArgs> Death;

    public void Kill()
    {
        OnDeath();
    }

    protected virtual void OnDeath()
    {
        if (Death != null)
        {
            Death(this, new SpaceshipDeathEventArgs(50));
        }
    }
}

SpaceshipDeathEventArgs.cs

class SpaceshipDeathEventArgs : EventArgs
{
    public int Points { get; private set; }

    internal SpaceshipDeathEventArgs(int points)
    {
        Points = points;
    }
}
John Pollick
la source
0

essayez d'utiliser l'agrégateur d'événements comme celui-ci

mindabyss
la source
4
Cette réponse serait meilleure si vous développiez davantage votre suggestion, au lieu de simplement fournir le lien.
MichaelHouse