Perguntas sobre arquitetura de jogos com o XNA

12

Por fim, comecei a brincar com o XNA e a brincar com um jogo em 2D (tenho vários recursos de arte de um amigo que o desenvolveu no iOS)

Muitas coisas parecem fáceis de fazer e saem da caixa, no entanto, fiquei perplexo por muitas coisas, já que a maioria da literatura (os livros que comprei por exemplo) não presta muita atenção ao 2D.

Eu realmente aprecio qualquer esclarecimento ou estou sendo direcionado para mais informações sobre as seguintes questões:

  1. Qual é o objetivo de um serviço de jogos? Entendo todo o idioma de registrar um objeto como um serviço para que qualquer outro GameComponent possa obtê-lo, no entanto, como isso acontece simplesmente tornando-o público ou estático? Por exemplo, meu livro recomendou o registro do objeto SpriteBatch na classe Game.cs. Não sei como é preferível simplesmente torná-lo público / estático, pois deve haver apenas uma instância do jogo (singleton)?

  2. Estou confuso sobre quando devo herdar de GameComponent ou RenderableGameComponent. Estou tentando seguir um design de gerente / controlador, de modo que todas as entidades sejam criadas / pertencentes a um único gerente e o mesmo para outras coisas. Atualmente, tenho cada gerente / controlador herdado do GameComponent, no entanto, como isso supera o fato de o objeto Game possuir todos os gerentes e chamar manualmente a atualização deles e desenhar?

  3. Percebi que Initialize é chamado antes de ContentLoad (), achei isso irritante, pois no meu Initialize é onde eu gostaria de criar algumas das minhas entidades (por exemplo, Sprite, Player etc.), no entanto, não posso fornecer o carregamento delas. SpriteSheets ou Textures desde que a chamada para carregá-los ainda não ocorreu. Talvez eu esteja inicializando incorretamente ou as pessoas simplesmente atribuem a textura mais abaixo no ContentLoad?

Esses parecem ser o meu maior " WTF " de não entender realmente

Setheron
fonte

Respostas:

11

A resposta para sua pergunta é simplesmente: faça o que funcionar. Idealmente, faça o que é simples e funciona. Muitas vezes, o uso da arquitetura embutida não é simples porque, como você está descobrindo, você precisa pular os aros para estruturar seu jogo da maneira que desejar.

Para responder à primeira pergunta, encaminhá-lo-ei à minha resposta à pergunta "Por que usar serviços?" . A resposta curta é que os serviços são uma boa maneira de evitar problemas de acoplamento (controle de versão, dependência, extensibilidade) que você nunca encontrará em um jogo independente . Na maioria das vezes, é mais simples criar um membro public(e talvez até mesmo staticse você estiver com pressa) e se preocupar com problemas mais interessantes.

Para responder sua segunda pergunta, encaminhá-lo-ei à minha resposta à pergunta "Quais são os contras do uso de DrawableGameComponent para todas as instâncias de um objeto de jogo?" bem como meus pensamentos sobre arquitetura de jogos . A resposta curta é: não há nenhum benefício de usar os GameComponentmais simples criação de suas próprias classes e chamando métodos como Drawe Updatesi mesmo. Se GameComponentcorresponder exatamente à arquitetura desejada, use-a de todos os modos - mas quase sempre não.

O uso de serviços e componentes só se torna realmente interessante se você estiver criando uma biblioteca para consumo de terceiros. O próprio XNA faz isso - ele tem IGraphicsDeviceServicee GamerServicesComponent, por exemplo. Eles atendem a requisitos específicos de dissociação que você normalmente não encontra ao desenvolver um jogo.

Finalmente, a resposta para sua terceira pergunta é bastante simples: basta criar suas entidades de jogo LoadContent. Não há nada de especial Initialize.

Vale ressaltar que toda a Microsoft.Xna.Framework.Gamemontagem é opcional . Ele fornece um ponto de partida extremamente útil - mas de maneira alguma deve ser considerado como "o caminho certo" para fazer as coisas.

Andrew Russell
fonte
Maneira muito interessante de ver. Muito mais realista e realista do que a minha resposta. Bem merecido +1, senhor.
precisa saber é o seguinte
Eu tenho mais uma pergunta! E a origem quando você desenha para um Texture2D. É comum colocar a origem no meio da parte inferior da textura? Eu estou tentando fazer animações, e eu estou achando que tem a origem em (0,0) está causando a textura para mudar ligeiramente se a largura e altura não são os mesmos
Setheron
@ Etheron: Você realmente deve criar uma nova pergunta - pois isso não tem relação com a pergunta original. Vou reverter a edição que você fez nesta pergunta. A origem de SpriteBatch.Drawé especificada nas coordenadas de pixel de textura em relação à parte superior esquerda da textura (ou retângulo de origem, se você usar uma).
Andrew Russell
1
@ Andrew: Recentemente refatorei meu jogo para que ele fosse inteiramente baseado em serviços, em vez de em public/ staticpropriedades. Por quê? Testabilidade. É quase impossível mudar as coisas com a abordagem de propriedades, enquanto que é trivial se tudo for inicializado com um IServiceProviderque possa ser preenchido com qualquer coisa que a classe precise pelo seu método de teste. Também evita a necessidade de instanciar o Gamepróprio objeto em seu teste, que fica confuso muito rapidamente.
Matthew Scharley
Como um aparte, ainda tenho muitas propriedades públicas no Gameobjeto. Por acaso, eles são acessados ​​por meio de interfaces empurradas para as Game.Servicesquais são repassadas a tudo para obter o que precisam novamente.
Matthew Scharley
3

Para sua primeira pergunta, devo admitir que realmente não sei a resposta real. Eu acho que seria pelo mesmo motivo pelo qual singleton geralmente é um padrão de design ruim. Vou encaminhá-lo para uma citação neste link :

O primeiro que pensamos (e uma das razões por trás da introdução de serviços) foi disponibilizar uma referência GraphicsDevice em um jogo. Originalmente, tínhamos o jogo como proprietário do GraphicsDevice e expô-lo através de uma propriedade como Game.GraphicsDevice. O problema era que isso vinculava firmemente o GraphicsDevice ao jogo, não permitindo que outra pessoa fosse “proprietária” do dispositivo (como um renderizador) e impedindo que alguém usasse um GraphicsDevice atualizado futuro sem enviar uma nova versão do jogo que não seria ' Não seja compatível com versões anteriores.

Para sua segunda pergunta, eu assumiria que usar Componentes como este seria mais útil se você seguisse uma abordagem baseada em componentes , como se tivesse uma lista de Spritecomponentes que saberiam desenhar e atualizar a si mesmos em vez de um gerente que sabe atualizar e desenhar todos os sprites. Concordo, parece o mesmo, mas prefiro um design baseado em componentes porque realmente gosto da abordagem orientada a objetos que ele possui.

Para sua terceira pergunta, sim, você deve carregar qualquer forma de conteúdo (ativos, fontes, etc.) no método LoadContent. Quanto à inicialização, você normalmente cria o objeto Player na função Initialize e carrega seu conteúdo no LoadContent. Ou pode fazer tudo isso no LoadContent, se desejar.

Jesse Emond
fonte