Desbravando com eficiência muitos inimigos que se aglomeram em torno de obstáculos

20

Estou tentando melhorar o caminho para os inimigos do meu jogo. No momento, eles basicamente se movem constantemente em direção à posição exata do jogador, calculando o ângulo entre eles e os jogadores e se movendo nessa direção. Eu também tenho um algoritmo de agrupamento que evita que os inimigos se amontoem uns sobre os outros, para que eles se formem em grupos, em vez de se agruparem.

No entanto, agora que adicionei um mapa baseado em blocos, preciso que os inimigos também sejam capazes de percorrer obstáculos e paredes, por exemplo. Inicialmente, tentei adicionar um valor de separação a blocos "não passíveis de passagem", para que o algoritmo de agrupamento considerasse as paredes e os obstáculos como objetos para os quais se afastar. Eu ainda tenho que descobrir se isso é ou não viável, porque meu teste inicial mostrou os inimigos atingindo uma "parede" invisível onde não há azulejos que não podem ser percorridos, mas, por alguma razão, eles acertam e começam a disparar.

Fiquei me perguntando se poderia ser muito alto desempenho para calcular um caminho para o jogador usando A * e, em seguida, use o algoritmo flocking para evitar aglomeração. Originalmente, meu jogo seria um shooter baseado em ondas, mas decidi torná-lo baseado no nível na linha direta da Miami, por isso é provável que eu tenha menos inimigos, com a horda ocasional, e apenas faça eles mais fortes.

Esta é uma solução viável? Estou usando Java com o Slick2D como meu mecanismo de jogo. Ou existe uma solução / algoritmo melhor que lide com esses dois problemas?

Darin Beaudreau
fonte
7
Como descrevi na edição, "isso é muito pesado" é uma pergunta a ser feita ao seu criador de perfil, porque isso dependerá da sua implementação, hardware de destino, orçamento de desempenho e contexto do seu jogo - tudo o que você e seu criador de perfil conhecem intimamente, mas estranhos da Internet não. Se você deseja que os bandos encontrem o caminho de maneira eficiente, podemos sugerir estratégias para ajudá-lo, mas apenas seu próprio perfil pode responder o que é eficiente o suficiente para suas necessidades. Se você criar um perfil e identificar um problema de desempenho específico, também podemos ajudá-lo a descobrir como resolvê-lo.
DMGregory
11
Como você os implementa afeta o desempenho. Por exemplo, apenas executando A * em líderes e confiando em reunir-se para seguidores.
Pikalek
Se o seu jogo se basear principalmente na luta contra esses inimigos, o algoritmo que você usar terá um grande impacto na aparência do jogo. Portanto, você deve tentar abordagens diferentes, por exemplo, parece que os inimigos sabem o nível e a posição do jogador perfeitamente o tempo todo e o seguem como dirigido por uma IA onisciente? - outras abordagens poderiam ser deixar os inimigos correrem na direção geral em que o jogador fez barulho e apenas na linha de visão direta correr em sua direção, ou gritar e informar outros inimigos onde o jogador está ...
Falco
@Falco Como o jogo não é mais baseado em ondas, e será baseado em níveis, e como os inimigos são zumbis ... eu estava pensando em fazer isso para que você seja visto ou faça barulho para que eles o encontrem. Então, se você usa uma arma barulhenta? Ele emite um som em um alcance e todos os inimigos no caminho do alcance em direção à localização do som emitido e, em seguida, traçam aleatoriamente em torno dessa área.
Darin Beaudreau

Respostas:

49

Isso soa como um caso de uso para campos de fluxo.

Nesta técnica, você faz uma única consulta de busca de caminho a partir do (s) objeto (s) do jogador, marcando cada célula que encontra com a célula da qual chegou.

Se todos os seus ladrilhos / arestas tiverem um custo de passagem igual, você poderá usar uma pesquisa simples e abrangente. Caso contrário, o algoritmo de Dijkstra (como A * sem objetivo / heurística) funciona.

Isso cria um campo de fluxo: uma tabela de pesquisa que associa cada célula ao próximo passo em direção ao objeto player mais próximo dessa posição.

Agora, seus inimigos podem procurar sua posição atual no campo de fluxo para encontrar o próximo passo no caminho mais curto para evitar obstáculos até o objeto jogador mais próximo, sem que cada um faça sua própria consulta de busca de caminhos.

Isso aumenta cada vez melhor quanto mais inimigos você tiver no seu rebanho. Para um único inimigo, é mais caro que o A *, porque pesquisa no mapa inteiro (embora você possa sair mais cedo depois de atingir todos os agentes de busca de caminhos). Porém, à medida que você adiciona mais inimigos, eles compartilham cada vez mais o custo de encontrar caminhos, computando segmentos de caminho compartilhados uma vez, e não repetidamente. Você também ganha vantagem com o fato de que os BFS / Dijkdtra são mais simples que A * e geralmente mais baratos para avaliar por célula inspecionada.

Exatamente onde o ponto de equilíbrio atinge, de A * individual mais barato a A * com memorização sendo mais barata (onde você reutiliza alguns dos resultados de uma consulta de busca de caminho anterior para acelerar a próxima), para os campos de fluxo sendo mais barato, dependerá da sua implementação, do número de agentes e do tamanho do seu mapa. Mas se você planejar um grande número de inimigos se aproximando de várias direções em uma área confinada, um campo de fluxo quase certamente será mais barato que o A * iterado.

