Saltar para o conteúdo

Entity-component-system

Origem: Wikipédia, a enciclopédia livre.

Em Engenharia de Software, ECS é o acrônimo de Entity Component System (em português: Sistema de Componente e Entidade), é um padrão de arquitetura de software usado principalmente no desenvolvimento de jogos eletrônicos. Um ECS segue o princípio da "composição ao invés de herança" que permite uma flexibilidade maior na definição de entidades, onde cada objeto em uma cena de um jogo é uma entidade (por exemplo inimigos, projéteis, veículos, etc.). Cada entidade consiste de um ou mais componentes que adicionam comportamento ou funcionalidade. Portanto, o comportamento de uma entidade pode ser alterado durante o tempo de execução simplesmente adicionando ou removendo componentes. Isso elimina problemas de ambiguidade com que sofrem as hierarquias de herança profunda e vasta, que são difíceis de entender, manter e estender. Abordagens comuns de ECS são altamente compatíveis e, muitas vezes, combinadas com técnicas de design orientado por dados.

O jogo de 1998 Thief: The Dark Project é o primeiro jogo que é conhecido publicamente por fazer uso do ECS.[1] No Entanto, os detalhes técnicos não foram publicados até muito mais tarde do que outros.[2]

O jogo de 2002, Dungeon Siege tem uma das primeiras implementações de ECS claramente definidas, como foi documentado por Scott Bilas na sua palestra na GDC do mesmo ano.[3]

A palestra de Bilas menciona explicitamente alguns conceitos que agora são padrões: banco de dados para a composição, esquema "dirigido por dados" (não apenas as propriedades), componentes como itens independentes, etc.

Em 2007, a equipe que desenvolveu Operation Flashpoint: Dragon Rising experimentou com designs de ECS, incluindo aquelas inspiradas por Bilas/Dungeon Siege. Adam Martin, mais tarde, escreveu um relato detalhado da ECS design,[4] incluindo as definições das terminologias e conceitos centrais.[5] Em particular, o trabalho de Martin popularizou as ideias de "sistemas" como elementos de primeira classe, "entidades como identificações", "componentes como dados brutos" e "código armazenado em sistemas, e não em componentes ou entidades".

O motor de jogo Unity cresceu muito em popularidade a partir de 2008 a 2010, tornando o seu uso diferente de entidades e componentes conhecido e amplamente utilizado. No entanto, a Unity não descreveu formalmente sua abordagem, e "ECS" como um termo técnico é geralmente associado à abordagem de Bilas em 2002, a menos que especificado.

A terminologia de Martin, amplamente usada hoje:

  • Entidade: A entidade é um objeto de finalidade geral. Normalmente, consiste apenas de uma identificação única. Elas "etiquetam cada objeto no jogo como um item separado". Implementações normalmente utilizam um número inteiro para isso.[6]
  • Componente: dados brutos para um aspecto do objeto, e como ele interage com o mundo. "Rotula a entidade como possuindo este aspecto em particular". Implementações normalmente usam estruturas, classes, ou arranjos associativos.
  • Sistema: "Cada sistema é executado continuamente (como se cada sistema tivesse a sua própria thread) e executa ações globais em cada entidade que possui um componente do mesmo aspecto que esse Sistema."

Exemplo de jogo

[editar | editar código-fonte]

Suponha que há uma função que desenha. Isso seria um "sistema" que itera através de todas as entidades que tenham componentes físicos e visíveis, e as desenha. O componente visível normalmente teria algumas informações sobre a aparência de uma entidade (por exemplo: humano, monstro, faíscas voando, flechas voando), e usa o componente físico para saber as coordenadas de onde desenhar.

Outro sistema poderia ser detecção de colisão. O sistema iteraria através de todas as entidades que têm um componente física, pois não se preocupam com como a entidade é desenhada. Este sistema iria então, por exemplo, detectar flechas que colidem com monstros, e gera um evento quando isso acontece. O sistema não deveria precisar entender o que é uma flecha, e o que significa quando um outro objeto é atingido por uma seta.

Um outro componente poderia ser dados de saúde, e um sistema que gerencia a saúde. Componentes de saúde seriam anexados às entidades de humanos e monstros, mas não a entidades de flechas. O sistema de gestão de saúde receberia os eventos gerado a partir de colisões e atualizaria a saúde de acordo. Este sistema também poderia conhecer e iterar por todas as entidades que têm um componente da saúde, e regenerar saúde.

Design de uma entidade

[editar | editar código-fonte]

Uma entidade consiste apenas de uma identificação e um recipiente de componentes. A ideia é não ter nenhum método de jogo incorporado na entidade. O recipiente não precisa ser localizado fisicamente junto da entidade, mas deve ser de fácil localização e acesso.

