Maneira elegante de simular grandes quantidades de entidades em um mundo de jogo

33

Suponha que você tenha um jogo no qual existem muitas (muitas) entidades que cumprem algumas funções, nem todas são constantemente necessárias ou precisam ser consideradas em todos os quadros. O problema concreto em que estou trabalhando, no qual esse problema está presente, é uma simulação detalhada de um corpo, incluindo seus órgãos.

No jogo, cada criatura tem seu próprio corpo, que é separado em partes menores (tronco, pernas, etc.) e, às vezes, essas partes contêm órgãos, que desempenham uma função específica dentro do corpo. Se um órgão atualmente serve ou não a um objetivo ou está ativo nunca é realmente claro. Afinal, um animal pode estar com o estômago vazio e, portanto, não precisa digerir nada. Seria bastante ridículo verificar ou simular cada objeto em cada quadro e muito caro assim que você tiver muitas criaturas no mundo. Então, eu estava pensando em uma maneira inteligente de diferenciar entre objetos que precisam ser atualizados e aqueles que não precisam.

O que eu criei parece uma solução pelo menos aceitável. Ele cria uma fila / pilha simples (essencial é que cada elemento seja removido assim que for lido; a ordem é irrelevante) chamada de "pilha de atenção" onde residem os objetos que precisam ser simulados. Objetos que precisam de atenção simplesmente se colocam na pilha ou são colocados lá por outros objetos. Esses objetos provavelmente implementariam uma interface simples com uma função simulate ().

Aplicado ao meu exemplo de digestão anterior, isso significaria:

O jogador escolhe algo para comer (suponha que seja pão) no inventário e o coloca na boca de seu personagem, e a boca é colocada na pilha de atenção. No próximo quadro, a boca é retirada da pilha e sua função simular () - é chamada. Como é uma boca, seria razoável simular a mastigação aqui. Isso pode continuar por alguns quadros em que a boca continua se colocando na pilha até decidir que o alimento está pronto para ser engolido. Nesse caso, a boca coloca o pão mastigado no estômago (eu sei que ele não vai diretamente para lá, mas o esôfago é deixado de fora para simplificação), que também é colocado na pilha de atenção. No próximo quadro, a simulação do processo de digestão é iniciada. E assim por diante para o restante dos órgãos necessários.

Um problema previsível com isso são os objetos inativos. Um animal adormecido é um bom exemplo disso. Isso pode ser feito conforme descrito anteriormente, mantendo o animal adormecido na pilha e verificando cada vez que ele precisa acordar, mas isso parece um desperdício, pois é a única coisa a ser feita. Para tornar os objetos ociosos mais eficientes, eu planejava adicionar um tipo de agendamento que armazena os trabalhos a serem executados em um horário específico. Se um animal for dormir, colocará um trabalho nesse horário que seria agendado por um certo período de tempo após o animal dormir. Esse trabalho cuidaria de colocar o animal adormecido na pilha de atenção novamente. Agora, você pode dizer que um animal adormecido que não está na pilha de atenção pode deixar de ser atacado por algo porque sua IA não é simulada,

Agora, sinceramente, não sei se isso é mesmo uma solução elegante para esse problema devido à falta de experiência. Estou perto de algo utilizável? Como isso geralmente é feito ou alguém tem sugestões ou soluções melhores?

Marc Müller
fonte

Respostas:

10

Foi exatamente assim que resolvemos esse problema em Stendhal . No nosso caso, muitas coisas acontecem periodicamente, mas não a cada turno: feitiços de cura, plantas crescendo um pouco mais, degeneração de cadáveres, itens no chão expirando.

Temos um número de turno que aumenta a cada turno. E mantemos um mapa de números de turnos futuros apontando para um conjunto de objetos que precisam ser notificados nesse turno.

Hendrik Brummermann
fonte
e se algo mais interage com o objeto nesse meio tempo? Por exemplo, o animal adormecido pode ser acordado por uma pedra batendo. Você deve remover a programação do animal nesse caso?
Emiliano
Isso depende da situação: se a ação é inofensiva (como acordar enquanto já estamos acordados), apenas deixamos acontecer. Mas se a ação consumir algum recurso, procuramos na fila pendente e a removemos.
Hendrik Brummermann 14/10/10
14

Parece um problema semelhante ao das entradas: você tem mais de 100 teclas no teclado, mas não deseja verificar cada tecla individual em cada quadro; então, o que você faz?

Duas respostas: sondagem ou mensagens do sistema.

Polling = em qualquer momento em que isso realmente importe no jogo, consulte o estado das teclas do teclado (ou dos objetos, no seu caso). O resto do tempo, ignore-os.

Mensagens = faça com que cada tecla do teclado (objeto) coloque algo em uma fila de mensagens quando pressionada ou liberada (quando precisar de atenção). Em cada iteração do loop do jogo, ele examina a fila e resolve todas as mensagens antes de continuar.

