Que técnica devo usar para facilitar a comunicação entre os XNA GameComponents (ou entre componentes de qualquer tipo em um jogo)?

9

Estou começando no meu primeiro projeto de jogo 'adequado' e, inevitavelmente, atingi um quarteirão tentando decidir como os componentes do jogo no XNA devem se comunicar.

Em eventos de programação GUI (Java) anteriores, manipuladores e ouvintes pareciam o caminho a seguir. Então, eu teria algum tipo de barramento de evento que aceita registros e classes de eventos inscritos nesses eventos, com manipuladores para lidar com eles. Por exemplo (pseudocódigo):

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

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

No entanto, não tenho certeza da configuração do código (em C #) necessária para fazer isso completamente. O que mantém o controle de eventos (algum tipo de ônibus?) E como está estruturado?

Também parece haver muita coisa mencionada no Game Services, na qual você pode registrar um GameComponent na classe Game.cs principal e buscá-lo em qualquer lugar do código que tenha uma referência ao objeto 'Game' principal. Eu tentei isso com meu objeto SpriteBatch e parece muito fácil .. no entanto, não vejo isso tão flexível quanto um modelo de evento.

Tomemos, por exemplo, quando um inimigo morre. Queremos atualizar a pontuação do jogo. Usando serviços, posso obter uma referência ao meu objeto StateManager criado no Game1 e adicionado como um serviço, depois definir 'score' para o novo valor. Eu pensaria que um evento 'onEnemyDeath', que pode ser tratado de maneira diferente por várias classes, mas iniciado por 1 linha de código na seção 'detecção de morte inimiga' relevante, seria melhor do que lançar individualmente cada GameComponent necessário e depois chamar o que quer métodos são necessários.

Ou essas estratégias são inferiores a outra coisa?

Sei que esse é parcialmente o meu pobre conhecimento em C #, tanto quanto os paradigmas de comunicação de jogos, mas eu realmente gostaria de acertar essa coisa fundamental.

Atualizar

Observar os Serviços é mais detalhes, estou menos convencido - basicamente está passando uma variável global (pelo que entendi).

Atualização 2

Tendo examinado este tutorial básico sobre manipulação de eventos e teste do código de amostra, parece que os eventos seriam uma escolha lógica para o que estou discutindo. Mas não posso usá-lo muito nas amostras que já vi. Existe alguma razão óbvia para que alguém não deva?

codinghands
fonte
Eu recomendo que você tente o FlatRedBall primeiro. Ele fica em cima do XNA e torna a vida muito fácil.
ashes999
3
Eu realmente gostaria de entender o básico do que está acontecendo antes de pular para um motor.
Codinghands 16/10/11
Eu acho que sua falha aqui é realmente uma falta de informações sobre c #. O C # usa eventos para facilitar a comunicação entre as classes normalmente. É realmente você quem decide como fazê-lo - o XNA fornece uma classe SpriteBatch, e a maior parte do trabalho depende de você escrever. Um mecanismo lhe dará uma idéia sólida de como as coisas funcionam em um nível mais alto antes de mergulhar nos detalhes.
ashes999
11
Eu concordo até certo ponto, mas pelo que li, a maioria das comunicações interclasses entre os GameComponents pode ser feita usando os Serviços. Estou acostumado a usar eventos. Eu não tenho certeza qual é o melhor, ou se existem alternativas válidas (singletons, classes estáticas disfarçados de globals como 2 outros métodos que eu omitidos)
codinghands

Respostas:

4

Em eventos de programação GUI (Java) anteriores, manipuladores e ouvintes pareciam o caminho a seguir. Então, eu teria algum tipo de barramento de evento que aceita registros e classes de eventos inscritos nesses eventos, com manipuladores para lidar com eles.

O motivo de você não encontrar muito sobre barramentos de eventos em C # é porque, na minha experiência, acho que ninguém fora do Java chama isso de 'barramento'. Termos mais comuns podem ser fila de mensagens, gerenciador de eventos, sinais / slots, publicação / assinatura, etc.

Você não precisa de nenhum deles para criar um jogo funcional, mas se esse é o tipo de sistema com o qual você se sente confortável, ele também será útil aqui. Infelizmente, ninguém pode lhe dizer exatamente como fazer um, porque todo mundo os interpreta de maneira um pouco diferente, se é que os usa mesmo. (Não, por exemplo.) Você pode começar com um simples System.Collections.Generic.List<Event>para a fila, com seus vários eventos sendo subclasses de Event e uma lista de assinantes para cada tipo de evento. Para cada evento na fila, obtenha a lista de assinantes e, para cada assinante, ligue handle(this_event)para ele. Em seguida, remova-o da fila. Repetir.

Também parece haver muita coisa mencionada no Game Services, na qual você pode registrar um GameComponent na classe Game.cs principal e buscá-lo em qualquer lugar do código que tenha uma referência ao objeto 'Game' principal. Eu tentei isso com meu objeto SpriteBatch e parece muito fácil .. no entanto, não vejo isso tão flexível quanto um modelo de evento.

Provavelmente não é tão flexível, mas é mais fácil depurar. Eventos e mensagens são complicados porque separam a função de chamada da função chamada, o que significa que suas pilhas de chamadas não são muito úteis durante a depuração; ações intermediárias podem causar a quebra de algo que normalmente funciona; as coisas podem falhar silenciosamente quando não são conectadas corretamente. , e assim por diante. O método explícito de "pegar um componente e chamar o que você precisa" funciona bem quando se trata de detectar erros desde o início e ter um código claro e explícito. Isso tende a levar a códigos fortemente acoplados e muitas dependências complexas.

Sei que esse é parcialmente o meu pobre conhecimento em C #, tanto quanto os paradigmas de comunicação de jogos, mas eu realmente gostaria de acertar essa coisa fundamental.

C # é praticamente uma linguagem idêntica ao Java. Tudo o que você fez em Java pode ser feito em C #, apenas com sintaxe diferente. Se você tiver problemas para traduzir um conceito, provavelmente é apenas porque você conhece apenas um nome específico para Java.

Kylotan
fonte
Obrigado @Kylotan. Por curiosidade, que sistema você utiliza para comunicação entre classes - a abordagem de Serviços ou outra coisa?
Codigohands 16/10
AFAIK, os barramentos de eventos são semelhantes aos itens da fila de mensagens, mas para eventos.
ashes999
2
@codinghands, normalmente não uso nenhum método formal de comunicação entre classes. Eu apenas mantenho as coisas simples - se uma classe precisa chamar outra, geralmente ela já tem uma referência à segunda classe como membro ou teve a segunda classe passada como argumento. Às vezes, no entanto, quando estou trabalhando com o mecanismo do Unity, isso tem uma abordagem semelhante à que você chama de abordagem de 'serviços', na medida em que procurava um objeto pelo nome que possui o componente necessário, procure o componente no objeto e chame métodos nesse componente.
Kylotan
@ ashes999 - Mensagens e eventos são praticamente a mesma coisa neste contexto. Se você colocar um evento em uma fila, ônibus ou qualquer outra coisa, o que você realmente está armazenando é uma mensagem que indica que um evento ocorreu. Da mesma forma, colocar uma mensagem em uma fila implica que ocorreu um evento para gerar essa mensagem. Apenas maneiras diferentes de observar uma chamada de função assíncrona.
Kylotan
Desculpe, ainda não marquei isso como 'respondido', mas pensei que haveria mais pontos de vista, já que todo jogo precisa se comunicar entre os componentes de alguma forma, independentemente do idioma ... Cobrimos as principais possibilidades?
Codigohands 18/10/11
1

Você já tentou usar eventos estáticos simples? Por exemplo, se você quiser que sua classe de pontuação saiba quando um inimigo morreu, faça algo assim:

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

tente usar o agregador de eventos como este

mindabyss
fonte
4
Essa resposta seria melhor se você elaborasse mais sobre sua sugestão, em vez de apenas fornecer o link.
MichaelHouse