Quale tecnica dovrei usare per facilitare la comunicazione tra XNA GameComponents (o tra componenti di qualsiasi tipo in un gioco)?


9

Sto iniziando il mio primo progetto di gioco "corretto" e ho inevitabilmente colpito un blocco cercando di decidere come comunicare i componenti di gioco in XNA.

Dai precedenti eventi di programmazione della GUI (Java), i gestori e gli ascoltatori sembravano la strada da percorrere. Quindi avrei una specie di bus per eventi che accetta le iscrizioni agli eventi e le classi iscritte a quegli eventi, con gestori che li gestiscono. Ad esempio (pseudocodice):

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

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

Tuttavia, non sono sicuro dell'impostazione del codice (in C #) che sarebbe necessaria per ottenere questo risultato. Cosa tiene traccia degli eventi (una specie di autobus?) E come è strutturato?

Sui Servizi di gioco sembra anche essere molto menzionato, per cui puoi registrare un GameComponent nella tua classe Game.cs principale, quindi recuperarlo da qualsiasi parte del codice che abbia un riferimento all'oggetto principale "Game". L'ho provato con il mio oggetto SpriteBatch e sembra molto semplice .. tuttavia, non riesco a vedere che sia flessibile come un modello di eventi.

Prendi ad esempio quando un nemico muore. Vogliamo aggiornare il punteggio del gioco. Usando i servizi posso ottenere un riferimento al mio oggetto StateManager creato in Game1 e aggiunto come servizio, quindi impostare 'punteggio' sul nuovo valore. Pensavo che un evento "onEnemyDeath", che può essere gestito in modo diverso da una moltitudine di classi, ma avviato da 1 riga di codice nella relativa sezione "Rilevazione della morte nemica", sarebbe meglio che lanciare individualmente ogni GameComponent richiesto quindi chiamare qualunque sono richiesti metodi.

O queste strategie inferiori sono qualcos'altro?

Mi rendo conto che questa è in parte la mia scarsa conoscenza di C # tanto quanto i paradigmi di comunicazione del gioco, ma mi piacerebbe davvero che questa cosa fondamentale fosse corretta.

Aggiornare

Dopo aver esaminato i Servizi è più dettagliato, ne sono meno convinto: sostanzialmente passa una variabile globale (da quello che ho capito).

Aggiornamento 2

Dopo aver dato un'occhiata a questo tutorial di base sulla gestione degli eventi e testare il codice di esempio, sembra che gli eventi sarebbero una scelta logica per ciò di cui sto discutendo. Ma non posso usarlo molto nei campioni che ho visto. C'è qualche ovvio motivo per cui non si dovrebbe?


Ti consiglio di provare prima FlatRedBall. Si trova in cima a XNA e rende la vita davvero facile.
ashes999,

3
Mi piacerebbe davvero capire le basi di quello che sta succedendo prima di saltare su un motore.
codinghands,

Penso che il tuo fallimento qui sia davvero una mancanza di informazioni su C #. C # usa di solito eventi per facilitare la comunicazione tra le classi. Sta a te decidere come farlo: XNA fornisce una classe SpriteBatch e la maggior parte del lavoro spetta a te scrivere. Un motore ti darà una solida idea di come funzionano le cose a un livello superiore prima di immergerti nei dettagli.
ashes999,

1
Accetto fino a un certo punto, ma da quando ho letto la maggior parte delle comunicazioni interclasse tra GameComponents può essere effettuata utilizzando i Servizi. Sono abituato a usare gli eventi. Non sono sicuro di quale sia la migliore, o se ci sono alternative valide (singleton, classi statiche mascherate da globi come altri 2 metodi che ho omesso)
codinghands

Risposte:


4

Dai precedenti eventi di programmazione della GUI (Java), i gestori e gli ascoltatori sembravano la strada da percorrere. Quindi avrei una specie di bus per eventi che accetta le iscrizioni agli eventi e le classi iscritte a quegli eventi, con gestori che li gestiscono.

Il motivo per cui non troverai molto sugli autobus per eventi in C # è perché non credo che nessuno al di fuori di Java definisca una cosa del genere "autobus", nella mia esperienza. Termini più comuni potrebbero essere coda dei messaggi, gestore eventi, segnali / slot, pubblicazione / iscrizione, ecc.

