ECS encontra SQL: o mundo como base de dados relacional

2024-11-01  |  
ECS SQL

Originalmente publicado em https://github.com/gilzoide/ecsql/blob/main/articles/01-ecs-databases-pt.md

Nota: esse documento assume que você tenha conhecimentos básicos de SQL

ECS: o básico

Entidade Componente Sistema, ou ECS, é um padrão de projeto que provê reusabilidade de código ao separar a lógica de negócio dos dados, comumente usado em jogos. Projetos ECS são compostos de:

Para informações mais detalhadas sobre conceitos do padrão ECS, recomendo a letura do ECS FAQ (em Inglês).

ECS e bases de dados

Um mundo de ECS pode ser enxergado como uma base de dados, que mantém os dados das entidades existentes e seus componentes. O mundo é consultado pelos sistemas de modo que somente as entidades que possuem o conjunto certo de componentes sejam processadas, usando uma espécie de operação JOIN entre diferentes componentes. Componentes podem ser adicionados e removidos de entidades em uma relação um-pra-um (1:1) ou um-pra-muitos (1:N).

Pensando nessa analogia de mundos ECS como bases de dados, e se a gente implementasse um framework de ECS utilizando um banco de dados SQL, por exemplo SQLite?

Os pros:

Os contras:

Mesmo que uma implementação de ECS utilizando SQL provavelmente fique mais lenta do que as implementações especializadas que existem por aí, SQLite é muito rápido no geral e provavelmente vai ser rápido o suficiente para muitos projetos de jogos, então quero experimentar essa ideia e ver o quão longe ela vai. Esse framework experimental de ECS baseado em SQLite se chamará ECSQL.

Representando entidades em SQL

Em ECS, entidades são representadas por identificadores únicos, normalmente números inteiros. Para representar entidades em SQL, basta criarmos uma tabela entity que mantém as entidades existentes, representadas por seu ID numérico:

CREATE TABLE entity (
  id INTEGER PRIMARY KEY
);

Representando componentes em SQL

Componentes são estruturas de dados que podem ser atreladas a entidades. Todos os componentes precisam estar associados a uma entidade existente. Quando a entidade for apagada, seus componentes devem ser apagados junto com ela.

Para mim, o melhor jeito de representar componentes em SQL é criando uma tabela para cada um.

A base para qualquer componente pode ser definida da seguinte forma:

CREATE TABLE component (
  -- ID da entidade a qual pertence
  -- "PRIMARY KEY": componentes são identificados pelo ID da sua entidade
  -- "REFERENCES entity(id)": chave estrangeira, relacionamento 1:1
  -- "ON DELETE CASCADE": componente será apagado quando sua entidade for apagada
  entity_id INTEGER PRIMARY KEY REFERENCES entity(id) ON DELETE CASCADE
);

Por exemplo, o componente "Posição" para objetos 3D poderia ser definido assim:

CREATE TABLE position (
  entity_id INTEGER PRIMARY KEY REFERENCES entity(id) ON DELETE CASCADE,

  -- Eixos de posição em 3D, todos com valor padrão 0
  x DEFAULT 0,
  y DEFAULT 0,
  z DEFAULT 0
);

Como um outro exemplo, o componente "Velocidade" poderia ser definido assim:

CREATE TABLE velocity (
  entity_id INTEGER PRIMARY KEY REFERENCES entity(id) ON DELETE CASCADE,

  -- Valor de velocidade, em m/s
  value DEFAULT 0
);

Consultando componentes em sistemas

Os sistemas em si não são armazenados na base de dados SQLile, mas utilizam SQL para consultar os componentes. Vamos usar o sistema "Mover" como exemplo mais uma vez, que é aplicado a entidades que possuem tanto o componente "Posição" quanto o componente "Velocidade" descritos acima. Podemos iterar sobre as entidades que possuem ambos os componentes utilizando a seguinte consulta:

SELECT 
  entity_id,
  position.x, position.y, position.z,
  velocity.value
FROM position
JOIN velocity USING(entity_id);

Podemos também suportar facilmente sistemas onde alguns componentes são obrigatórios, mas outros componentes são opcionais. Para isso, basta trocarmos o JOIN por LEFT JOIN ou RIGHT JOIN, de modo que a consulta retorna NULL pros valores dos componentes que não estão presentes na entidade.

Conclusão

Vimos uma breve introdução ao padrão de projeto ECS e como mundos ECS podem ser modelados usando bases de dado relacionais e SQL.

No próximo artigo, começaremos a implementar nosso framework experimental ECSQL. Vejo vocês lá!



Curte esse conteúdo? Patrocine meu trabalho usando o botão abaixo!