Sistema de componentes de entidade - progressão do jogo


8

Eu sou bastante novo no desenvolvimento de jogos (mas não na programação) e estou tentando descobrir qual seria a melhor maneira de lidar com a comunicação entre os mundos. O que eu quero dizer é o seguinte:

Eu tenho lido sobre sistemas de componentes de entidades (ECS) e como as pessoas sugerem o uso de mundos / espaços diferentes ( http://gamedevelopment.tutsplus.com/tutorials/spaces-useful-game-object-containers--gamedev-14091 ) para uma subseção de um jogo. Por exemplo, um HUD, inventário ou combate / movimento têm um mundo / espaço separado (porque eles têm gráficos diferentes e lógica subjacente).

No entanto, eu queria saber como o inventário, ou o HUD, sabe sobre a saúde de um jogador quando a saúde é tratada por um espaço / mundo diferente, por exemplo, quando em combate?

Isso também se aplica à progressão do jogo em geral, por exemplo, diálogo com o NPC (um diálogo seria um espaço separado, pois é uma tela pop-up), mas como você transmitiria as escolhas feitas (ou no estado) do diálogo para outros espaços / mundos . Ou basicamente qualquer outro tipo de evento que influencie a progressão do jogo em diferentes espaços / mundos (saúde, mana, missões, diálogo, combate, inventário, hud, etc.)

Como alguém lidaria com esse tipo de design? Precisa de um objeto singleton (em implementação) que contenha todo esse tipo de informação? Isso seria estranho, porque, então, a componentsnecessidade de transmitir cada alteração a esse objeto singleton, que parece fazer as coisas duas vezes (indo contra o principal DRY da programação) ...

Estou meio que perdida aqui em termos de design, alguma dica?


---EDITAR---

Então, eu li alguns outros posts sugeridos por comentários e tive uma idéia geral sobre as possibilidades, no entanto, cada um deles parece ter uma grande desvantagem que os torna simplesmente não corretos. É muito possível que eu esteja supervisionando os detalhes que resolveriam essas desvantagens, então fique à vontade para me corrigir. Vou tentar dar uma visão geral, bem como algumas respostas para algumas perguntas.

Estou vendo três opções principais para 'compartilhar' dados entre espaços. Embora a maioria das postagens seja sobre o compartilhamento de dados entre sistemas, sinto que o mesmo pode ser aplicado ao compartilhamento de dados entre sistemas.

1. Consulta

Exemplo : Se o mundo do HUD precisar conhecer a saúde atual do jogador, ele poderá consultar outro mundo e solicitar a saúde atual.

Desvantagem : os mundos precisam saber um do outro, o que é um grande problema de dependência e vai contra a dissociação.

2: Mensagens diretas (sincronização e assíncrona)

Exemplo : se durante o combate a saúde de um jogador mudar, ele poderá enviar mensagens (sincronizadas e assíncronas, o que for necessário) para outros mundos que precisem saber sobre essa alteração.

Desvantagem : Ainda é a questão da dissociação: os mundos precisam saber um do outro.

3: Mensagens indiretas (sincronização e assíncrona) <- melhor opção

Exemplo : se durante o combate a saúde de um jogador mudar, ele poderá enviar mensagens (sincronização e assíncrona, o que for necessário) para o hub geral de mensagens. Outros mundos / sistemas que precisam saber sobre essa alteração são inscritos no canal de mensagens específico e lêem as mensagens.

De cabeça para baixo : completamente desacoplado, facilmente gerenciável e extensível.

Desvantagem / incerto : quando o canal de mensagens sabe que as mensagens precisam ser excluídas? Ou talvez o sistema que está inscrito marque (apenas para si) a mensagem como lida e aguarde novas mensagens -> messagebox se torne enorme depois de um tempo. Como os mundos / sistemas lidam com a ordem? Por exemplo, durante um quadro: se o HUD já pesquisou a mensagem de integridade e depois disso a saúde muda, o próximo quadro é atualizado. Para alguns aplicativos, esse pode não ser o caminho certo.

P: Um único objeto de jogo pode existir em vários espaços

Estou usando a estrutura Artemis ECS, que vem com espaços internos (chamados mundos). Cada entidade (e com ela, os dados na forma de componentes) é criada em um mundo e, portanto, não pode ser compartilhada entre mundos.


As mensagens são a abordagem padrão aqui: gamedev.stackexchange.com/questions/23834/…
MichaelHouse

Pelo que posso ler no artigo vinculado, um único objeto de jogo pode existir em vários espaços. Se você tiver gráficos ou lógica diferentes entre os espaços, separe os dados dos gráficos e da lógica. Compartilhe o objeto de jogo de dados entre espaços e agregue-o com diferentes gráficos e objetos de jogo lógico.
Andreas

Esta resposta que dei sobre mensagens sistemas podem ajudá-lo bem: gamedev.stackexchange.com/questions/7718/...
James

Eu implementei todas as três soluções (consultas, diretas e indiretas) na minha vida de jogador. E posso dizer que a terceira opção funciona melhor para mim. Você pode facilmente desacoplar sistemas e executar a lógica deles em paralelo. A única desvantagem é que você precisa realizar 9 chamadas de função para rotear cada mensagem / evento de um sistema para outro. Obviamente, você pode otimizá-lo e a grande vantagem é que você não precisa de mutexes ou singletons nessa abordagem.
Gregory

@ Gregory Obrigado pela sua imputação, esperava que as mensagens indiretas fossem a melhor opção. Eu não estava ciente das 9 chamadas de função, mas ao planejar este hub de mensagens, percebi que, de fato, seriam algumas chamadas. Você já encontrou uma boa solução / alternativa para a exclusão de mensagens quando nenhum sistema mais precisa delas?
Tim

Respostas:


1

Uma maneira de ver isso é que você possivelmente está colocando muito em seus objetos de jogo.

Não há razão para que o código que conecte o HUD ao seu jogo no mundo precise estar em um componente / sistema que mora em algum espaço específico. Talvez seja melhor viver com esse código em um gerente central ou script global que tenha acesso a todos os espaços e objetos e, em seguida, interagir com o código que sabe quando realmente criar um espaço e o que colocar neles (por exemplo, o código que gera o jogador, salva seu estado entre níveis, etc.).

Você também pode ter apenas um "espaço mestre" que contém objetos de jogo com lógica ou dados que precisam persistir ou manipular os espaços usados ​​para níveis e interface do usuário. Essa abordagem é comum em mecanismos que forçam os desenvolvedores a colocar todos os scripts / lógicas em componentes / objetos (por exemplo, no Unity, você cria um objeto Main global e o configura para persistir durante o descarregamento da cena; se o Unity realmente tiver espaços, você ' d use aqueles em vez da bandeira).

Lembre-se de abusar do seu ECS é o mesmo que abusar dos padrões de design; Só porque você tem uma ferramenta nova e bacana, não significa que você deve usá-la para resolver todos os problemas que encontrar. Avalie o espaço do seu problema e selecione a solução mais adequada, mesmo que seja a coisa mais antiga que seus ancestrais usavam na idade das trevas dos anos 90. : p


0

Eu criei alguns protótipos, mas nada muito grande, e a maneira como lidava com vários espaços era simplesmente criar um objeto de jogo que contenha mundo, jogador, etc. , no objeto do jogo

Sempre que chamado, ele obtém a saúde do jogador. Dessa forma, eu poderia enviá-lo ao HUD e exibir a barra de integridade.

Não é o mais limpo, mas faz o trabalho, fiz alguns testes de desempenho em 2013 e tudo parecia funcionar sem problemas. para evitar essas dependências, você sempre pode soltar a barra de saúde quando a saúde do jogador for nula.

Normalmente, quando o jogador não existe, significa que o usuário está em um menu ou em uma cena.

Exemplo de código:

public float PlayerHealth {
  get {
    if (player !+ null) 
      return player.Health;
    return -1;
  }
}

Espero que isto seja o que você estava procurando.


0

isso é algo em que estou trabalhando nas últimas semanas. Estou trabalhando em minha própria biblioteca de ECS (queria fazer isso por experiência e apenas para experimentá-lo, porque queria fazê-lo por algum tempo).

Este é o link do github: https://github.com/gioragutt/xna-ecs

Para o seu problema, eu sempre escrevi uma pequena biblioteca pubsub, que você pode ver aqui

Basicamente, eu tenho uma EmsClientclasse, da qual as coisas podem derivar. Atualmente, meus componentes não fazem isso, mas as classes de nível mais alto, embora não haja motivo para não fazê-lo. Eu subscrevo a Nomes de mensagens, e proporcionar um retorno de chamada com a seguinte assinatura: Action<JObject>. Como você já entendeu, estou usando o Json Objects como meio para transferir mensagens. Fiz isso depois de usar apenas byte[]as anteriores e descobri que precisava de algo mais geral, e como estou acostumado a algo assim no meu local de trabalho (temos um IPCD que funciona da mesma forma, exceto o retorno de chamada) O método é sempre o mesmo, pois geralmente separamos a responsabilidade de diferentes manipuladores).

Há um EmsServer(um no servidor e um em cada cliente) responsável por mover as mensagens entre EmsCliento seu domínio ( EmsServerno lado do servidor move as mensagens entre EmsClientso lado do servidor, vice-versa para o lado do cliente).

Para mensagens entre o Cliente e o Servidor, criei um EmsServerEndpointque é EmsClientele mesmo, ele apenas executa a lógica de armazenar em buffer as mensagens enviadas em seu domínio e liberá-las para outros domínios (por exemplo, o cliente envia a mensagem para o servidor, enquanto quando o servidor transfere cada mensagem para todos os clientes conectados.

Você pode ver o uso em muitos lugares, fe: ClientGameManager, ServerGameManager.

Considerando que, por exemplo, se eu quiser adicionar um componente GUI para um jogador, você pode consultar AQUI , os métodos BeginAllocateLocale BeginAllocateRemote, que são responsáveis ​​pela criação GameObjectsdos jogadores. Cada GameObjectum contém um Entity(da lib ECS) e um IComponentContainer(que também é, da lib ECS). Cada GameObjectum recebe automaticamente uma transformação (como em Unity, da qual me inspirei).

Meu código praticamente fala por si, e se você conseguir, estou procurando críticas, então gostaria de receber críticas construtivas :)

Espero que meu código o ajude com algumas idéias!


0

Considere os componentes do padrão observador / assunto para os componentes do jogo e da interface do usuário. Você os une na criação / carregamento e depois os esquece. Se a saúde do personagem mudar, ele notifica todos os observadores, que podem fazer o que quiserem com as informações.

Ao utilizar nosso site, você reconhece que leu e compreendeu nossa Política de Cookies e nossa Política de Privacidade.
Licensed under cc by-sa 3.0 with attribution required.