Por que é uma má idéia armazenar métodos em Entidades e Componentes? (Juntamente com outras questões do Sistema de entidades.)


16

Este é um seguimento desta pergunta, que eu respondi, mas esta aborda um assunto muito mais específico.

Essa resposta me ajudou a entender o Entity Systems ainda melhor do que o artigo.

Eu li o artigo (sim) sobre os sistemas de entidades e ele me disse o seguinte:

Entidades são apenas uma identificação e uma matriz de componentes (os artigos dizem que armazenar entidades em componentes não é uma boa maneira de fazer as coisas, mas não fornece uma alternativa).
Componentes são pedaços de dados que indicam o que pode ser feito com uma determinada entidade.
Sistemas são os "métodos", eles realizam manipulação de dados nas entidades.

Isso parece realmente prático em muitas situações, mas a parte de componentes serem apenas classes de dados está me incomodando. Por exemplo, como eu poderia implementar minha classe Vector2D (Position) em um sistema de entidades?

A classe Vector2D contém dados: coordenadas x e y, mas também possui métodos , que são cruciais para sua utilidade e distinguem a classe de apenas uma matriz de dois elementos. Exemplo métodos são: add(), rotate(point, r, angle), substract(), normalize(), e todos os outros padrão, métodos úteis, e absolutamente necessário que as posições (que são exemplos da classe Vector2D) deve ter.

Se o componente fosse apenas um detentor de dados, não seria possível ter esses métodos!

Uma solução que provavelmente poderia aparecer seria implementá-los dentro de sistemas, mas isso parece muito contra-intuitivo. Esses métodos são coisas que eu quero executar agora , que estejam completos e prontos para uso. Não quero esperar pela MovementSystemleitura de um conjunto caro de mensagens que a instruem a executar um cálculo na posição de uma entidade!

E, o artigo afirma claramente que apenas os sistemas devem ter alguma funcionalidade, e a única explicação para isso, que eu pude encontrar, foi "evitar OOP". Antes de tudo, não entendo por que devo me abster de usar métodos em entidades e componentes. A sobrecarga de memória é praticamente a mesma e, quando acoplada aos sistemas, deve ser muito fácil de implementar e combinar de maneiras interessantes. Os sistemas, por exemplo, só poderiam fornecer lógica básica para entidades / componentes que conhecem a implementação por si mesmos. Se você me perguntar - isso é basicamente pegar os presentes do ES e OOP, algo que não pode ser feito de acordo com o autor do artigo, mas para mim parece uma boa prática.

Pense nisso desta maneira; existem muitos tipos diferentes de objetos desenháveis ​​em um jogo. Imagens antigas simples, animações ( update(), getCurrentFrame()etc), combinações desses tipos primitivos e todos eles poderiam simplesmente fornecer um draw()método ao sistema de renderização, que não precisa se preocupar com a maneira como o sprite de uma entidade é implementado. sobre a interface (desenho) e a posição. E então, eu precisaria apenas de um sistema de animação que chamaria métodos específicos de animação que nada têm a ver com a renderização.

E apenas mais uma coisa ... Existe realmente uma alternativa para matrizes quando se trata de armazenar componentes? Não vejo outro lugar para os componentes serem armazenados além de matrizes dentro de uma classe Entity ...

Talvez essa seja uma abordagem melhor: armazene componentes como propriedades simples de entidades. Por exemplo, um componente de posição seria colado entity.position.

A única outra maneira seria ter algum tipo de tabela de pesquisa estranha dentro dos sistemas, que referencia diferentes entidades. Mas isso parece muito ineficiente e mais complicado de desenvolver do que simplesmente armazenar componentes na entidade.


Alexandre, você está fazendo muitas edições para obter outro selo? Porque isso é impertinente e impertinente, ele continua colidindo com uma tonelada de tópicos antigos.
Jhocking 20/06

Respostas:


25

Eu acho totalmente bom ter métodos simples para acessar, atualizar ou manipular os dados nos componentes. Eu acho que a funcionalidade que deve ficar de fora dos componentes é a lógica. As funções utilitárias estão perfeitas. Lembre-se, o sistema de componente de entidade é apenas uma diretriz, não regras estritas que você precisa seguir. Não faça o possível para segui-los. Se você acha que faz mais sentido fazê-lo de uma maneira, faça-o dessa maneira :)

EDITAR

Para esclarecer, seu objetivo é não evitar POO . Isso seria bem difícil na maioria dos idiomas comuns usados ​​atualmente. Você está tentando minimizar a herança , que é um aspecto amplo do POO, mas não é obrigatório. Você quer se livrar da herança do tipo Objeto-> MobileObject-> Criatura-> Bipedal-> Human.

