Nos comentários, publiquei uma versão condensada da minha resposta antiga resposta:
Usar DrawableGameComponent
bloqueia você em um único conjunto de assinaturas de método e em um modelo de dados retido específico. Normalmente, essas são as escolhas erradas inicialmente, e o bloqueio dificulta significativamente a evolução do código no futuro.
Aqui tentarei ilustrar por que esse é o caso, publicando algum código do meu projeto atual.
O objeto raiz que mantém o estado do mundo do jogo tem um Update
método parecido com este:
void Update(UpdateContext updateContext, MultiInputState currentInput)
Existem vários pontos de entrada para Update
, dependendo se o jogo está rodando na rede ou não. É possível, por exemplo, que o estado do jogo seja revertido para um ponto anterior no tempo e depois simulado novamente.
Existem também alguns métodos adicionais de atualização, como:
void PlayerJoin(int playerIndex, string playerName, UpdateContext updateContext)
Dentro do estado do jogo, há uma lista de atores a serem atualizados. Mas isso é mais do que uma simples Update
ligação. Existem passes adicionais antes e depois para lidar com a física. Há também algumas coisas sofisticadas para desovar / destruir adiado de atores, bem como transições de nível.
Fora do estado do jogo, há um sistema de interface do usuário que precisa ser gerenciado de forma independente. Mas algumas telas na interface do usuário também precisam ser executadas em um contexto de rede.
Para o desenho, devido à natureza do jogo, existe um sistema personalizado de classificação e seleção que recebe entradas desses métodos:
virtual void RegisterToDraw(SortedDrawList sortedDrawList, Definitions definitions)
virtual void RegisterShadow(ShadowCasterList shadowCasterList, Definitions definitions)
E então, depois de descobrir o que desenhar, chama automaticamente:
virtual void Draw(DrawContext drawContext, int tag)
O tag
parâmetro é necessário porque alguns atores podem se apegar a outros atores - e, portanto, precisam ser capazes de desenhar acima e abaixo do ator retido. O sistema de classificação garante que isso aconteça na ordem correta.
Há também o sistema de menus para desenhar. E um sistema hierárquico para desenhar a interface do usuário, ao redor do HUD, ao redor do jogo.
Agora compare esses requisitos com o que DrawableGameComponent
fornece:
public class DrawableGameComponent
{
public virtual void Update(GameTime gameTime);
public virtual void Draw(GameTime gameTime);
public int UpdateOrder { get; set; }
public int DrawOrder { get; set; }
}
Não há uma maneira razoável de passar de um sistema DrawableGameComponent
para o sistema descrito acima. (E esses não são requisitos incomuns para um jogo de complexidade ainda modesta).
Além disso, é muito importante observar que esses requisitos não surgiram simplesmente no início do projeto. Eles evoluíram ao longo de muitos meses de trabalho de desenvolvimento.
O que as pessoas normalmente fazem, se iniciam o DrawableGameComponent
caminho, é começar a desenvolver hacks:
Em vez de adicionar métodos, eles fazem uma conversão feia para o seu próprio tipo base (por que não usar isso diretamente?).
Em vez de substituir o código para classificar e chamar Draw
/ Update
, eles fazem algumas coisas muito lentas para alterar os números de pedidos rapidamente.
E o pior de tudo: em vez de adicionar parâmetros aos métodos, eles começam a "passar" "parâmetros" em locais de memória que não são a pilha (o uso Services
para isso é popular). Isso é complicado, propenso a erros, lento, frágil, raramente seguro para threads e provavelmente se tornará um problema se você adicionar redes, criar ferramentas externas e assim por diante.
Ele também faz reutilização de código mais difícil , porque agora as suas dependências estão escondidos dentro do "componente", em vez de publicado no limite API.
Ou, eles quase veem o quão louco isso tudo é e começam a fazer "gestores" DrawableGameComponent
para solucionar esses problemas. Exceto que eles ainda têm esses problemas nos limites do "gerente".
Invariavelmente, esses "gerentes" poderiam ser facilmente substituídos por uma série de chamadas de método na ordem desejada. Qualquer outra coisa é muito complicada.
Obviamente, quando você começa a empregar esses hacks, é preciso um trabalho real para desfazê- los quando você perceber que precisa de mais do que aquilo que DrawableGameComponent
oferece. Você se torna " trancado ". E a tentação é muitas vezes continuar acumulando hacks - o que na prática o atrapalha e limita os tipos de jogos que você pode fazer aos relativamente simplistas.
Muitas pessoas parecem chegar a esse ponto e têm uma reação visceral - possivelmente porque usam DrawableGameComponent
e não querem aceitar "estar errado". Então eles se prendem ao fato de que é pelo menos possível fazê-lo "funcionar".
Claro que pode ser feito para "trabalhar"! Somos programadores - fazer as coisas funcionarem sob restrições incomuns é praticamente o nosso trabalho. Mas essa restrição não é necessária, útil ou racional ...
O que é particularmente espantosa sobre é que é surpreendentemente trivial para side-passo toda esta questão. Aqui, eu vou te dar o código:
public class Actor
{
public virtual void Update(GameTime gameTime);
public virtual void Draw(GameTime gameTime);
}
List<Actor> actors = new List<Actor>();
foreach(var actor in actors)
actor.Update(gameTime);
foreach(var actor in actors)
actor.Draw(gameTime);
(É simples adicionar a classificação de acordo com DrawOrder
e UpdateOrder
. Uma maneira simples é manter duas listas e usá-la List<T>.Sort()
. Também é trivial manipular Load
/ UnloadContent
.)
Não é importante o que esse código realmente é . O importante é que eu possa modificá-lo trivialmente .
Quando percebo que preciso de um sistema de temporização diferente gameTime
, ou preciso de mais parâmetros (ou um UpdateContext
objeto inteiro com cerca de 15 coisas), ou preciso de uma ordem de operações completamente diferente para desenhar, ou preciso de alguns métodos extras para diferentes passes de atualização, posso apenas fazer isso .
E eu posso fazer isso imediatamente. Não preciso me preocupar em escolher DrawableGameComponent
meu código. Ou pior: corte-o de alguma forma que tornará ainda mais difícil a próxima vez que essa necessidade surgir.
Na verdade, existe apenas um cenário em que é uma boa escolha DrawableGameComponent
: se você estiver literalmente criando e publicando middleware no estilo arrastar e soltar componentes para o XNA. Qual era o seu propósito original. O que - acrescentarei - ninguém está fazendo!
(Da mesma forma, só faz sentido usarServices
ao criar tipos de conteúdo personalizados para ContentManager
.)