¿Qué tipo de comportamiento o lógica de dirección puedo usar para que los móviles rodeen a otro?

10

Estoy usando la búsqueda de ruta en mi juego para dirigir una mafia a otro jugador (para perseguirlos). Esto funciona para que se sobrepongan al jugador, pero quiero que se detengan un poco antes de su destino (por lo que elegir el penúltimo nodo funciona bien).

Sin embargo, cuando múltiples mobs persiguen el móvil, a veces "se apilan uno encima del otro". ¿Cuál es la mejor manera de evitar esto? No quiero tratar a los mobs como opacos y bloqueados (porque no lo son, puedes atravesarlos) pero quiero que los mobs tengan algún sentido de estructura.

Ejemplo:

Imagine que cada serpiente se guiaba hacia mí y debería rodear a "Setsuna". ¿Te das cuenta de cómo ambas serpientes han elegido pincharme? Este no es un requisito estricto; incluso estar ligeramente compensado está bien. Pero deberían "rodear" a Setsuna.

ingrese la descripción de la imagen aquí

Vaughan Hilts
fuente
1
¿El apilamiento es solo una preocupación en el destino o también durante el tránsito? Estoy adivinando lo último.
SpartanDonut
Es lo último, @SpartanDonut
Vaughan Hilts
@KromStern Agregué una foto, espero que ayude.
Vaughan Hilts

Respuestas:

15

Dele a sus agentes una "carga electrostática" débil para que se repelen entre sí, de acuerdo con la ley de Coulomb .

Suponiendo por simplicidad que las turbas deberían empujarse entre sí con una fuerza equivalente, debería ser suficiente aplicar una fuerza entre cada par de monstruos con una magnitud some_constant / distance^2, donde some_constanthay una fuerza de repulsión configurable y distancees la distancia que los separa.

Las fuerzas de repulsión luego se caen con el cuadrado de la distancia.

Nature of Code tiene un gran ejemplo (con una demostración en vivo) aquí . Se parece a esto:

seguimiento combinado y comportamientos separados

Hacer coincidir cada elemento entre sí es una O(n^2)operación de tiempo cuadrático ( ). Si realmente tiene muchos agentes, es posible que desee optimizar los cálculos de fuerza con una aproximación de Barnes-Hut , que lo reduce a log-linear ( O(n log n)) pero requiere un quadtree .

Anko
fuente
Gran enlace, Anko. ¡Muy apreciado! Definitivamente tendré que leer todo este sitio.
Vaughan Hilts
Starcraft (1, al menos) hace algo similar con sus unidades voladoras. Pero solo lo hace cuando dejan de moverse, es decir, cuando están en movimiento, se agrupan uno encima del otro (se ignoran por completo como obstáculos), pero cuando se detienen, todos comienzan a extenderse desde lo que parece ser centro local de alguna área regular (cuadrado / círculo, probablemente) que los abarca. Esto no se ve tan bonito como el ejemplo en la respuesta, pero probablemente usa menos recursos de CPU, y posiblemente también sea más fácil de codificar ...
Shivan Dragon
@ShivanDragon SC2 presenta el mismo comportamiento, todos convergen al destino en una multitud, luego se espacian para obtener un aspecto realista y estéticamente agradable (para que sus partes no se abrochen).
Kroltan
2
Algún tipo de fuerza repelente puede ser una buena idea, pero los detalles son complicados. Experimenté con estos en un RTS temático espacial y recomiendo no seguir la física demasiado de cerca y modelarla para que se comporte bien. Algunas observaciones: 1) Dado que esto no es una simulación física, solo aplicaría la fuerza en distancias cortas. 2) Esto no puede evitar que los cuerpos finitos se superpongan 3) El potencial duro fácilmente causa errores numéricos, como las partículas que se refractan a altas velocidades. 4) Una vez que tienes un número significativo de partículas y aumenta la presión en el medio, las cosas tienden a ponerse feas.
CodesInChaos
1

Mi enfoque es similar al de @ Anko, pero basado en el trabajo de Millington y Funge de Artificial Intelligence for Games .

Así es como se vería un comportamiento de separación, pero debe tener en cuenta que esta velocidad debe calcularse con la velocidad del agente en su función de actualización.

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
fuente