No entanto, não há problema em ter alguma herança! Você está lidando com um idioma que é fortemente influenciado pela herança, é muito difícil não usar nada disso. Por exemplo, você pode ter uma Componentclasse ou interface que todos os outros componentes estendem ou implementam. O mesmo acordo com a sua Systemturma. Isso facilita muito as coisas. Eu recomendo fortemente que você dê uma olhada na estrutura Artemis . É de código aberto e tem alguns exemplos de projetos. Abra essas coisas e veja como elas funcionam.

Para Artemis, as entidades são armazenadas em uma matriz, simples. No entanto, seus componentes são armazenados em uma matriz ou matrizes (separadas das entidades). A matriz de nível superior agrupa a matriz de nível inferior por tipo de componente. Portanto, cada tipo de componente tem sua própria matriz. A matriz de nível inferior é indexada pelo ID da entidade. (Agora não tenho certeza se faria dessa maneira, mas é assim que é feito aqui). Artemis reutiliza IDs de entidade, para que o ID máximo de entidade não seja maior que o número atual de entidades, mas você ainda pode ter matrizes esparsas se o componente não for usado com freqüência. De qualquer forma, não vou separar muito isso. Este método para armazenar entidades e seus componentes parece funcionar. Eu acho que seria um ótimo primeiro passo para implementar seu próprio sistema.

As entidades e componentes são armazenados em um gerente separado.

A estratégia que você menciona, fazendo as entidades armazenarem seus próprios componentes ( entity.position), é meio que contra o tema dos componentes da entidade, mas é totalmente aceitável se você achar que faz mais sentido.


1
Hmm, isso simplifica bastante a situação, obrigado! Eu pensei que havia alguma coisa mágica "você vai se arrepender mais tarde" acontecendo, e eu simplesmente não conseguia ver!
jcora

1
Na, eu os uso totalmente no meu sistema de componentes de entidade. Eu até tenho alguns componentes que herdam de um pai comum, suspiro . Acho que a única lamentando que você gostaria de fazer é se você tentou contornar não usando métodos como esse. É tudo sobre fazer o que faz mais sentido para você. Se fizer sentido usar herança ou colocar alguns métodos em um componente, faça isso.
MichaelHouse

2
Aprendi com minha última resposta sobre esse assunto. Disclaimer: Eu não estou dizendo que esta é a maneira de fazê-lo. :)
MichaelHouse

1
Sim, eu sei como pode ser assustador aprender um novo paradigma. Felizmente, você pode usar aspectos do antigo paradigma para facilitar as coisas! Atualizei minha resposta com informações de armazenamento. Se você olhar para Artemis, confira EntityManagercomo é onde as coisas são armazenadas.
MichaelHouse

1
Agradável! Será um mecanismo muito bom quando terminar. Boa sorte com isso! Obrigado por fazer perguntas interessantes.
MichaelHouse

10

Este artigo não é com o qual eu particularmente concordo, portanto, minha resposta será um pouco crítica, eu acho.

Isso parece realmente prático em muitas situações, mas a parte de componentes serem apenas classes de dados está me incomodando. Por exemplo, como eu poderia implementar minha classe Vector2D (Position) em um sistema de entidades?

A idéia não é garantir que nada no seu programa seja diferente de um ID, componente ou sistema de entidade - é garantir que os dados e o comportamento da entidade sejam criados através da composição de objetos, em vez de usar uma árvore de herança complexa ou, pior ainda, tentar coloque todas as funcionalidades possíveis em um único objeto. Para implementar esses componentes e sistemas, você certamente terá dados normais, como vetores, que são, na maioria dos idiomas, melhor representados como uma classe.

Ignore a parte do artigo que sugere que isso não é OOP - é tão OOP quanto qualquer outra abordagem. Quando a maioria dos compiladores ou tempos de execução da linguagem implementam métodos de objeto, é basicamente como qualquer outra função, exceto que existe um argumento oculto chamado thisor self, que é um ponteiro para um local na memória onde os dados desse objeto são armazenados. Em um sistema baseado em componentes, o ID da entidade pode ser usado para descobrir onde os componentes relevantes (e, portanto, os dados) estão para uma determinada entidade. Assim, o ID da entidade é equivalente a um ponteiro this / self, e os conceitos são basicamente a mesma coisa, apenas reorganizados um pouco.

E, o artigo afirma claramente que apenas os sistemas devem ter alguma funcionalidade, e a única explicação para isso, que eu pude encontrar, foi "evitar OOP". Antes de tudo, não entendo por que devo me abster de usar métodos em entidades e componentes.

