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 MovementSystem
leitura 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.
Respostas:
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
Component
classe ou interface que todos os outros componentes estendem ou implementam. O mesmo acordo com a suaSystem
turma. 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.fonte
EntityManager
como é onde as coisas são armazenadas.Este artigo não é com o qual eu particularmente concordo, portanto, minha resposta será um pouco crítica, eu acho.
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
this
orself
, 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.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.
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 ().
Claro, mas isso não funciona bem com componentes. Você tem 3 opções:
Você pode armazenar os componentes no sistema. A matriz não é o problema, mas onde você armazena o componente.
fonte
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.
fonte