Que tipo de comportamento ou lógica de direção posso usar para fazer com que os celulares cercem outros?

10

Estou usando a busca de caminhos no meu jogo para levar uma multidão a outro jogador (para persegui-los). Isso funciona para levá-los ao topo do player, mas eu quero que eles parem um pouco antes do destino (portanto, escolher o penúltimo nó funciona bem).

No entanto, quando vários mobs estão perseguindo o celular, às vezes "se empilham uns sobre os outros". Qual é a melhor maneira de evitar isso? Não quero tratar as mobs como opacas e bloqueadas (porque não são, você pode passá-las), mas quero que as mobs tenham algum senso de estrutura.

Exemplo:

Imagine que cada cobra se guiou para mim e deveria cercar "Setsuna". Observe como as duas cobras decidiram me atacar? Este não é um requisito estrito; mesmo sendo ligeiramente deslocado, tudo bem. Mas eles devem "cercar" Setsuna.

insira a descrição da imagem aqui

Vaughan Hilts
fonte
1
O empilhamento é apenas uma preocupação no destino ou também durante o transporte? Eu estou adivinhando o último.
SpartanDonut
É o último, @SpartanDonut
Vaughan Hilts
@KromStern Adicionei uma foto, espero que ajude.
Vaughan Hilts

Respostas:

15

Dê aos seus agentes uma "carga eletrostática" fraca para fazê-los se repelirem, de acordo com a lei de Coulomb .

Assumindo por simplicidade que os mobs devem se afastar com força equivalente, deve ser suficiente aplicar uma força entre todos os pares de mobs com uma magnitude some_constant / distance^2, onde some_constantexiste uma força de repulsão configurável e distancea distância que os separa.

As forças de repulsão caem com o quadrado da distância.

A Nature of Code tem um ótimo exemplo (com uma demonstração ao vivo) aqui . Se parece com isso:

comportamentos combinados de acompanhamento e separação

Combinar cada elemento com o outro é uma O(n^2)operação de tempo quadrático ( ). Se você realmente possui muitos agentes, pode otimizar os cálculos de força com uma aproximação de Barnes-Hut , que o leva a log-linear ( O(n log n)), mas requer um quadtree .

Anko
fonte
Ótimo link, Anko. Muito apreciado! Definitivamente vou ter que ler todo o site.
Vaughan Hilts
Starcraft (1, pelo menos) faz algo semelhante com suas unidades voadoras. Mas isso acontece apenas quando eles param de se mover, ou seja, quando estão em movimento, eles se amontoam (se ignoram completamente como obstáculos), mas quando eles param, todos começam a se espalhar pelo que parece ser o centro local de alguma área regular (quadrado / círculo, provavelmente) que os abrange. Isso não parece tão bonita como o exemplo na resposta, mas provavelmente usa menos recursos de CPU, e é possivelmente mais fácil de código também ...
Shivan Dragão
O @ShivanDragon SC2 apresenta o mesmo comportamento, todos convergem para o destino em uma multidão, depois se abrem para olhares realistas e esteticamente agradáveis ​​(para que suas partes não fiquem grudadas).
Kroltan
2
Algum tipo de força repulsiva pode ser uma boa ideia, mas os detalhes são complicados. Eu experimentei isso em um RTS com tema espacial e recomendo não seguir a física muito de perto e modelá-la para que ela se comporte bem. Algumas observações: 1) Como essa não é uma simulação de física, eu aplicaria a força apenas a curtas distâncias. 2) Isso não pode impedir a sobreposição de corpos finitos. 3) O potencial rígido causa com facilidade erros numéricos, como partículas sendo refratadas em altas velocidades. 4) Quando você tem um número significativo de partículas e a pressão no meio aumenta, as coisas tendem a ficar feias.
CodesInChaos
1

Minha abordagem é semelhante à @ Anko's, mas baseada no trabalho de Millington e Funge da Artificial Intelligence for Games .

É assim que um comportamento de separação seria, mas é necessário levar em consideração que essa velocidade deve ser calculada com a velocidade do agente em sua função Atualizar.

public Vector3 GetSeparationVel (float threshold, float decayCoefficient)
{
    threshold = threshold * threshold;
    Vector3 separationVelocity = Vector3.Zero;
    for (int i = 0; i < enemies.Length; i++) {
        if (enemies[i] == this) {
            continue;
        }
        Vector3 direction = this.position - enemies[i].position;
        float distance = direction.LengthSquared();
        float strenght = 0.0f;
        if (distance < (threshold)) {
            strenght = Math.Min(decayCoefficient / distance, this.maxAccel);
            direction.Normalize();
            separationVelocity += strenght * direction;
        }
    }
}
reefaktor
fonte