É uma prática comum usar uma identificação única para cada entidade. Este não é um requisito, mas tem várias vantagens:

  • A entidade pode ser chamada usando a identificação ao invés de um ponteiro. Isso é mais robusto, pois permite que a entidade seja destruída sem deixar ponteiros selvagens.
  • Ajuda a salvar o estado externamente. Quando o estado é carregado novamente, não há necessidade de reconstruir ponteiros.
  • Os dados podem ser embaralhadas na memória conforme necessário.
  • Identificações de entidades podem ser usadas na comunicação em uma rede para identificar a entidade.

Algumas destas vantagens podem também ser conseguidas utilizando ponteiros inteligentes.

Há alguns problemas com o design ECS.

Comunicação entre sistemas

[editar | editar código-fonte]

A maneira normal de enviar dados entre sistemas é armazenar os dados em componentes. Por exemplo, a posição de um objeto pode ser atualizada regularmente. Esta posição é então usada por outros sistemas.

Se há vários eventos diferentes que não são frequentes, várias checagens serão necessárias em um ou mais componentes. Sistemas então são obrigados a fazer várias checagens em cada iteração, o que pode tornar-se ineficaz. Uma solução pode ser usar o observador padrão. Todos os sistemas que dependem de um evento devem inscrever-se nele. A ação a partir do evento será, portanto, executada apenas uma vez, evitando polling.

Custo de iterar por entidades

[editar | editar código-fonte]

Em algumas arquiteturas ECS, uma ideia básica é ter uma lista grande de todas as entidades. Cada sistema itera pela lista completa, e seleciona somente as entidades que são necessárias. Se o número de sistemas cresce, e o número de entidades é grande, o custo de iteração de todos os sistemas juntos pode ser muito alto.

Em outras arquiteturas ECS, cada tipo de componente é armazenado em listas separadas, então sistemas operam nesses tipos de componentes estão automaticamente iterando apenas pelos objetos que interessam a eles. Nesta arquitetura ECS comum, a desvantagem se torna uma grande vantagem em termos de desempenho, pois aproveita a CPU e cache de dados de forma mais eficiente.

Gerenciamento de dependências seguro

[editar | editar código-fonte]

A arquitetura ECS lida com dependências de uma forma muito segura e simples. Já que componentes são simples baldes de dados, eles não têm dependências. Cada sistema normalmente registra os componentes que uma entidade deve ter para que o sistema possa operar nela. Por exemplo, um sistema de renderização pode registrar o modelo, transformações, e componentes desenháveis. Em seguida, ele irá verificar cada entidade procurando esses componentes, e se a entidade tem todos eles, o sistema irá executar sua lógica naquela entidade. Se não, a entidade é simplesmente ignorada, sem a necessidade de árvores de dependência complexas. No entanto, este pode ser um lugar para bugs se esconderem, pois a propagação de valores de um sistema para outro através de componentes pode ser muito difícil de depurar.

Ter dados separados da lógica não ajuda a reduzir dependências, a redução de dependências é feita através de um projeto adequado de código. Você pode mover dados de uma classe com 100 dependências para um componente, e isto não vai resolver o problema das dependências, mas, em vez disso, faz com que a classe seja mais difícil de depurar. ECS pode ser utilizado quando os dados precisam ser vinculados a um tempo de vida.

A arquitetura ECS arquitetura utiliza composição ao invés de árvores de herança mais complexas. Uma entidade normalmente consistirá de uma identificação e uma lista de componentes que estão ligados a ela. Qualquer tipo de objeto no jogo pode ser criado adicionando os componentes corretos a uma entidade. Isso também pode permitir que o desenvolvedor facilmente adicione recursos de um tipo de objeto para outro, sem quaisquer problemas de dependência. Por exemplo, uma entidade "jogador" poderia ter um componente "bala" adicionado e, em seguida, ele iria cumprir os requisitos para ser manipulado por alguns sistemas que manipulam balas, o que poderia resultar em o jogador causando danos nas coisas com que colide.

  1. Leonard, Tom. «Postmortem: Thief: The Dark Project». Consultado em 19 de Janeiro de 2015 
  2. Church, Doug. «Object Systems». Chris Hecker's Website (Powerpoint). Consultado em 19 de Janeiro de 2015 
  3. Bilas, Scott. «A Data-Driven Game Object System» (PDF). Consultado em 25 de Dezembro de 2013 
  4. Martin, Adam. «Entity Systems are the Future of MMOG Development». Consultado em 25 de Dezembro de 2013 
  5. Martin, Adam. «Entity Systems are the Future of MMOG Development Part 2». Consultado em 25 de Dezembro de 2013 
  6. «Entity Systems Wiki». Consultado em 9 de Fevereiro de 2014