Entity-Component-System

O que é Entity-Component-System?

editar

Entity-Component-System, ou ECS, é um padrão de arquitetura de software.

Esse padrão visa separar a representação dos dados de um objeto da lógica de seu comportamento, e consiste na interação entre três grandes peças: as entidades, os componentes e os sistemas.

  • Entidades: são objetos de propósito geral, normalmente identificados por um id. Não possuem lógica nem dados, exceto pelo identificador e por uma lista de componentes, os quais definem suas propriedades e características.
  • Componentes: representam dados de um aspecto (posição, velocidade, etc.) que pode ser atribuído a uma entidade. Também não possuem lógica.
  • Sistemas: são responsáveis por realizar as operações nos componentes e consequentemente implementar os comportamentos das entidades.

A arquitetura é considerada um padrão orientado a dados. Um dos primeiros casos de uso do design orientado a dados baseado em composição em um software de larga escala ocorreu em um jogo digital chamado Thief: The Dark Project (1998), onde os desenvolvedores adotaram uma filosofia de criar componentes altamente reutilizáveis para a game engine. Segundo o lead programmer do jogo Tom Leonard, essa abordagem resultou na não existência de qualquer tipo de hierarquia (definida em código) entre objetos do jogo. A abordagem deu tão certo que a equipe foi capaz de usar o mesmo arquivo executável durante grande parte do desenvolvimento para Thief e System Shock, dois jogos muito diferentes, apenas mudando a hierarquia de objetos e conjunto de dados durante a execução.

Desde então, a arquitetura foi sendo refinada e melhor descrita. Diversos frameworks foram criados e o padrão tem sido amplamente usado no desenvolvimento de jogos.

Exemplo

editar

Considere que gostaríamos de desenvolver um jogo onde os objetos são capazes de se mover e são desenhados na tela em suas respectivas posições. Portanto, cada objeto pode ter uma posição no espaço (position), uma velocidade (velocity) e um sprite que será desenhado (sprite). Usando ECS, temos que cada característica dessas é um componente diferente, além de termos dois sistemas: um para movimentar as entidades e outro para desenhar as entidades. Um UML dessa implementação seria:

 

Assim, o update() dos sistemas seria chamado regularmente pela classe Game, que gerencia o jogo, providenciando uma lista de entidades para a função. Então, a função update() de cada sistema é algo como:

class MovementSystem : System {
  override fun update(entities: List<Entity>) {
    for (entity in entities) {
      if entity.contains(Position) and entity.contains(Velocity):
          positionComponent = entitiy.getComponent(Position)
          velocityComponent = entitiy.getComponent(Velocity)
          positionComponent.position += velocityComponent.velocity
    }
  }
}
class DrawSystem : System {
  override fun update(entities: List<Entity>) {
    for (entity in entities) {
      if entity.contains(Position) and entity.contains(Sprite):
          positionComponent = entitiy.get_component(Position)
          spriteComponent = entitiy.get_component(Sprite)
          draw(positionComponent.position, spriteComponent.texture)
    }
  }
  
  fun draw(position: Vector2D, texture: Texture) { /* ... */ }
}

Note como uma entidade pode, em tempo de execução, deixar de se mover: basta remover seu componente Velocity. Semelhantemente, uma entidade poderia deixar de ser desenhada na tela, caso tivesse seu Sprite removido. Além disso, o sistema de movimentação poderia ser completamente reimplementado sem a necessidade de alterar os componentes ou entidades.

Por que usar ECS?

editar

Um dos grandes contrapontos entre a ECS e o uso da programação orientada a objetos tradicional é quanto ao uso de herança. A arquitetura ECS sempre favorece a criação por meio da composição em vez da herança, isso acontece porque a composição traz mais flexibilidade em relação à herança e evita a repetição desnecessária de código.

Com a composição, entidades podem ter propriedades dinamicamente alteradas, ou seja, modificas em tempo de execução. Outra vantagem do uso da composição em vez da herança é que a herança inevitavelmente aumenta o grau de complexidade do código por conta da hierarquia entre classes. Já a composição, por sua vez, não apresenta esse aumento de complexidade e assim auxilia os programadores a terem um entendimento mais claro e rápido do código desenvolvido.

