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).
fonte
System
(s) classe (s) para permitir que os componentes permaneçam estruturas de dados.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.Respostas:
Você pode criar um sistema ScriptExecutionSystem que opera em todas as entidades com um componente Script. Ele obtém todos os componentes da entidade que podem ser úteis para expor ao sistema de script e os passa para a função com script.
Outra abordagem seria fazer com que seus usuários também adotassem o ECS e permitissem que eles definissem seus próprios componentes e implementassem seus próprios sistemas usando a linguagem de script.
fonte
A ECS tem seus prós e contras. O script fácil de usar não é um dos seus profissionais.
O problema que o ECS resolve é a capacidade de ter um grande número de coisas semelhantes em seu jogo ao mesmo tempo, mantendo o desempenho. Mas esta solução tem um custo - o custo de uma arquitetura fácil de usar. Não é a melhor arquitetura para todos os jogos.
Por exemplo, o ECS teria sido uma ótima opção para os Space Invaders , mas não tanto para o PacMan .
Portanto, não é exatamente a resposta que você estava procurando, mas é possível que o ECS não seja a ferramenta certa para o seu trabalho.
Se você adicionar um invólucro, observe o custo adicional. Se você acabar removendo o aumento de desempenho do ECS em seu invólucro, terá o pior dos dois mundos.
Mas, para responder diretamente à sua pergunta - "Para todos os desenvolvedores de jogos que já implementaram scripts em um ECS antes - como você fez isso?"
Praticamente exatamente como você está fazendo, sem um invólucro. As entidades têm apenas um identificador. Os componentes têm apenas dados. Os sistemas não têm nada além de lógica. Sistemas que aceitam entidades com os componentes necessários são executados. Adicione sistemas, entidades e componentes livremente.
Uma vez eu usei uma estrutura com um quarto aspecto, chamado quadro negro. Era basicamente uma maneira de os sistemas se comunicarem. Criou mais problemas do que resolveu.
Relacionado: Devo implementar o Entity Component System em todos os meus projetos?
fonte
Com o ECS, você pode dividir em uma única responsabilidade; portanto, qualquer entidade que se mova desejará dois componentes de dados: um MoveComponent e um MoveSpeedComponent.
agora, na sua conversão, você adiciona esses componentes às suas entidades
Agora que temos a conversão e os dados que podemos mudar para o sistema, removi o sistema de entrada para facilitar a leitura, mas se você quiser saber mais sobre o sistema de entrada, terei tudo isso listado no meu artigo na próxima semana sobre o Unity Connect.
observe que a classe acima está usando o Unity.Mathmatics. Isso é ótimo para poder usar diferentes funções matemáticas com as quais você está acostumado a trabalhar nos sistemas normais. Com tudo isso alinhado, agora você pode trabalhar no comportamento das entidades - novamente removi a entrada aqui, mas tudo isso é muito melhor explicado no artigo.
Agora você pode introduzir entidades que avançarão a uma velocidade.
Mas também isso moverá todas as entidades com esse comportamento para que você possa introduzir tags, por exemplo, se você adicionou uma PlayerTag, somente a entidade com playerTag IComponentData poderá executar o MoveForward se eu quiser mover o player apenas como o exemplo abaixo.
Vou me aprofundar nisso também no artigo, mas parece com isso em um ComponentSystem típico
Muito disso é explicado muito bem na apresentação do Angry Dots com Mike Geig, se você ainda não viu, recomendo dar uma olhada. Apontarei para o meu artigo também depois que ele terminar. Deve ser realmente útil obter várias daquelas coisas com as quais você está acostumado a trabalhar, como gostaria no ECS.
fonte