Non hai bisogno di nessuno di questi per creare un gioco funzionante, ma se questo è il tipo di sistema con cui ti senti a tuo agio, ti sarà utile anche qui. Sfortunatamente nessuno può dirti esattamente come realizzarne uno perché tutti li fanno rotolare in modo leggermente diverso, se addirittura li usano. (Non lo so, per esempio.) Potresti iniziare con un semplice System.Collections.Generic.List<Event>per la coda, con i tuoi vari eventi che sono sottoclassi di Evento e un elenco di abbonati per ciascun tipo di evento. Per ogni evento in coda, ottenere l'elenco degli abbonati e, per ogni abbonato, chiamarlo handle(this_event). Quindi rimuoverlo dalla coda. Ripetere.

Sui Servizi di gioco sembra anche essere molto menzionato, per cui puoi registrare un GameComponent nella tua classe Game.cs principale, quindi recuperarlo da qualsiasi parte del codice che abbia un riferimento all'oggetto principale "Game". L'ho provato con il mio oggetto SpriteBatch e sembra molto semplice .. tuttavia, non riesco a vedere che sia flessibile come un modello di eventi.

Probabilmente non è così flessibile, ma è più facile eseguire il debug. Eventi e messaggi sono complicati perché disaccoppiano la funzione di chiamata dalla funzione chiamata, il che significa che le tue pile di chiamate non sono estremamente utili durante il debug, azioni intermedie possono causare un guasto che normalmente funziona, le cose possono fallire silenziosamente se non agganciate correttamente , e così via. Il metodo esplicito di "afferrare un componente e chiamare ciò di cui hai bisogno" funziona bene quando si tratta di rilevare errori in anticipo e di avere un codice chiaro ed esplicito. Tende solo a codice strettamente accoppiato e molte dipendenze complesse.

Mi rendo conto che questa è in parte la mia scarsa conoscenza di C # tanto quanto i paradigmi di comunicazione del gioco, ma mi piacerebbe davvero che questa cosa fondamentale fosse corretta.

C # è praticamente un linguaggio identico a Java. Tutto ciò che hai fatto in Java può essere fatto in C #, solo con una sintassi diversa. Se hai problemi a tradurre un concetto, probabilmente è solo perché conosci solo un nome specifico di Java per esso.


Grazie @Kylotan. Per curiosità, quale sistema utilizzi tu stesso per la comunicazione interclasse: l'approccio dei servizi o qualcos'altro?
codinghands,

AFAIK, i bus degli eventi sono simili alle cose della coda di messaggistica, ma per gli eventi.
ashes999,

2
@codinghands, di solito non uso alcun metodo di comunicazione formalizzato interclasse. Tengo semplicemente le cose semplici: se una classe ha bisogno di chiamarne un'altra, di solito o ha già un riferimento alla seconda classe come membro, o aveva la seconda classe passata come argomento. A volte, tuttavia, quando lavoro con il motore Unity, che ha un approccio simile a quello che stai chiamando l'approccio "servizi", in quanto cerco un oggetto per nome che ha il componente richiesto, cerco il componente sull'oggetto, quindi chiama i metodi su quel componente.
Kylotan,

@ ashes999 - Messaggi ed eventi sono praticamente la stessa cosa in questo contesto. Se si inserisce un evento in una coda, in un bus o in qualsiasi altra cosa, ciò che si sta realmente archiviando è un messaggio che indica che si è verificato un evento. Allo stesso modo, l'inserimento di un messaggio in una coda implica che si è verificato un evento per generare quel messaggio. Solo modi diversi di guardare una chiamata di funzione asincrona.
Kylotan,

Mi dispiace non aver ancora contrassegnato questo come 'risposta', ma ho pensato che ci sarebbero stati più punti di vista dato che ogni gioco deve comunicare in qualche modo tra i componenti, indipendentemente dalla lingua ... Abbiamo coperto le possibilità principali?
codinghands,

1

Hai provato ad usare semplici eventi statici? Ad esempio, se vuoi che la tua classe di punteggio sappia quando è morto un nemico, faresti qualcosa del genere:

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;
    }
}


0

prova ad usare un aggregatore di eventi come questo


4
Questa risposta sarebbe migliore se avessi approfondito il tuo suggerimento, invece di fornire semplicemente il link.
MichaelHouse
Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.