Recentemente, perguntei sobre como separar as entidades de seu comportamento e a principal resposta vinculada a este artigo: http://cowboyprogramming.com/2007/01/05/evolve-your-heirachy/
O conceito final escrito aqui é o de: OBJETO COMO UMA AGREGAÇÃO PURA.
Eu estou pensando como eu poderia criar entidades de jogos como agregação pura usando C #. Ainda não compreendi o conceito de como isso pode funcionar. (Talvez a entidade seja uma matriz de objetos implementando uma certa interface ou tipo de base?)
Meu pensamento atual ainda envolve ter uma classe concreta para cada tipo de entidade que implementa as interfaces relevantes (IMoveable, ICollectable, ISpeakable etc.).
Como posso criar uma entidade puramente como uma agregação sem ter nenhum tipo concreto para essa entidade?
fonte
Respostas:
A abordagem de "agregação pura" descrita por West nesse artigo vinculado evita completamente um objeto "entidade". Existem componentes flutuando na memória, mas eles são unidos apenas por relacionamentos implícitos, se é que existem.
Uma maneira de fazer isso é a chamada abordagem externa . Nesse sistema, os componentes são mantidos por sistemas que os gerenciam ou, de outra forma, os controlam (eu uso o termo "gerenciar" aqui, mas você não deve considerar que isso significa que estou sugerindo que você tenha várias classes * Manager para realizar tipos de componentes). Por exemplo, seu sistema de física pode se apegar a várias coisas que representam cada corpo rígido em seu mundo de simulação e pode expor essas coisas como Componentes Físicos. Os componentes podem ser os objetos reais manipulados pelo subsistema em questão ou podem ser proxies para esses objetos, conforme necessário.
Nesse sistema, não há necessariamente a necessidade de uma classe "Entity" manter uma coleção de referências aos componentes que a compõem; em vez disso, é emitida uma notificação sobre a criação ou destruição de uma "entidade" e cada subsistema que manipula componentes analisa a descrição da entidade criada / destruída (que normalmente é carregada de alguns dados) e determina se um componente é necessário para ela.
Uma das vantagens dessa abordagem é que você obtém uma boa localidade de referência para cada componente. Infelizmente, é um pouco estranho, no geral, e não é o sabor mais amigável das entidades baseadas em componentes que encontrei. Às vezes, é realmente conveniente ter um objeto real que represente uma entidade, mesmo que esse objeto faça pouco mais do que agregar referências fracas a componentes que ainda são mantidos por outros subsistemas (se nada mais, ele fornece uma maneira fácil de rotear mensagens entre componentes) .
Existem várias maneiras de implementar sistemas de objetos de jogo orientados a componentes; -lo realmente, realmente, realmente ajuda se você tem uma idéia sólida dos requisitos que você quer fora de seu sistema - você pode olhar para o que os quadros populares como Unidade fazer por exemplos. Sem definir requisitos rigorosos para você, você pode se deparar com o problema de "projetar" infinitamente o sistema sem realmente construí-lo, tentando em vão encontrar a implementação perfeita. Por alguma razão, eu já vi isso muito com sistemas de componentes.
fonte
A maneira como o Unity faz é que todos os scripts (no seu caso, código de jogo específico para um tipo de objeto de jogo) derivem de uma classe base
MonoBehaviour
, que por sua vez deriva da classe mais relevante para o seu casoComponent
. Você nunca edita (e não tem acesso ao código) a classe GameObject.O objeto do jogo é responsável por conter todos esses
Component
s. Também é ostensivamente encarregado de chamar as funções relevantes sobre eles (ieUpdate
). O Unity usa a reflexão para determinar para quais funções chamar (consulte a seção "Funções substituíveis" nesta página ), mas você provavelmente desejaria torná-las virtuais.Portanto, um dos mecanismos que você usa no Unity é obter componentes no seu objeto de jogo atual (ou em seus filhos) por tipo. Existem algumas propriedades auxiliares que envolvem algumas das coisas comuns. Por exemplo, se você deseja acessar o
Transform
componente do objeto do jogo (para controlar a posição / rotação / escala do objeto do jogo), normalmente seria necessário fazer algo parecidothis.GetComponent<Transform>().position
, mas eles o envolvem em umathis.transform.position
chamada auxiliar . Outro padrão comum é acessar oRenderer
componente atual do objeto do jogo . Portanto, se você quiser fazer algo como alterar o material atual do objeto do jogo, de outro script, faça algo comothis.renderer.material = someOtherMaterial
, e o objeto do jogo será atualizado adequadamente.Uma das maneiras pelas quais isso funciona no Unity é que o editor deles é configurado para que você possa criar objetos de jogo em sua cena que possuam componentes já conectados. No caso do Unity, todos os objetos do jogo têm um
Transform
componente, mas também podem conter tipos internos comoAudioListener
ouRenderer
, que fazem o que você esperaria. Ou você pode adicionar seus próprios componentes que fazem o que você deseja que eles façam. O editor também expõe campos públicos / serializáveis em seus componentes para que você não precise criar scripts diferentes se quiser usar o mesmo código básico, mas alterar alguns números mágicos.Em suma, é muito esperto, e eu sugiro baixar a versão gratuita do Unity e brincar com a forma como o sistema de script deles é configurado como uma prova bastante decente do conceito do que você deseja alcançar.
fonte
Eu gosto de uma abordagem mais próxima da visão de Adam no blog da t-machine. Os componentes devem ser apenas dados e os sistemas fazem todo o trabalho com eles.
Veja um exemplo de implementação do ES em C #: https://github.com/thelinuxlich/artemis_CSharp
E um exemplo de jogo usando-o (XNA 4): https://github.com/thelinuxlich/starwarrior_CSharp
fonte