Estou interessado no sistema de entidades baseado em componentes há um tempo e li inúmeros artigos sobre ele (Os jogos Insomiac , o bastante padrão Evolve Your Hierarchy , a T-Machine , Chronoclast ... só para citar alguns).
Todos eles parecem ter uma estrutura externa de algo como:
Entity e = Entity.Create();
e.AddComponent(RenderComponent, ...);
//do lots of stuff
e.GetComponent<PositionComponent>(...).SetPos(4, 5, 6);
E se você trouxer a ideia de dados compartilhados (este é o melhor design que eu já vi até agora, em termos de não duplicar dados em todos os lugares)
e.GetProperty<string>("Name").Value = "blah";
Sim, isso é muito eficiente. No entanto, não é exatamente o mais fácil de ler ou escrever; parece muito desajeitado e trabalhando contra você.
Pessoalmente, gostaria de fazer algo como:
e.SetPosition(4, 5, 6);
e.Name = "Blah";
Embora, é claro, a única maneira de chegar a esse tipo de design esteja de volta ao tipo de hierarquia Entidade-> NPC-> Enemy-> FlyingEnemy-> FlyingEnemyWithAHatOn que esse design tenta evitar.
Alguém viu um design para esse tipo de sistema de componentes que ainda é flexível, mas mantém um nível de facilidade de uso? E, nesse caso, consegue contornar o (provavelmente o problema mais difícil) o armazenamento de dados de uma maneira boa?
Que projetos existem para um sistema de entidade baseado em componentes que seja fácil de usar, mas ainda flexível?
fonte
Eu sugeriria que algum tipo de classe de interface para seus objetos de entidade seria bom. Ele poderia executar o tratamento de erros com a verificação para garantir que uma entidade contenha também um componente do valor apropriado em um local, para que você não precise fazer isso em todos os lugares em que acessar os valores. Como alternativa, na maioria dos projetos que já fiz com um sistema baseado em componentes, trato diretamente dos componentes, solicitando, por exemplo, seu componente posicional e depois acessando / atualizando diretamente as propriedades desse componente.
Muito básica em um nível alto, a classe absorve a entidade em questão e fornece uma interface fácil de usar para os componentes subjacentes da seguinte maneira:
fonte
No Python, você pode interceptar a parte 'setPosition'
e.SetPosition(4,5,6)
via declarar uma__getattr__
função na Entidade. Essa função pode percorrer os componentes e encontrar o método ou propriedade apropriado e retornar isso, para que a chamada ou atribuição da função vá para o local certo. Talvez o C # tenha um sistema semelhante, mas pode não ser possível devido ao fato de ser digitado estaticamente - presumivelmente não pode ser compilado ae.setPosition
menos que e tenha definidoPosition na interface em algum lugar.Você também pode fazer com que todas as suas entidades implementem as interfaces relevantes para todos os componentes que você pode adicionar a eles, com métodos de stub que geram uma exceção. Então, quando você chama addComponent, você apenas redireciona as funções da entidade da interface desse componente para o componente. Um pouco complicado embora.
Mas, talvez, mais fácil seria sobrecarregar o operador [] na sua classe Entity para procurar os componentes ligados para a presença de uma propriedade válida e retorno que, por isso pode ser atribuído a assim:
e["Name"] = "blah"
. Cada componente precisará implementar seu próprio GetPropertyByName e a Entidade chama cada um por vez até encontrar o componente responsável pela propriedade em questão.fonte
GetProperty
método .. Apenas a desvantagem é a manipulação e comparação de strings. Mas esse é um ponto discutível.Para expandir a resposta de Kylotan, se você estiver usando o C # 4.0, você pode digitar estaticamente uma variável para ser dinâmica .
Se você herdar de System.Dynamic.DynamicObject, poderá substituir TryGetMember e TrySetMember (entre os vários métodos virtuais) para interceptar nomes de componentes e retornar o componente solicitado.
Escrevi um pouco sobre sistemas de entidades ao longo dos anos, mas presumo que não posso competir com os gigantes. Algumas notas do ES (um pouco desatualizadas e não refletem necessariamente meu entendimento atual dos sistemas de entidades, mas vale a pena ler, imho).
fonte
object
parâmetro out - mas tudo isso será resolvido no tempo de execução. Eu acho que é uma maneira glorificada de manter uma interface fluente sobre ie entity.TryGetComponent (compName, componente out). Eu não tenho certeza se entendi a pergunta, no entanto :)Eu vejo muito interesse aqui no ES em C #. Confira meu porto da excelente implementação do ES Artemis:
https://github.com/thelinuxlich/artemis_CSharp
Além disso, um exemplo de jogo para você começar a trabalhar (usando o XNA 4): https://github.com/thelinuxlich/starwarrior_CSharp
Sugestões são bem-vindas!
fonte
Decidi usar o excelente sistema de reflexão do C #. Baseei-o no sistema de componentes gerais, além de uma mistura das respostas aqui.
Os componentes são adicionados com muita facilidade:
E pode ser consultado por nome, tipo ou (se comuns, como Saúde e Posição) por propriedades:
E removido:
Os dados são, novamente, acessados normalmente por propriedades:
Qual é o lado do componente armazenado; menos sobrecarga se não tiver HP:
A grande quantidade de digitação é auxiliada pelo Intellisense no VS, mas na maioria das vezes há pouca digitação - o que eu estava procurando.
Nos bastidores, estou armazenando um
Dictionary<Type, Component>
eComponents
substituindo oToString
método de verificação de nome. Meu design original usava principalmente strings para nomes e outras coisas, mas se tornou um porco para se trabalhar (olha, eu tenho que lançar em todos os lugares também a falta de propriedades de fácil acesso).fonte