No momento, estou criando um pequeno projeto de hobby para voltar ao desenvolvimento de jogos e decidi estruturar minhas entidades usando um ECS (Entity Component System). Esta implementação de um ECS está estruturada da seguinte forma:
- Entidade : no meu caso, é um
int
identificador exclusivo usado como chave para uma lista de componentes. - Componente : Armazena apenas dados, por exemplo, o
Position
componente possui umx
ey
coordenar, e oMovement
componente contém umspeed
edirection
variável. - Sistema : componentes canetas, por exemplo, que leva os
Position
eMovement
componentes e adiciona ospeed
edirection
para a posição dex
ey
coordenadas.
Isso funciona bem, mas agora desejo implementar scripts nos meus jogos, na forma de uma linguagem de script. Em projetos anteriores, usei uma implementação OOP de objetos de jogo, o que significava que o script era bastante direto. Por exemplo, um script simples pode ser algo como isto:
function start()
local future = entity:moveTo(pos1)
wait(future)
local response = entity:showDialog(dialog1)
if wait(response) == 1 then
local itemStack = entity:getInventory():removeItemByName("apple", 1)
world:getPlayer():getInventory():addItemStack(itemStack)
else
entity:setBehavior(world:getPlayer(), BEHAVIOR_HOSTILE)
end
end
No entanto, ao usar um ECS, a própria entidade não possui funções como , moveTo
ou seja getInventory
, o script acima, escrito no estilo ECS, seria algo como isto:
function start()
local movement = world:getComponent(MOVEMENT, entity)
movement:moveTo(pos1)
local position = world:getComponent(POSITION, entity)
local future = Future:untilEquals(position.pos, pos1)
wait(future)
local dialogComp = world:getComponent(DIALOG, entity)
local response = dialogComp:showDialog(dialog1)
if wait(response) == 1 then
local entityInventory = world:getComponent(INVENTORY, entity)
local playerInventory = world:getComponent(INVENTORY, world:getPlayer())
local itemStack = entityInventory:removeItemByName("apple", 1)
playerInventory:addItemStack(itemStack)
else
local entityBehavior = world:getComponent(BEHAVIOR, entity)
local playerBehavior = world:getComponent(BEHAVIOR, world:getPlayer())
entityBehavior:set(playerBehavior, BEHAVIOR_HOSTILE)
end
end
Isso é muito mais detalhado se comparado à versão OOP, o que não é desejável quando o script é voltado principalmente para não programadores (jogadores do jogo).
Uma solução seria ter algum tipo de objeto wrapper que encapsule Entity
e forneça funções como moveTo
diretamente e lide com o resto internamente, embora essa solução pareça subótima, pois é preciso muito trabalho para cobrir todos os componentes e todos os Quando um novo componente é adicionado, você precisa alterar o objeto wrapper com novas funções.
Para todos os desenvolvedores de jogos que já implementaram scripts em um ECS antes - como você fez isso? O foco principal aqui é a usabilidade do usuário final, com o menor custo possível de "manutenção" (de preferência, você não precisa alterá-lo toda vez que adicionar um componente).
moveTo
método como parte do sistema subjacente no seu caso de uso, por exemplo, MovementSystem? Dessa forma, não somente então você poderá usá-lo nos scripts que escrever, mas também poderá usá-lo como parte do código C ++, sempre que precisar. Portanto, sim, você terá que expor novos métodos à medida que novos sistemas são adicionados, mas isso é de se esperar, pois seu comportamento totalmente novo é introduzido de qualquer maneira.
System
(s) classe (s) para permitir que os componentes permaneçam estruturas de dados.