Depois de fazer duas perguntas sobre os sistemas de entidades ( 1 , 2 ) e ler alguns artigos sobre eles, acho que os entendo muito melhor do que antes. Ainda tenho algumas incertezas, principalmente sobre a construção de um emissor de partículas, um sistema de entrada e uma câmera. Obviamente, ainda tenho alguns problemas para entender os sistemas de entidades, e eles podem se aplicar a toda uma gama de objetos, mas escolhi esses três porque são conceitos muito diferentes, devem cobrir uma base bastante ampla e me ajudar a entender os sistemas de entidades e como lidar com problemas como esses, eu mesmo, à medida que eles aparecem.
Estou construindo um mecanismo em JavaScript e implementei a maioria dos recursos principais, que incluem: manipulação de entradas, sistema de animação flexível, emissor de partículas, aulas e funções de matemática, manipulação de cenas, câmera e renderização e um monte de coisas de outras coisas que os motores geralmente suportam. Eu li a resposta do Byte56, que me interessou em transformar o mecanismo em um sistema de entidades. Continuaria sendo um mecanismo de jogo HTML5, com a filosofia básica da cena, mas deve oferecer suporte à criação dinâmica de entidades a partir de componentes.
O problema que tenho agora é ajustar meu antigo conceito de mecanismo neste novo paradigma de programação. Estas são algumas das definições das perguntas anteriores, atualizadas:
Uma entidade é um identificador. Ele não possui dados, não é um objeto, é um ID simples que representa um índice na lista de cenas de todas as entidades (que eu realmente planejo implementar como uma matriz de componentes).
Um componente é um detentor de dados, mas com métodos que podem operar com esses dados. O melhor exemplo é um
Vector2D
, ou um componente "Posição". Tem de dados:x
ey
, mas também alguns métodos que tornam operacionais nos dados um pouco mais fácil:add()
,normalize()
e assim por diante.Um sistema é algo que pode operar em um conjunto de entidades que atendem a determinados requisitos; geralmente as entidades precisam ter um conjunto especificado de componentes para serem operadas. O sistema é a parte "lógica", a parte "algoritmo", toda a funcionalidade fornecida pelos componentes é puramente para facilitar o gerenciamento de dados.
Câmera
A câmera possui uma Vector2D
propriedade de posição, uma propriedade de rotação e alguns métodos para centralizá-la em torno de um ponto. Cada quadro é alimentado para um renderizador, juntamente com uma cena, e todos os objetos são traduzidos de acordo com sua posição. A cena é então renderizada.
Como eu poderia representar esse tipo de objeto em um sistema de entidades? A câmera seria uma entidade, um componente ou combinação (conforme minha resposta )?
Emissor de partículas
O problema que tenho com o meu emissor de partículas é, novamente, o que deveria ser o quê. Tenho certeza de que as próprias partículas não devem ser entidades, pois quero apoiar mais de 10.000 delas e acredito que criar tantas entidades seria um duro golpe no meu desempenho.
Como eu poderia representar esse tipo de objeto em um sistema de entidades?
Gerenciador de Entrada
O último sobre o qual quero falar é como a entrada deve ser tratada. Na minha versão atual do mecanismo, há uma classe chamada Input
. É um manipulador que assina os eventos do navegador, como pressionamentos de tecla e alterações na posição do mouse, além de manter um estado interno. Em seguida, a classe player possui um react()
método que aceita um objeto de entrada como argumento. A vantagem disso é que o objeto de entrada pode ser serializado em .JSON e depois compartilhado na rede, permitindo simulações suaves para vários jogadores.
Como isso se traduz em um sistema de entidades?
Aqui está como eu me aproximei disso:
Câmera
Minha câmera é uma entidade como qualquer outra, que anexou componentes:
Transform
temTranslation
,Rotation
eScale
propriedades, em adição a outros para a velocidade, etc.Pov
(Ponto de vista) temFieldOfView
,AspectRatio
,Near
,Far
, e qualquer outra coisa necessária para produzir uma matriz de projecção, para além de umaIsOrtho
bandeira utilizado para alternar entre perspectiva e projecções ortogonais.Pov
também fornece umaProjectionMatrix
propriedade de carregamento lento usada pelo sistema de renderização que é calculada internamente na leitura e armazenada em cache até que qualquer outra propriedade seja modificada.Não existe um sistema de câmera dedicado. O Sistema de renderização mantém uma lista de
Pov
's' e contém lógica para determinar qual deles usar ao renderizar.Entrada
Um
InputReceiver
componente pode ser anexado a qualquer entidade. Isso possui um manipulador de eventos anexado (ou lambda, se o seu idioma suportar) que é usado para armazenar o processamento de entrada específico da entidade, que aceita parâmetros para o estado atual e anterior da chave, a localização atual e anterior do mouse e o estado do botão etc. (Na verdade, existem manipuladores separados para mouse e teclado).Por exemplo, em um jogo de teste semelhante ao Asteroids que criei ao me acostumar com a Entidade / Componente, tenho dois métodos lambda de entrada. Um deles lida com a navegação do navio processando as teclas de seta e a barra de espaço (para disparar). O outro lida com a entrada geral do teclado - teclas para saída, pausa, etc, nível de reinicialização, etc. Crio dois componentes, anexo cada lambda ao seu próprio componente, depois atribuo o componente receptor de navegação à entidade embarcada, e o outro a um entidade do processador de comandos não visível.
Aqui está o manipulador de eventos para manipular as chaves mantidas entre os quadros anexados ao
InputReceiver
componente do navio (C #):Se sua câmera é móvel, dar-lhe o seu próprio
InputReceiver
eTransform
componente, anexar um lambda ou manipulador que implementa qualquer tipo de controle que você quer, e está feito.Isso é bem legal: você pode mover o
InputReceiver
componente com o manipulador de navegação conectado do navio para um asteróide, ou qualquer outra coisa, e voar por aí. Ou, atribuindo umPov
componente a qualquer outra coisa em sua cena - um asteróide, poste de luz etc. - você pode visualizar sua cena da perspectiva dessa entidade.Uma
InputSystem
classe que mantém um estado interno para o teclado, mouse etc.InputSystem
filtra sua coleção interna de entidades para entidades que possuem umInputReceiver
componente. Em seuUpdate()
método, itera através dessa coleção e chama os manipuladores de entrada anexados a cada um desses componentes da mesma maneira que o sistema de renderização desenha cada entidade com umRenderable
componente.Partículas
Isso realmente depende de como você planeja interagir com as partículas. Se você só precisa de um sistema de partículas que se comporte como um objeto - digamos, um fogo de artifício mostre que o jogador não pode tocar ou bater - então eu criaria uma única entidade e um
ParticleRenderGroup
componente que contém todas as informações necessárias para as partículas - deterioração etc. - que não é coberto pelo seuRenderable
componente. Ao renderizar, o sistema de renderização veria se uma entidade tem oRenderParticleGroup
anexo e o trataria de acordo.Se você precisar que partículas individuais participem da detecção de colisão, responda à entrada, etc., mas queira apenas renderizá-las como um lote, eu criaria um
Particle
componente que contenha essas informações por partícula e as criará como entidades separadas. O sistema de renderização ainda pode agrupá-los em lote, mas eles serão tratados como objetos separados pelos outros sistemas. (Isso funciona muito bem com instanciamento.)Então, seja no seu
MotionSystem
(ou qualquer que seja o seu uso que lide com a atualização da posição da entidade etc.) ou em dedicadoParticleSystem
, execute o processamento necessário para cada partícula por quadro. ARenderSystem
seria responsável pela construção / lotes e cache de coleções de partículas como eles são criados e destruídos, e torná-los conforme necessário.Uma coisa legal dessa abordagem é que você não precisa ter casos especiais para colisão, descarte etc. de partículas; eles codificam você escreve para qualquer outro tipo de entidade ainda pode ser usado.
Conclusão
Se você está pensando em usar várias plataformas - não é super aplicável ao JavaScript - todo o código específico da plataforma (ou seja, renderização e entrada) é isolado em dois sistemas. Sua lógica de jogo permanece em classes agnósticas de plataforma (movimento, colisão etc.), portanto você não precisa tocá-las ao portar.
Eu entendo e concordo com a posição de Sean de que as coisas de calçar sapatos em um padrão a fim de aderir estritamente ao padrão, em vez de ajustá-lo para atender às necessidades do seu aplicativo, são ruins. Eu simplesmente não vejo nada na entrada, câmera ou partículas que exija esse tipo de tratamento.
fonte
A lógica de entrada e jogo provavelmente será tratada em um pedaço de código dedicado fora do sistema de componentes da entidade. É tecnicamente possível inseri-lo no design, mas há pouco benefício - a lógica do jogo e a interface do usuário são hacky e cheias de abstrações com vazamentos, não importa o que você faça, e tentar forçar a estaca quadrada a um buraco redondo apenas por pureza arquitetônica é um desperdício de tempo.
Da mesma forma, os emissores de partículas são bestas especiais, especialmente se você se importa com o desempenho. Um componente emissor faz sentido, mas os gráficos farão alguma mágica especial com esses componentes, misturados à mágica pelo restante da renderização.
Em relação à sua câmera, dê um sinalizador ativo e talvez um índice de "profundidade" às câmeras e deixe o sistema gráfico renderizar todos os que estão ativados. Isso é realmente útil para muitos truques, incluindo GUIs (quer que sua GUI seja renderizada em um modo ortográfico no topo do mundo do jogo? Não há problema, são apenas duas câmeras com máscaras de objetos diferentes e GUI configuradas para uma camada superior). Também é útil para camadas de efeitos especiais e afins.
fonte
Não tenho certeza do que esta pergunta realmente está perguntando. Dado que as únicas coisas que você tem no jogo são entidades, as câmeras precisam ser entidades. A funcionalidade da câmera é implementada através de algum tipo de componente da câmera. Não tenha componentes "Posição" e "Rotação" separados - isso é um nível muito baixo. Eles devem ser combinados em algum tipo de componente WorldPosition que se aplicaria a qualquer entidade localizada no mundo. Quanto a qual usar ... você precisa inserir a lógica no sistema de alguma forma. Você o codifica no sistema de manuseio da câmera ou anexa scripts ou algo assim. Você pode ter um sinalizador ativado / desativado em um componente da câmera, se isso ajudar.
Eu também. Um emissor de partículas seria uma entidade e o sistema de partículas rastrearia as partículas associadas a uma determinada entidade. Coisas assim são onde você percebe que "tudo é uma entidade" é absurdamente impraticável. Na prática, as únicas coisas que são entidades são objetos relativamente complexos que se beneficiam das combinações de componentes.
Quanto à entrada: a entrada não existe no mundo do jogo, portanto é tratada por um sistema. Não é necessariamente um 'sistema de componentes' porque nem tudo no seu jogo gira em torno dos componentes. Mas haverá um sistema de entrada. Você pode marcar a entidade que responde à entrada com algum tipo de componente Player, mas a entrada será complexa e completamente específica do jogo, então não faz sentido tentar criar componentes para isso.
fonte
Aqui estão algumas das minhas idéias para resolver esses problemas. Eles provavelmente terão algo errado com eles, e provavelmente haverá uma abordagem melhor, então, por favor, indique-me aos que responderem!
Câmera :
Existe um componente "Câmera", que pode ser adicionado a qualquer entidade. No entanto, não consigo entender quais dados devo colocar nesse componente: eu poderia ter componentes "Posição" e "Rotação" separados! O
follow
método não precisa ser implementado, porque já está seguindo a entidade à qual está anexado! E eu sou livre para mudar isso. O problema com este sistema seria muitos objetos de câmera diferentes: comoRendererSystem
saber quais usar? E também, eu costumava passar o objeto da câmera, mas agora parece queRendererSystem
ele precisará percorrer duas vezes todas as entidades: primeiro para encontrar as que agem como câmeras e, em segundo, para renderizar tudo.ParticleEmitter :
Haveria um
ParticleSystem
que atualizaria todas as entidades que tinham um componente "Emissor". Partículas são objetos burros em um espaço de coordenadas relativo, dentro desse componente. Há um problema de renderização aqui: eu precisaria criar umParticleRenderer
sistema ou estender a funcionalidade do existente.Sistema de entrada :
A principal preocupação para mim aqui era a lógica ou o
react()
método. A única solução que encontrei é um sistema separado para isso, e um componente para cada sistema, que indicaria qual deles usar. Isso realmente parece muito hacky, e eu não sei como lidar bem com isso. Uma coisa é que, enquanto estou preocupado, o programaInput
pode permanecer implementado como uma classe, mas não vejo como poderia integrá-lo ao resto do jogo.fonte