Outra divergência entre o ECS e o POO tradicional é quanto ao encapsulamento de dados. Em POO, os objetos restringem o acesso aos seus dados e disponibilizam métodos controlados para a modificação desses dados. Já na arquitetura ECS, os dados e os comportamentos não possuem essa relação direta e os dados são expostos completamente por objetos POD (plain old data).

Um grande ponto positivo da separação comportamentos/dados é que o código de comportamentos fica mais enxuto, localizado e mais fácil de ler. Isso traz grandes benefícios para a aplicação porque qualquer mudança necessária em um sistema fica restrita ao código daquele sistema e não requer mudanças em componentes ou entidades. Além disso, com o código desta maneira a procura de erros e testabilidade fica mais facil pelas mesmas razões.

A manipulação da memória em sistemas ECS pode ser feita de algumas maneiras diferentes, cada uma possuindo vantagens distintas, portanto sua escolha deve depender do que é desejável otimizar. Mais informações podem ser vistas aqui.

De maneira geral, a arquitetura favorece o armazenamento de dados em blocos contíguos de memória. Esse armazenamento de memória beneficia o cache-hit, ou seja, faz com que os dados a serem utilizados em seguida pela aplicação estejam mais frequentemente na memória cache, então não precisam ser solicitados a outras memórias, melhorando o desempenho. Manipular dados em bloco contíguos também reduz a fragmentação de memória. Além disso, por conta da interdependência entre os sistemas, o padrão ECS se torna ainda mais eficiente com a utilização da programação paralela e multithreading.

Vale ressaltar que o ganho de performance depende da natureza da aplicação e do uso eficiente das partes do ECS, não sendo uma garantia universal de melhoria.

A arquitetura ECS é muito recomendada para projetos de desenvolvimento de jogos, simulações complexas, e projetos que necessitam de ampla flexibilidade como projetos de realidade virtual.

Aqui está uma lista de projetos ativos e com licença MIT que utilizam a arquitetura ECS nas mais diversas linguagens de programação:

Uma lista dos projetos mais relevantes que utilizam do padrão ECS pode ser encontrada aqui. Dois dos mais relevantes são os jogos Minecraft e Overwatch 2.

Por que não usar ECS?

editar

A arquitetura ECS pode não ser tão recomendada para aplicações que não necessitem de perfomance ou que tenham requisitos de processamento limitados. Para projetos mais enxutos, a arquitetura pode trazer uma sobrecarga desnecessária, dificultando o desenvolvimento.

Além disso, se sua aplicação não necessita de tamanha flexibilidade, ou seja, os dados são relativamente estáticos e não requerem tanta manipulação ou se as componentes da sua aplicação são utilizadas de maneira limitada pelas entidades então o overhead de memória da arquitetura possa não valer a pena.

Por fim, vale ressaltar que o padrão Entity-Component-System é um padrão relativamente novo, com um grande potencial em um escopo bastante específico, e acima de tudo de relativa dificuldade de implementação, logo não é recomendado o uso do padrão caso a equipe de desenvolvimento não possua conhecimento sólido da abordagem da arquitetura.

Bibliografia

editar

EHRLICH, Justin. The Component Entity System for Virtual Environments.

HÄRKÖNEN, Toni. Advantages and Implementation of Entity-Component-Systems. 2019.

LANGE, Patrick; WELLER, Rene; ZACHMANN, Gabriel. Wait-free hash maps in the entity-component-system pattern for realtime interactive systems. In: 2016 IEEE 9th Workshop on Software Engineering and Architectures for Realtime Interactive Systems (SEARIS). IEEE, 2016. p. 1-8.

LEONARD, TOM. Postmortem: Thief: The Dark Project. Gamasutra. Disponível em: https://web.archive.org/web/20220518165826/http://www.gamasutra.com/view/feature/3355/postmortem_thief_the_dark_project.php?print=1.

MERTENS, SANDERS. ECS-FAQ. GitHub. Disponível em: https://github.com/SanderMertens/ecs-faq?tab=readme-ov-file.

WEBSTER, Carson. Entity Component Systems Usefulness.