Boa. Os métodos são uma maneira eficaz de organizar seu código. O importante a tirar da idéia "evitar OOP" é evitar o uso de herança em todos os lugares para estender a funcionalidade. Em vez disso, divida a funcionalidade em componentes que podem ser combinados para fazer a mesma coisa.

Pense nisso desta maneira; existem muitos tipos diferentes de objetos desenháveis ​​em um jogo. Imagens antigas simples, animações (update (), getCurrentFrame (), etc), combinações desses tipos primitivos e todas elas poderiam simplesmente fornecer um método draw () ao sistema de renderização.

A idéia de um sistema baseado em componentes é que você não teria classes separadas para elas, mas teria uma única classe Object / Entity, e a imagem seria um Objeto / Entidade que possui um ImageRenderer, as Animações seriam um Object / Entidade que possui um AnimationRenderer etc. Os sistemas relevantes saberiam como renderizar esses componentes e, portanto, não haveria nenhuma classe base com o método Draw ().

[...] que então não precisa se preocupar com a forma como o sprite de uma entidade é implementado, apenas com a interface (draw) e a posição. E então, eu precisaria apenas de um sistema de animação que chamaria métodos específicos de animação que nada têm a ver com a renderização.

Claro, mas isso não funciona bem com componentes. Você tem 3 opções:

  • Cada componente implementa essa interface e possui um método Draw (), mesmo que nada seja desenhado. Se você fizesse isso para todas as funcionalidades, os componentes pareceriam muito feios.
  • Somente componentes que têm algo para desenhar implementam a interface - mas quem decide em quais componentes chamar Draw ()? Um sistema precisa, de alguma forma, consultar cada componente para ver qual interface é suportada? Isso seria propenso a erros e potencialmente complicado de implementar em alguns idiomas.
  • Os componentes são sempre manipulados por seu sistema proprietário (que é a idéia no artigo vinculado). Nesse caso, a interface é irrelevante porque um sistema sabe exatamente com qual classe ou tipo de objeto está trabalhando.

E apenas mais uma coisa ... Existe realmente uma alternativa para matrizes quando se trata de armazenar componentes? Não vejo outro lugar para os componentes serem armazenados além de matrizes dentro de uma classe Entity ...

Você pode armazenar os componentes no sistema. A matriz não é o problema, mas onde você armazena o componente.


+1 Obrigado por outro ponto de vista. É bom ter alguns ao lidar com um assunto tão ambíguo! Se você estiver armazenando componentes no sistema, isso significa que os componentes podem ser modificados apenas por um sistema? Por exemplo, o sistema de desenho e o sistema de movimento acessariam o componente de posição. Onde você o armazena?
MichaelHouse

Bem, eles só armazenariam um ponteiro para esses componentes, que podem, desde que eu esteja em algum lugar ... Além disso, por que você armazenaria componentes em sistemas? Existe uma vantagem nisso?
jcora

Estou correto, @Kylotan? É assim que eu faria isso, parece lógico ...
jcora

No exemplo da Adam / T-Machine, a intenção é que exista 1 sistema por componente, mas um sistema certamente pode acessar e modificar outros componentes. (Isso dificulta os benefícios multithreading dos componentes, mas isso é uma questão diferente.)
Kylotan

1
O armazenamento de componentes no sistema permite uma melhor localidade de referência para esse sistema - esse sistema somente (geralmente) funciona com esses dados, então por que percorrer toda a memória do computador de entidade para entidade para obtê-los? Também ajuda na concorrência, porque você pode colocar um sistema inteiro e seus dados em um núcleo ou processador (ou mesmo em um computador separado, em MMOs). Novamente, esses benefícios diminuem quando um sistema acessa mais de um tipo de componente, de modo que isso deve ser levado em consideração ao decidir onde dividir as responsabilidades do componente / sistema.
Kylotan

2

Um vetor são dados. As funções são mais parecidas com funções utilitárias - elas não são específicas para a instância dos dados, podem ser aplicadas a todos os vetores independentemente. Uma boa maneira de pensar sobre isso é: Essas funções podem ser reescritas como métodos estáticos? Se assim for, é apenas utilidade.


Eu sei disso, mas o problema é que chamar métodos é mais rápido e pode ser feito no local por um sistema ou qualquer outra coisa que possa precisar manipular a posição de uma entidade. Expliquei que, veja também, a pergunta tem muito mais do que apenas isso, acredito.
jcora
Ao utilizar nosso site, você reconhece que leu e compreendeu nossa Política de Cookies e nossa Política de Privacidade.
Licensed under cc by-sa 3.0 with attribution required.