Estou começando a entender o design baseado em componentes. Não sei qual é a maneira "certa" de fazer isso.
Aqui está o cenário. O jogador pode equipar um escudo. O escudo é desenhado como uma bolha ao redor do jogador, tem uma forma de colisão separada e reduz o dano que o jogador recebe dos efeitos da área.
Como esse escudo é arquitetado em um jogo baseado em componentes?
O que me deixa confuso é que o escudo obviamente tem três componentes associados a ele.
- Redução / filtragem de danos
- Um sprite
- Um colisor.
Para piorar, diferentes variações de blindagem podem ter ainda mais comportamentos, todos componentes:
- melhorar a saúde máxima do jogador
- recuperação de Saúde
- desvio de projétil
- etc
Estou pensando demais nisso? O escudo deveria ser apenas um super componente?
Eu realmente acho que essa é uma resposta errada. Então, se você acha que esse é o caminho a seguir, explique.O escudo deve ser sua própria entidade que rastreia a localização do jogador?
Isso pode dificultar a implementação da filtragem de danos. Também meio que desfoca as linhas entre componentes e entidades anexados.O escudo deve ser um componente que abriga outros componentes?
Eu nunca vi ou ouvi falar de algo assim, mas talvez seja comum e ainda não sou profundo o suficiente.O escudo deve ser apenas um conjunto de componentes que são adicionados ao player?
Possivelmente com um componente extra para gerenciar os outros, por exemplo, para que todos possam ser removidos como um grupo. (deixe acidentalmente para trás o componente de redução de danos, agora isso seria divertido).Outra coisa óbvia para alguém com mais experiência em componentes?
fonte
Respostas:
Edit: Eu acho que não há "comportamento autônomo" suficiente para uma entidade separada. Nesse caso específico, um escudo segue o alvo, trabalha para o alvo e não sobrevive ao alvo. Embora eu concorde que não há nada errado com o conceito de "objeto de escudo", neste caso, estamos lidando com comportamento, que se encaixa perfeitamente em um componente. Mas também sou um defensor de entidades puramente lógicas (em oposição a sistemas de entidades completos nos quais você pode encontrar componentes de transformação e renderização).
Veja isso em uma perspectiva diferente; adicionar um componente também adiciona outros componentes e, após a remoção, os componentes adicionais também desaparecem.
Essa poderia ser uma solução, promoveria a reutilização, mas também é mais suscetível a erros (para o problema que você mencionou, por exemplo). Não é necessariamente ruim. Você pode descobrir novas combinações de feitiços com tentativa e erro :)
Eu vou elaborar um pouco.
Acredito que você tenha notado como alguns componentes devem ter prioridade, independentemente de serem adicionados a uma entidade (isso também responderia a sua outra pergunta).
Também vou assumir que estamos usando a comunicação baseada em mensagens (por uma questão de discussão, é apenas uma abstração sobre uma chamada de método no momento).
Sempre que um componente de blindagem é "instalado", os manipuladores de mensagens dos componentes de blindagem são encadeados com uma ordem específica (superior).
O componente "stats" instala um manipulador de mensagens "dano" no índice In / Invariant / Normal. Sempre que uma mensagem de "dano" for recebida, diminua o HP pelo valor "valor".
Comportamento bastante padrão (coloque alguma resistência a danos naturais e / ou características raciais, qualquer que seja).
O componente blindado instala um manipulador de mensagens "danificado" no índice In / Pre / High.
Você pode ver que isso é bastante flexível, apesar de exigir um planejamento cuidadoso ao projetar a interação do componente, pois você precisará determinar em que parte dos manipuladores de eventos de componentes do pipeline de manipulação de mensagens estão instalados.
Faz sentido? Deixe-me saber se posso adicionar mais detalhes.
Editar: sobre instâncias de múltiplos componentes (dois componentes de armadura). Você pode acompanhar a contagem total de instâncias em apenas uma instância da entidade (no entanto, isso mata o estado por componente) e continuar adicionando manipuladores de eventos de mensagem ou garantir que seus contêineres de componentes permitam a duplicação de tipos de componentes com antecedência.
fonte
Talvez, depende de quão reutilizável você deseja que seu código seja e se faz sentido.
A menos que esse escudo seja algum tipo de criatura que possa andar independentemente em algum momento.
Isso parece muito com entidade, então a resposta é não.
É provável.
fonte
Um escudo, como uma entidade física , não é diferente de qualquer outra entidade física , por exemplo, um zangão que circula ao seu redor (e que, de fato, poderia ser um tipo de escudo!). Portanto, faça do shield uma entidade lógica separada (permitindo que ele mantenha seus próprios componentes).
Dê ao seu escudo alguns componentes: um componente Físico / Espacial para representar sua forma de colisão, e um componente DamageAffector que mantém uma referência a alguma entidade à qual ele aplicará dano aumentado ou reduzido (por exemplo, seu personagem do jogador) toda vez que a entidade segurar o DamageAffector sofre danos. Assim, seu jogador está recebendo dano "por procuração".
Defina a posição da entidade de escudo para a posição do jogador a cada tick. (Escreva uma classe de componente reutilizável que faça isso: escreva uma vez, use várias vezes.)
Você precisará criar a entidade de escudo, por exemplo. em coletar um powerup. Eu uso um conceito genérico chamado Emissor, que é um tipo de componente de entidade que gera novas entidades (geralmente através do uso de uma EntityFactory à qual ele se refere). Onde você decide localizar o emissor é com você - por exemplo. coloque-o em uma energização e faça-o disparar quando a energização for coletada.
Existe uma linha tênue entre subcomponentes lógicos (espacial, AI, slots de armas, processamento de entrada etc. etc.) e subcomponentes físicos. Você precisa decidir de que lado você está, pois isso define fortemente que tipo de sistema de entidades você possui. Para mim, o subcomponente Física da minha Entidade lida com os relacionamentos hierárquicos da física (como membros em um corpo - pense nos nós dos cenários), enquanto os controladores lógicos mencionados acima são tipicamente o que são representados pelos componentes da sua entidade - e não pelos que representam "equipamentos" físicos individuais.
fonte
Talvez não abrigue outros componentes, mas controla a vida útil dos subcomponentes. Portanto, em algum pseudo-código aproximado, o código do seu cliente adicionaria esse componente "shield".
fonte
this
significa em sua resposta. Estáthis
se referindo ao componente Shield ou você quis dizer a entidade que está usando o shield, seu pai? A confusão pode ser minha culpa. "Baseado em componentes" é meio vago. Na minha versão de entidades baseadas em componentes, uma entidade é simplesmente um contêiner de componentes com alguma funcionalidade mínima própria (nome do objeto, tags, mensagens, etc.).gameObject
ou algo assim. É uma referência ao objeto / entidade do jogo atual / qualquer que seja o proprietário dos componentes.Se o seu sistema de componentes permitir scripts, o componente de blindagem poderá ser quase um supercomponente que apenas chama um script para seu parâmetro "effect". Dessa forma, você mantém a simplicidade de um único componente para blindagens e transfere toda a lógica do que ele realmente faz para arquivos de script personalizados que são alimentados com blindagens pelas definições de sua entidade.
Eu faço algo semelhante para o meu componente Moveable, ele contém um campo que é do script de reação-chave (uma subclasse de script no meu mecanismo). Esse script define um método que segue minha mensagem de entrada. como tal, eu posso simplesmente fazer algo assim no meu arquivo de definição de tempalte
então no meu componente móvel durante o registro de mensagens, registro os scripts Do método (código em C #)
é claro que isso depende do meu método Do, seguindo o padrão de funções que meu RegisterHandler executa. Nesse caso, seu (remetente IComponent, argumento do tipo ref)
então meu "script" (no meu caso também C # apenas runime compilado) define
e minha classe base KeyReactionScript
depois, quando um componente de entrada enviar uma mensagem do tipo MessageTypes.InputUpdate com o tipo como tal
O método no script que foi vinculado a essa mensagem e tipo de dados será acionado e manipulará toda a lógica.
O código é bastante específico para o meu mecanismo, mas a lógica deve ser funcional em qualquer caso. Eu faço isso para muitos tipos, a fim de manter a estrutura do componente simples e flexível.
fonte