Até agora, os sistemas de componentes de entidade que eu usei funcionaram principalmente como artemis de Java:
- Todos os dados nos componentes
- Sistemas independentes sem estado (pelo menos na medida em que eles não exigem entrada na inicialização) iterando sobre cada entidade que contém apenas os componentes nos quais o sistema em particular está interessado
- Todos os sistemas processam suas entidades um tique e depois tudo começa de novo.
Agora, estou tentando aplicar isso a um jogo baseado em turnos pela primeira vez, com vários eventos e respostas que devem ocorrer em uma ordem definida em relação um ao outro, antes que o jogo possa seguir em frente. Um exemplo:
O jogador A recebe dano de uma espada. Em resposta a isso, a armadura de A entra em ação e diminui o dano recebido. A velocidade de movimento de A também é reduzida como resultado de ficar mais fraca.
- O dano sofrido é o que desencadeia toda a interação
- A armadura deve ser calculada e aplicada ao dano recebido antes que o dano seja aplicado ao jogador
- A redução da velocidade de movimento não pode ser aplicada a uma unidade até que o dano tenha sido efetivamente causado, pois depende do valor final do dano.
Eventos também podem acionar outros eventos. Reduzir o dano da espada usando armadura pode fazer com que a espada se quebre (isso deve ocorrer antes que a redução de dano seja concluída), que por sua vez pode causar eventos adicionais em resposta a ela, essencialmente uma avaliação recursiva dos eventos.
Em suma, isso parece levar a alguns problemas:
- Muitos ciclos de processamento desperdiçados: a maioria dos sistemas (exceto itens que sempre são executados, como renderização) simplesmente não tem nada que valha a pena fazer quando não é a "vez deles" de trabalhar, e passa a maior parte do tempo esperando o jogo entrar um estado de trabalho válido. Isso afeta todo sistema com verificações que continuam crescendo em tamanho, à medida que mais estados são adicionados ao jogo.
- Para descobrir se um sistema pode processar entidades que estão presentes no jogo, ele precisa de alguma maneira de monitorar outros estados de entidade / sistema não relacionados (o sistema responsável por causar dano precisa saber se a armadura foi aplicada ou não). Isso atrapalha os sistemas com múltiplas responsabilidades ou cria a necessidade de sistemas adicionais sem outra finalidade além de varrer a coleção de entidades após cada ciclo de processamento e se comunicar com um conjunto de ouvintes, dizendo a eles quando é bom fazer alguma coisa.
Os dois pontos acima assumem que os sistemas funcionam no mesmo conjunto de entidades, que acabam mudando de estado usando sinalizadores em seus componentes.
Outra maneira de resolver isso seria adicionar / remover componentes (ou criar entidades inteiramente novas) como resultado de um único sistema trabalhar para progredir no estado dos jogos. Isso significa que sempre que um sistema realmente possui uma entidade correspondente, ele sabe que tem permissão para processá-lo.
No entanto, isso torna os sistemas responsáveis por acionar os sistemas subseqüentes, dificultando o raciocínio sobre o comportamento dos programas, uma vez que os bugs não aparecem como resultado de uma única interação do sistema. A adição de novos sistemas também fica mais difícil, pois eles não podem ser implementados sem saber exatamente como eles afetam outros sistemas (e os sistemas anteriores podem ter que ser modificados para acionar os estados nos quais o novo sistema está interessado), meio que frustrando o propósito de ter sistemas separados. com uma única tarefa.
Isso é algo que eu vou ter que viver? Todos os exemplos de ECS que eu vi foram em tempo real e é realmente fácil ver como esse loop de uma iteração por jogo funciona nesses casos. E eu ainda preciso disso para renderização, parece realmente inadequado para sistemas que pausam a maioria dos aspectos de si sempre que algo acontece.
Existe algum padrão de design para avançar o estado do jogo que seja adequado para isso, ou devo apenas mover toda a lógica para fora do loop e, em vez disso, acioná-la somente quando necessário?