Por exemplo, ter uma classe base GameObject com uma hierarquia profunda de herança pode ser bom para manutenção ...
Na verdade, hierarquias profundas geralmente são piores em termos de manutenção do que as rasas, e o estilo arquitetônico moderno para objetos de jogo está tendendo a abordagens rasas e baseadas em agregação .
No entanto, acho que essa abordagem pode criar problemas de desempenho. Por outro lado, os dados e funções de todos os objetos do jogo podem ser globais. O que seria uma dor de cabeça para manutenção, mas pode estar mais próximo de um loop de jogo com desempenho ideal.
O loop que você mostrou potencialmente tem problemas de desempenho, mas não, como está implícito na sua declaração subsequente, porque você tem dados de instância e funções de membro na GameObject
classe. Em vez disso, o problema é que, se você tratar todos os objetos do jogo como exatamente o mesmo, provavelmente não estará agrupando esses objetos de maneira inteligente - eles provavelmente estarão espalhados aleatoriamente por toda a lista. Potencialmente, então, todas as chamadas para o método de atualização para esse objeto (se esse método é uma função global ou não, e se esse objeto possui dados de instância ou "dados globais" flutuando em alguma tabela na qual você está indexando ou o que seja) diferente da chamada de atualização nas últimas iterações do loop.
Isso pode aumentar a pressão no sistema, pois pode ser necessário paginar a memória com a função relevante dentro e fora da memória e preencher novamente o cache de instruções com mais freqüência, resultando em um loop mais lento. Se isso é ou não observado a olho nu (ou mesmo em um criador de perfil) depende exatamente do que é considerado um "objeto de jogo", quantos deles existem em média e o que mais está acontecendo no seu aplicativo.
Os sistemas de objetos orientados a componentes são uma tendência popular no momento, aproveitando a filosofia de que a agregação é preferível à herança . Esses sistemas permitem que você divida a lógica de "atualização" dos componentes (onde "componente" é definido aproximadamente como alguma unidade de funcionalidade, como a coisa que representa a parte fisicamente simulada de algum objeto, que é processada pelo sistema físico ) para vários encadeamentos - discriminados por tipo de componente - se possível e desejado, o que poderia ter um ganho de desempenho. No mínimo, você pode organizar os componentes de forma que todos os componentes de um determinado tipo sejam atualizados juntos , aproveitando ao máximo o cache da CPU. Um exemplo de um sistema orientado a componentes é discutido neste tópico .
Esses sistemas geralmente são altamente dissociados, o que também é um benefício para a manutenção.
O design orientado a dados é uma abordagem relacionada - trata-se de orientar-se em torno dos dados exigidos dos objetos como primeira prioridade, para que esses dados possam ser efetivamente processados em massa (por exemplo). Isso geralmente significa uma organização que tenta manter os dados usados para o mesmo objetivo em conjunto e operam de uma só vez. Não é fundamentalmente incompatível com o design do OO, e você pode encontrar alguma conversa sobre o assunto aqui no GDSE nesta pergunta .
Com efeito, uma abordagem mais ideal para o ciclo do jogo seria, em vez do seu original
foreach(GameObject g in gameObjects) g.update();
algo mais parecido
ProcessUserInput();
UpdatePhysicsForAllObjects();
UpdateScriptsForAllObjects();
UpdateRenderDataForAllObjects();
RenderEverything();
Em tal mundo, cada um GameObject
pode ter um ponteiro ou referência à sua própria PhysicsData
ou Script
ou RenderData
, para os casos em que você pode precisar interagir com os objetos em uma base individual, mas o real PhysicsData
, Scripts
, RenderData
, et cetera seriam todos de propriedade de seus respectivos subsistemas (simulador de física, ambiente de hospedagem de scripts, renderizador) e processado em massa, conforme indicado acima.
É muito importante observar que essa abordagem não é uma bala mágica e nem sempre produz melhorias no desempenho (embora seja geralmente um design melhor do que uma árvore de herança profunda). Você provavelmente notará basicamente nenhuma diferença de desempenho se tiver muito poucos objetos ou muitos objetos para os quais não é possível paralelizar efetivamente as atualizações.
Infelizmente, não existe um loop mágico ideal - todos os jogos são diferentes e podem precisar de ajustes de desempenho de maneiras diferentes. Portanto, é muito importante medir as coisas (perfil) antes de você seguir cegamente o conselho de um cara aleatório na internet.