Какое рулевое поведение или логику я могу использовать, чтобы заставить мобильные телефоны окружать другого?

10

Я использую поиск пути в своей игре, чтобы привести толпу к другому игроку (преследовать его). Это работает для того, чтобы превзойти игрока, но я хочу, чтобы они немного остановились перед назначением (поэтому выбор предпоследнего узла работает нормально).

Тем не менее, когда несколько мобов преследуют мобильные устройства, они иногда «укладываются друг на друга». Какой лучший способ избежать этого? Я не хочу относиться к мобам как к непрозрачным и заблокированным (потому что это не так, вы можете проходить через них), но я хочу, чтобы у мобов было какое-то чувство структуры.

Пример:

Представь, что каждая змея ведет себя ко мне и должна окружать «Сецуна». Заметьте, как обе змеи выбрали меня? Это не строгое требование; даже немного смещено - это нормально. Но они должны «окружить» Сецуну.

введите описание изображения здесь

Вон Хилтс
источник
1
Является ли штабелирование только проблемой в пункте назначения или также в пути? Я предполагаю последнее.
SpartanDonut
Это последнее, @SpartanDonut
Вон Хилтс
@KromStern Я добавил картинку, надеюсь, это поможет.
Вон Хилтс

Ответы:

15

Дайте вашим агентам слабый «электростатический заряд», чтобы заставить их отталкивать друг друга, в соответствии с законами Кулона .

Предполагая для простоты, что мобы должны отталкивать друг друга с эквивалентной силой, этого должно быть достаточно, чтобы приложить силу между каждой парой мобов с величиной some_constant / distance^2, где some_constantесть настраиваемая сила отталкивания и distanceрасстояние, разделяющее их.

Сила отталкивания затем уменьшается с квадратом расстояния.

Природа кода есть отличный пример (с живой демонстрацией) здесь . Это выглядит так:

комбинированное следование и раздельное поведение

Сопоставление каждого элемента со всеми другими - это O(n^2)операция квадратичного времени ( ). Если у вас действительно много агентов, вы можете оптимизировать вычисления сил с помощью приближения Барнса-Хата , которое сводится к логарифмическому ( O(n log n)), но требует квадродерева .

Анко
источник
Отличная ссылка, Анко. Очень признателен! Я определенно должен прочитать весь этот сайт.
Воган Хилтс
Starcraft (по крайней мере, 1) делает нечто подобное со своими летающими единицами. Но это происходит только тогда, когда они перестают двигаться, то есть когда они находятся в движении, они слипаются друг на друга (полностью игнорируя друг друга как препятствия), но когда они останавливаются, они начинают распространяться из того, что выглядит как локальный центр некоторой регулярной области (квадрат / круг, вероятно), которая охватывает их. Это не выглядит так красиво, как в примере ответа, но, вероятно, использует меньше ресурсов ЦП, и, возможно, его будет проще кодировать ...
Shivan Dragon
@ShivanDragon SC2 демонстрирует одинаковое поведение, все они сходятся к месту назначения в толпе, а затем выделяются для своего рода реалистичного и эстетически привлекательного вида (поэтому их части не смещаются).
Кролтан
2
Какая-то отталкивающая сила может быть хорошей идеей, но детали хитры. Я экспериментировал с ними в RTS на космическую тематику и рекомендую не слишком внимательно следить за физикой, а скорее моделировать ее, чтобы она вела себя хорошо. Некоторые наблюдения: 1) Так как это не физическое моделирование, я применил бы силу только на короткие расстояния. 2) Это не может помешать перекрытию конечных тел. 3) Твердый потенциал легко вызывает числовые ошибки, такие как частицы, преломленные при высоких скоростях. 4) Как только у вас появляется значительное количество частиц и давление в середине повышается, все становится уродливым.
CodesInChaos
1

Мой подход похож на @ Anko's, но основан на работе Миллингтона и Фанге из « Искусственного интеллекта для игр». .

Вот как должно выглядеть поведение Разделения, но вы должны принять во внимание, что эта скорость должна вычисляться со скоростью агента в его функции Обновления.

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
источник