Entity-Component-System
O que é Entity-Component-System?
editarEntity-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
editarConsidere 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?
editarUm 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:
- Java: Dominion
- C++: Entt
- C#: Flecs
- Kotlin: Fleks
- RUST: Specs
- Python: Esper
- Go: Arche
- Zig: Mach
- Lua: LuaECS
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?
editarA 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
editarEHRLICH, 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.