¿Qué técnica debo usar para facilitar la comunicación entre XNA GameComponents (o entre componentes de cualquier tipo en un juego)?

9

Estoy empezando mi primer proyecto de juego 'adecuado', e inevitablemente he llegado a un bloque tratando de decidir cómo deben comunicarse los componentes del juego en XNA.

De los eventos de programación de GUI (Java) anteriores, los controladores y los oyentes parecían el camino a seguir. Por lo tanto, tendría algún tipo de bus de eventos que acepte registros de eventos y clases suscritas a esos eventos, con controladores para tratar con ellos. Por ejemplo (pseudocódigo):

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

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

Sin embargo, no estoy seguro de la configuración del código (en C #) que se requeriría para lograr esto completamente. ¿Qué hace un seguimiento de los eventos (algún tipo de autobús?) Y cómo está estructurado?

También parece que se menciona mucho en Game Services, por lo que puede registrar un GameComponent en su clase principal de Game.cs, luego buscarlo en cualquier parte de su código que tenga una referencia al objeto principal 'Juego'. He intentado esto con mi objeto SpriteBatch y parece muy fácil ... sin embargo, no puedo ver que sea tan flexible como un modelo de evento.

Tomemos, por ejemplo, cuando un enemigo muere. Queremos actualizar la puntuación del juego. Al usar los servicios, puedo obtener una referencia a mi objeto StateManager creado en el Juego 1 y agregado como un servicio, luego establezco 'puntaje' al nuevo valor. Pensé que un evento 'onEnemyDeath', que puede ser manejado de manera diferente por una multitud de clases, pero iniciado por 1 línea de código en la sección relevante 'detección de muerte enemiga', sería mejor que lanzar individualmente cada GameComponent requerido y luego llamar a lo que sea Se requieren métodos.

¿O son estas estrategias inferiores a algo más?

Me doy cuenta de que esto es en parte mi pobre conocimiento de C # tanto como los paradigmas de comunicación del juego, pero realmente me gustaría entender bien esto.

Actualizar

Habiendo visto los Servicios es más detalle, estoy menos convencido: básicamente está pasando una variable global (por lo que entiendo).

Actualización 2

Después de echar un vistazo a este tutorial básico sobre el manejo de eventos y probar el código de muestra, parece que los eventos serían una elección lógica para lo que estoy discutiendo. Pero no puedo usarlo mucho en las muestras que he visto. ¿Hay alguna razón obvia por la que uno no debería?

codinghands
fuente
Te recomiendo que pruebes FlatRedBall primero. Se sienta encima de XNA y hace la vida realmente fácil.
cenizas999
3
Me gustaría entender realmente los conceptos básicos de lo que está sucediendo antes de saltar a un motor.
codinghands
Creo que su falla aquí es realmente una falta de información sobre C #. C # utiliza eventos para facilitar la comunicación entre clases por lo general. Realmente depende de usted cómo hacerlo: XNA proporciona una clase SpriteBatch, y la mayor parte del trabajo depende de usted para escribir. Un motor le dará una idea sólida de cómo funcionan las cosas a un nivel superior antes de sumergirse en los detalles.
cenizas999
1
Estoy de acuerdo en cierta medida, pero he leído que la mayoría de la comunicación entre clases entre GameComponents se puede hacer usando los Servicios. Estoy acostumbrado a usar eventos. No estoy seguro de que es mejor, o si existen alternativas válidas (únicos, clases estáticas que se hacen pasar como globales como otros 2 métodos que he omitido)
codinghands

Respuestas:

4

De los eventos de programación de GUI (Java) anteriores, los controladores y los oyentes parecían el camino a seguir. Por lo tanto, tendría algún tipo de bus de eventos que acepte registros de eventos y clases suscritas a esos eventos, con controladores para tratar con ellos.

La razón por la que no encontrará mucho acerca de los buses de eventos en C # es porque no creo que nadie fuera de Java llame a tal cosa un 'bus', en mi experiencia. Los términos más comunes pueden ser cola de mensajes, administrador de eventos, señales / ranuras, publicación / suscripción, etc.

No necesitas ninguno de esos para hacer un juego que funcione, pero si ese es el tipo de sistema con el que te sientes cómodo, también te servirá bien aquí. Desafortunadamente, nadie puede decirle exactamente cómo hacer uno porque todos los usan de manera un poco diferente, incluso si los usan. (No lo hago, por ejemplo). Puede comenzar con un simple System.Collections.Generic.List<Event>para la cola, con sus diversos eventos como subclases de Evento y una lista de suscriptores para cada tipo de evento. Para cada evento en la cola, obtenga la lista de suscriptores, y para cada suscriptor, solicítela handle(this_event). Luego quítelo de la cola. Repetir.

También parece que se menciona mucho en Game Services, por lo que puede registrar un GameComponent en su clase principal de Game.cs, luego buscarlo en cualquier parte de su código que tenga una referencia al objeto principal 'Juego'. He intentado esto con mi objeto SpriteBatch y parece muy fácil ... sin embargo, no puedo ver que sea tan flexible como un modelo de evento.

Probablemente no sea tan flexible, pero es más fácil de depurar. Los eventos y mensajes son complicados porque desacoplan la función de llamada de la función llamada, lo que significa que las pilas de llamadas no son muy útiles al depurar, las acciones intermedias pueden hacer que algo se rompa, lo que normalmente funciona, las cosas pueden fallar en silencio cuando no se conectan correctamente , y así. El método explícito de "agarrar un componente y llamar a lo que necesita" funciona bien cuando se trata de detectar errores desde el principio y tener un código claro y explícito. Simplemente tiende a generar código estrechamente acoplado y muchas dependencias complejas.

Me doy cuenta de que esto es en parte mi pobre conocimiento de C # tanto como los paradigmas de comunicación del juego, pero realmente me gustaría entender bien esto.

C # es prácticamente un lenguaje idéntico a Java. Lo que haya hecho en Java se puede hacer en C #, solo que con una sintaxis diferente. Si tiene problemas para traducir un concepto, probablemente sea porque solo conoce un nombre específico de Java para él.

Kylotan
fuente
Gracias @Kylotan. Por curiosidad, ¿qué sistema utiliza usted para la comunicación entre clases: el enfoque de Servicios u otra cosa?
codinghands
AFAIK, los buses de eventos son similares a las cosas de la cola de mensajes, pero para eventos.
cenizas999
2
@codinghands, normalmente no uso ningún método de comunicación formal entre clases. Simplemente mantengo las cosas simples: si una clase necesita llamar a otra, generalmente ya tiene una referencia a la segunda clase como miembro o la segunda clase se pasó como argumento. Sin embargo, a veces, cuando estoy trabajando con el motor de Unity, tiene un enfoque similar a lo que usted llama el enfoque de 'servicios', en el que buscaría un objeto por nombre que tenga el componente que necesito, buscar el componente en el objeto, luego llame a métodos en ese componente.
Kylotan
@ ashes999 - Los mensajes y eventos son más o menos lo mismo en este contexto. Si coloca un evento en una cola, en un autobús o lo que sea, lo que realmente está almacenando es un mensaje que denota que se ha producido un evento. Del mismo modo, poner un mensaje en una cola implica que ocurrió un evento para generar ese mensaje. Solo diferentes formas de ver una llamada de función asincrónica.
Kylotan
Lamento no haber marcado esto como 'respondido' todavía, pero pensé que habría más puntos de vista dado que cada juego necesita comunicarse entre los componentes de alguna manera, independientemente del idioma ... ¿Hemos cubierto las principales posibilidades?
codinghands
1

¿Has probado simplemente usando eventos estáticos simples? Por ejemplo, si desea que su clase de puntaje sepa cuándo murió un enemigo, haría algo como esto:

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
fuente
0

intenta usar el agregador de eventos como este

mindabyss
fuente
44
Esta respuesta sería mejor si elaborara más sobre su sugerencia, en lugar de simplemente proporcionar el enlace.
MichaelHouse