Estou tentando projetar um sistema de entidade baseado em componentes para fins de aprendizado (e uso posterior em alguns jogos) e estou tendo alguns problemas quando se trata de atualizar estados de entidade.
Não quero ter um método update () dentro do componente para evitar dependências entre componentes.
O que tenho atualmente em mente é que os componentes retêm dados e os sistemas atualizam os componentes.
Portanto, se eu tiver um jogo 2D simples com algumas entidades (por exemplo, jogador, inimigo1, inimigo2) que possuam componentes de transformação, movimento, estado, animação e renderização, acho que devo ter:
- Um MovementSystem que move todos os componentes do Movimento e atualiza os componentes do Estado
- E um RenderSystem que atualiza os componentes de Animação (o componente de animação deve ter uma animação (ou seja, um conjunto de quadros / texturas) para cada estado e atualizá-lo significa selecionar a animação correspondente ao estado atual (por exemplo, salto, deslocamento_querido, etc), e atualização do índice de quadros). Em seguida, o RenderSystem atualiza os componentes Render com a textura correspondente ao quadro atual da Animação de cada entidade e renderiza tudo na tela.
Eu já vi algumas implementações como o framework Artemis, mas não sei como resolver essa situação:
Digamos que meu jogo tenha as seguintes entidades. Cada entidade possui um conjunto de estados e uma animação para cada estado:
- jogador: "inativo", "moving_right", "jumping"
- enemy1: "moving_up", "moving_down"
- enemy2: "moving_left", "moving_right"
Quais são as abordagens mais aceitas para atualizar o estado atual de cada entidade? A única coisa em que consigo pensar é em ter sistemas separados para cada grupo de entidades e componentes separados de Estado e Animação, para que eu tivesse PlayerState, PlayerAnimation, Enemy1State, Enemy1Animation ... PlayerMovementSystem, PlayerRenderingSystem ... mas acho que isso é ruim solução e quebra o propósito de ter um sistema baseado em componentes.
Como você pode ver, estou bastante perdido aqui, por isso gostaria muito de receber qualquer ajuda.
EDIT: Eu acho que a solução para fazer isso funcionar como pretendo é esta:
Você torna o componente statecomponent e animationcomponent genérico o suficiente para ser usado para todas as entidades. Os dados que eles contêm serão o modificador para alterar coisas como quais animações são reproduzidas ou quais estados estão disponíveis. - Byte56
Agora, estou tentando descobrir como projetar esses 2 componentes genéricos o suficiente para que eu possa reutilizá-los. Ter um UID para cada estado (por exemplo, caminhar, correr ...) e armazenar animações em um mapa no AnimationComponent codificado por esse identificador pode ser uma boa solução?
fonte
statecomponent
eanimationcomponent
genérico o suficiente para ser usado para todas as entidades. Os dados que eles contêm serão o modificador para alterar coisas como quais animações são reproduzidas ou quais estados estão disponíveis.Respostas:
IMHO, o
Movement
componente deve conter o estado atual (Movement.state
) e oAnimation
componente deve observar alteraçõesMovement.state
e atualizar sua animação atual (Animation.animation
) de acordo, usando uma simples pesquisa de id de estado na animação (como sugerido no final do OP). Obviamente, isso significaAnimation
que dependeráMovement
.Uma estrutura alternativa seria ter um
State
componente genérico , queAnimation
observe eMovement
modifique, que é basicamente o controlador de exibição de modelo (movimento de animação de estado neste caso).Outra alternativa seria fazer a entidade despachar um evento para seus componentes quando seu estado mudar.
Animation
ouviria esse evento e atualizaria sua animação de acordo. Isso elimina a dependência, embora você possa argumentar que a versão dependente é um design mais transparente.Boa sorte.
fonte
Movement
seria controlarState
(não observar). Último caso: SimMovement
,entity.dispatchEvent(...);
sim, e todos os outros componentes que ouvem esse tipo de evento o receberão. É claro que o desempenho é pior do que as chamadas de método puro, mas não muito. Você pode agrupar objetos de eventos, por exemplo. Aliás, você não precisa usar a entidade como o "nó de eventos", também pode usar um "barramento de eventos", deixando sua classe de entidade completamente fora dela.Sobre o seu problema, se o STATE for usado apenas em Animações, você nem precisará expor isso a outros componentes. Se houver mais de um uso, você precisará expô-lo.
O sistema de Componentes / Subsistema que você descreve parece mais baseado em hierarquia do que em componente. Afinal, o que você descreve como componentes são de fato estruturas de dados. Isso não significa que é um sistema ruim, apenas que eu não acho que se encaixe muito bem na abordagem baseada em componentes.
Como você observou, as dependências são um grande problema em sistemas baseados em componentes. Existem diferentes maneiras de lidar com isso. Alguns exigem que cada componente declare suas dependências e faça uma verificação rigorosa. Outros consultam componentes que implementam uma interface específica. Outros ainda fazem referência aos componentes dependentes quando instanciam cada um deles.
Independentemente do método usado, você precisará de um GameObject de algum tipo para atuar como uma coleção de Componentes. O que o GameObject fornece pode variar muito e você pode simplificar suas dependências entre componentes, empurrando alguns dados usados com frequência para o nível do GameObject. A unidade faz isso com a transformação, por exemplo, força todo objeto do jogo a ter um.
Com relação ao problema que você pede de diferentes estados / animações para diferentes objetos do jogo, aqui está o que eu faria. Primeiro, eu não ficaria muito chique nesse estágio da implementação: apenas implemente o que você precisa agora para fazê-lo funcionar e adicione sinos e assobios conforme necessário.
Então, eu começaria com um componente 'State': PlayerStateComponent, Enemy1State, Enemy2State. O componente state cuidaria de mudar o estado no momento apropriado. Estado é algo que praticamente todos os seus objetos terão, portanto ele pode residir no GameObject.
Então, haveria um AnimationCompoment. Isso teria um dicionário de animações codificado para o estado. Em update (), altere a animação se o estado mudar.
Há um ótimo artigo sobre a construção de estruturas que não consigo encontrar. Ele disse que, quando você não tem experiência no domínio, deve escolher um problema e fazer a implementação mais simples que resolve o problema atual . Em seguida, você adiciona outro problema / caso de uso e expande a estrutura à medida que avança, para que ela cresça organicamente. Eu realmente gosto dessa abordagem, principalmente ao trabalhar com novos conceitos como você está fazendo.
A implementação que propus é bastante ingênua, mas aqui estão algumas melhorias possíveis à medida que você adiciona mais casos de uso:
fonte
Além da resposta do ADB, você pode usar http://en.wikipedia.org/wiki/Dependency_injection , que ajuda quando você precisa construir muitos componentes, passando-os como referências aos seus construtores. Obviamente, eles ainda dependerão um do outro (se isso for necessário em sua base de código), mas você pode colocar toda essa dependência em um local em que as dependências estejam configuradas e o restante do código não precise saber sobre a dependência.
Essa abordagem também funciona bem se você usar interfaces, porque cada classe de componente solicita apenas o que precisa ou onde precisa ser registrada e apenas a estrutura de injeção de dependência (ou o local onde você configura tudo, geralmente o aplicativo) sabe quem precisa do que .
Para sistemas simples, você pode fugir sem usar DI ou código limpo, suas classes RenderingSystem parecem precisar chamá-las estaticamente ou pelo menos tê-las disponíveis em cada componente, o que as torna dependentes umas das outras e difíceis de mudar. Se você estiver interessado em uma abordagem mais limpa, verifique os links do wiki do DI acima e leia sobre o Código Limpo: http://clean-code-developer.com/
fonte