Como um exemplo extremo, você pode ver um vídeo aqui com 20.000 agentes, todos simultaneamente localizando em uma grade razoavelmente pequena .

DMGregory
fonte
Essa técnica parece muito legal. Vou dar uma olhada.
Darin Beaudreau
15
É possível usar um algoritmo híbrido que constrói um campo de fluxo parcial sem pesquisar mais no mapa do que as repetidas chamadas para A * e nunca pesquisando a mesma posição duas vezes. A idéia básica é escolher um inimigo arbitrário e iniciar uma busca A * do jogador em direção a esse inimigo, marcando as células conforme você as encontra, como na geração normal de campos de fluxo. Quando a pesquisa encontrar esse inimigo, escolha outro inimigo (que você ainda não encontrou) como alvo, reordene o conjunto aberto de acordo com a nova heurística e continue pesquisando. Pare quando você encontrar todos os inimigos.
Ilmari Karonen 20/08
11
Que tal evitar colisões? Isso é (um pouco) mencionado no OP (evitando cortes quando eles chegam ao player). Parece-me que você teria que executar novamente os djikstras completos toda vez que algo acontecesse (ou acrescentar alguma lógica adicional)
Mars
2
@ Mars O OP fala sobre flocagem, então eu presumo que todos os indivíduos possam se mover na mesma velocidade; os únicos lugares em que as colisões serão um problema são os gargalos, que exigem que parte do rebanho pare e espere. No entanto, ele realmente não precisa alterar a busca de caminhos - uma fila simples provavelmente funcionaria bem o suficiente na maioria dos casos, e algum desvio de caminho (alguma seleção pseudo-aleatória de caminhos alternativos com custos semelhantes) funcionará para produzir um rebanho de aparência mais natural fluxos que também evitam todo o rebanho tentando passar por uma lacuna de um único bloco em particular :)
Luaan
3
@Luaan Em um jogo baseado em blocos, você ficaria surpreso com a frequência com que as colisões acontecem. Pessoalmente, acho a opção "enfileiramento" abaixo do ideal. Além disso, se as unidades não puderem passar uma pela outra, será necessário recalcular quando as unidades começarem a chegar à sua posição final e a vários outros casos extremos. Flocagem é difícil;)
Mars
8

A * não apresenta desempenho pesado. Eu abordaria essa situação variando os algoritmos. Faça A * de vez em quando e verifique se o próximo passo é livre para entrar ou se você precisa de evasão.

Por exemplo, rastreie a distância dos jogadores do local de destino A *, se estiver acima de um limite, recalcule um * e faça apenas movimentos de atualização. A maioria dos jogos usa uma combinação de waypoints, por exemplo, uma grade simplificada para encontrar caminhos e uma lógica que lida com o movimento entre os waypoints com algoritmos de evasão e direção usando raycasts. Os agentes tentam correr para um ponto de referência distante, manobrando obstáculos na proximidade deles, é a melhor abordagem na minha opinião.

É melhor trabalhar com máquinas de estado finito aqui e ler o livro "Programming Game AI By Example" de Mat Buckland. O livro oferece técnicas comprovadas para o seu problema e detalha a matemática necessária. O código-fonte do livro está disponível na web; o livro está em C ++, mas algumas traduções (incluindo Java) estão disponíveis.

D3d_dev
fonte
2
Com uma abordagem A * com atualização pouco frequente, pode ser útil escalonar suas atualizações, mantendo um orçamento para quantos inimigos podem reencaminhar em um único quadro. Dessa forma, você pode manter seu custo máximo de localização de caminho por quadro limitado e lidar com mais robustez com muitos caminhos de IA, amortizando seu custo total em vários quadros. Uma IA que usa um caminho obsoleto para um quadro ou dois quando o orçamento para o quadro foi excedido, ou recorrer ao acerto de contas morto se próximo, geralmente não será perturbadora.
DMGregory
2
Provavelmente afirmando o óbvio aqui, mas se você atualizar apenas alguns de seus caminhos em um determinado quadro, convém um sistema prioritário com base na distância do jogador. Provavelmente é mais importante que os inimigos próximos ao jogador atualizem seus caminhos, enquanto provavelmente não há problema em inimigos distantes usarem um caminho obsoleto.
AC
4

Não só é viável, acredito que foi feito em um jogo comercial nos anos 90 - BattleZone (1998).

Esse jogo tinha unidades 3D com movimento livre não baseado em blocos e construção de base baseada em blocos.

É assim que parecia funcionar:

Primeiro, A * ou algo semelhante (provavelmente uma variação de A * com limites estritos de quanto tempo um caminho pode ser encontrado, portanto, nunca são necessários muitos recursos para executar, mas nem sempre encontra um caminho até o destino) seria usado para encontrar um caminho para um hovertank chegar ao seu destino sem ficar preso em obstáculos baseados em ladrilhos.

Em seguida, o tanque voava pelo espaço cultivado como se fosse atraído para o centro de um ladrilho próximo em seu caminho e repelido por obstáculos, outros tanques próximos etc.

Robyn
fonte
11
Então, qual é uma boa maneira de lidar com seguir o caminho, mas não exatamente? Se eu permitir encurralar as crianças, preciso ser capaz de impedir que os inimigos colidam com o canto de um obstáculo. Devo manter o comportamento de flocagem de inimigos e obstáculos e adicionar A * para lidar com essas situações?
Darin Beaudreau