Ao criar jogos, você geralmente cria o seguinte objeto de jogo do qual todas as entidades herdam:
public class GameObject{
abstract void Update(...);
abstract void Draw(...);
}
Assim, ao atualizar o loop, você itera sobre todos os objetos do jogo e dá a eles a chance de mudar de estado; depois, no próximo loop, você itera novamente sobre todos os objetos do jogo e dá a eles a chance de se desenhar.
Embora isso funcione razoavelmente bem em um jogo simples com um renderizador de encaminhamento simples, geralmente leva a alguns objetos de jogo gigantescos que precisam armazenar seus modelos, várias texturas e, pior ainda, um método de desenho de gordura que cria um forte acoplamento entre o objeto de jogo, a estratégia de renderização atual e quaisquer classes relacionadas à renderização.
Se eu alterasse a estratégia de renderização de adiada para adiada, precisaria atualizar muitos objetos do jogo. E os objetos do jogo que eu faço não são tão reutilizáveis quanto poderiam ser. É claro que a herança e / ou composição podem me ajudar a combater a duplicação de código e facilitar um pouco a alteração da implementação, mas ainda falta.
Uma maneira melhor, talvez, seria remover completamente o método Draw da classe GameObject e criar uma classe Renderer. O GameObject ainda precisaria conter alguns dados sobre o visual, como qual modelo representá-lo e quais texturas devem ser pintadas no modelo, mas como isso é feito, será deixado para o renderizador. No entanto, muitas vezes há muitos casos de borda na renderização. Embora isso remova o acoplamento rígido do GameObject ao Renderer, o Renderer ainda precisa saber tudo sobre todos os objetos do jogo que o tornariam gordo, todo o conhecimento e fortemente acoplado. Isso violaria algumas boas práticas. Talvez o Design Orientado a Dados possa fazer o truque. Objetos de jogo certamente seriam dados, mas como o renderizador seria movido por isso? Não tenho certeza.
Então, estou perdida e não consigo pensar em uma boa solução. Tentei usar os princípios do MVC e, no passado, tive algumas idéias sobre como usá-lo em jogos, mas recentemente não parece tão aplicável quanto eu pensava. Gostaria muito de saber como todos vocês lidam com esse problema.
De qualquer forma, recapitulando, estou interessado em saber como os seguintes objetivos de design podem ser alcançados.
- Nenhuma lógica de renderização no objeto do jogo
- Acoplamento fraco entre os objetos do jogo e o mecanismo de renderização
- Nenhum processador que conhece tudo
- De preferência alternando o tempo de execução entre os mecanismos de renderização
A configuração ideal do projeto seria uma 'lógica de jogo' separada e renderizaria um projeto de lógica que não precisa se referir um ao outro.
Todo esse pensamento começou quando ouvi John Carmack dizer no twitter que ele tem um sistema tão flexível que ele pode trocar os mecanismos de renderização em tempo de execução e pode até dizer ao sistema para usar os dois renderizadores (um renderizador de software e um renderizador acelerado por hardware) ao mesmo tempo, para que ele possa inspecionar as diferenças. Os sistemas que eu programei até agora não são nem tão flexíveis
fonte
O que eu fiz para o meu próprio mecanismo é agrupar tudo em módulos. Então, eu tenho minha
GameObject
classe e ela possui uma alça para:Então eu tenho uma
Player
aula e umaBullet
aula. Ambos derivamGameObject
e são adicionados aoScene
. MasPlayer
possui os seguintes módulos:E
Bullet
possui estes módulos:Essa maneira de organizar as coisas evita o "Diamante da Morte", onde você tem a
Vehicle
, aVehicleLand
e aVehicleWater
e agora deseja aVehicleAmphibious
. Em vez disso, você tem umVehicle
e ele pode ter umModuleWater
e umModuleLand
.Bônus adicional: você pode criar objetos usando um conjunto de propriedades. Tudo o que você precisa saber é o tipo de base (Player, Inimigo, Bullet, etc.) e, em seguida, crie identificadores para os módulos necessários para esse tipo.
Na minha cena, faço o seguinte:
Update
para todas asGameObject
alças.ModuleCollision
identificador.UpdatePost
para todas asGameObject
alças para informar sobre sua posição final após a física.m_ObjectsCreated
lista àm_Objects
lista.E eu poderia organizá-lo ainda mais: por módulos em vez de por objeto. Então eu renderizava uma lista de
ModuleSprite
, atualizava váriasModuleScriptingBase
e fazia colisões com uma lista deModuleCollision
.fonte
GameObject
(por exemplo, uma maneira de renderizar uma "cobra" de Sprites), será necessário criar um filhoModuleSprite
para essa funcionalidade específica (ModuleSpriteSnake
) ou adicionar um novo módulo completamente (ModuleSnake
) Felizmente, são apenas indicadores, mas vi código ondeGameObject
literalmente tudo o que um objeto poderia fazer.