Ian Schreiber
fonte
10

Então, vou dar um passo atrás na implementação e revisar a questão da perspectiva do design. Você tem um plano sólido para exibir todos os detalhes que deseja incluir nesta simulação?

Por exemplo:

  • O jogador pode dizer a diferença entre um animal com o estômago vazio e um animal com o estômago cheio?
  • Essas informações são importantes para eles de alguma forma, quando estão fazendo escolhas de interação dentro do seu jogo?
  • Um jogador assistindo o animal poderia prever consistentemente o resultado de seus eventos simulados ou um número aleatório seria suficiente?

Basicamente, a regra geral é não tornar sua simulação mais complicada do que suas saídas. No final do dia, se as únicas animações que você tem para ovelhas são pastar, dormir, fugir. Então, realmente não importa quantos fatores entram na decisão sobre qual estado escolher. Todos os jogadores vão ver ovelhas que dormem à noite, fogem do perigo e comem durante o dia.

A simulação de comportamento é muito divertida de se trabalhar, mas mantenha sempre em mente a experiência do usuário final.

wkerslake
fonte
Sim, o jogador será capaz de dizer a diferença e há alguns efeitos que isso tem na jogabilidade, mas é mais um truque para se divertir no momento. Mais ou menos como o sistema de feridas em Dwarf Fortress, que também é muito detalhado, mas insignificante. Mas como você já mencionou, é muito divertido trabalhar e esse é o meu foco principal agora.
Marc Müller
9

Eu tive um problema semelhante em um jogo em que trabalhei alguns anos atrás - a simulação de objetos era complexa e não podia realmente ser realizada em detalhes em todos os objetos do mundo.

A solução foi usar o conceito LOD para a simulação. Objetos dentro da visão do jogador executariam a simulação completa. Objetos distantes do player executavam uma simulação altamente simplificada periodicamente. À medida que os objetos apareciam, os jogadores passavam do curso, atualização periódica da simulação para atualizações detalhadas e regulares.

Skizz
fonte
2

Solução com um cronograma é bom. Observe que toda entidade deve ter uma lista de indicadores para suas ações futuras, o que oferece a possibilidade de invalidar ações futuras, se necessário. Ou seja. o animal adormecido acorda instantaneamente quando atacado, então você deve invalidar sua ação de despertar no futuro.

user2047
fonte
1

Existe um padrão de design para isso. Eu acho que é chamado de objetos de banco de dados?

Basicamente, você mantém uma ovelha "modelo" que pode representar todas as ovelhas não especiais do mundo do jogo, desenha-as da mesma maneira, mantendo os dados exclusivos dentro do objeto modelo, digamos, como uma tabela de locais e / ou hora das ovelhas desde o cisalhamento. Então, sempre que precisar tornar uma ovelha única, é possível criar uma instância específica para rastrear essa ovelha única.

O mesmo vale para animações. Se for uma animação inativa ou evento comum a todas as instâncias, poderá residir na instância do modelo, onde animações mais específicas podem ser agendadas separadamente.

Há muito tempo, escrevi um jogo para um concurso de programação cujo loop principal chamava de animate () em toda a cena. Utilizou ponteiros de função para substituir animações inativas por outras, conforme necessário, e usou a técnica para oferecer suporte a animações herdadas (por exemplo, girar um personagem que está em um disco giratório).

É de natureza semelhante ao uso de um delegado para animação.

davenpcj
fonte
3
Padrão Flyweight, você quer dizer?
Topright
0

Uma máquina de estado funcionaria? O animal está, por exemplo, no estado de sono, estado de alimentação, estado de corrida, etc. Para cada estado, você associa uma lista de órgãos ativos. Assim, cada quadro que você visita cada animal, ativa o estado, lista de órgãos para esse estado e executa a atualização em cada um dos órgãos.

Erik Engheim
fonte
Eu acho que isso complicaria as coisas, porque é possível que várias coisas ocorram dentro do animal. Por exemplo, apenas porque o estômago está digerindo algo não significa que o coração para de bater ou o animal para de andar. Claro, você pode definir um conjunto de estados, cada um dos quais leva em consideração certas funções, mas a quantidade de estados seria incrivelmente grande.
Marc Müller
Várias coisas podem acontecer por estado, porque você armazena todos os órgãos ativos em um estado e executa cada um a cada quadro. Muito do que acontece em sua simulação parece se encaixar nas transições de estado. Por exemplo Mastigação -> Digestão. Mas acho que você está certo que um animal não pode ter apenas um estado. Há transições de estado acontecendo, digamos com os membros que não estão relacionados às transições de estado que acontecem no sistema digestivo. Mas talvez seja apenas mais complicado que o que você tem. Apenas pensei que poderia ser algo a considerar.
Erik Engheim