У меня очень большое количество сущностей (единиц). На каждом шаге каждый юнит должен знать позиции всех юнитов рядом с ним (расстояние меньше заданной константы R ). Все подразделения движутся непрерывно. Это в 3D.
В среднем, это будет 1% от общего числа единиц рядом с любой другой данной единицей с данными ограничениями.
Как я могу сделать это эффективно, без брутфорса?
Ответы:
Используйте один из распространенных алгоритмов разделения пространства, таких как Quadtree, Octree, BSP tree или даже простая Grid System. У каждого есть свои плюсы и минусы для каждого конкретного сценария. Вы можете прочитать больше о них в этих книгах .
Как правило (или, как я слышал, я не слишком знаком с причинами этого), Quadtree или Octree лучше подходят для наружной среды, в то время как дерево BSP лучше подходит для внутренних сцен. И выбор между использованием Quadtree или Octree зависит от того, насколько плоский ваш мир. Если есть небольшая разница в оси Y, использование Octree было бы расточительным. Octree - это в основном Quadtree с дополнительным измерением.
Наконец, не пренебрегайте простотой решения Grid. Многие люди игнорируют, что простой сетки иногда может быть достаточно (и даже более эффективным) для своих проблем, и вместо этого переходят прямо к более сложному решению.
Использование сетки состоит просто в разделении мира на равномерно распределенные регионы и хранении сущностей в соответствующем регионе мира. Затем, с учетом позиции, поиск соседних объектов будет происходить путем итерации по регионам, которые пересекают ваш радиус поиска.
Допустим, ваш мир варьировался от (-1000, -1000) до (1000, 1000) в плоскости XZ. Например, вы можете разделить его на сетку 10x10, например, так:
Затем вы поместите сущности в соответствующие ячейки сетки. Например, объект с XZ (-1000, -1000) попадет в ячейку (0,0), а объект с XZ (1000, 1000) попадет в ячейку (9, 9). Затем, учитывая положение и радиус в мире, вы можете определить, какие ячейки пересекаются этим «кругом», и выполнять итерацию только по ним с простым двойным значением для.
В любом случае, изучите все альтернативы и выберите ту, которая лучше всего подходит для вашей игры. Я признаю, что до сих пор не достаточно разбираюсь в предмете, чтобы решить, какой из алгоритмов будет для вас наилучшим.
Находите это на другом форуме, и это может помочь вам с решением:
Учитывая ваше расплывчатое описание проблемы, я склоняюсь и к сеточному решению (то есть если предположить, что единицы являются небольшими и довольно однородно распределены).
источник
Я написал это некоторое время назад. Это теперь на коммерческом сайте, но вы можете получить источник для личного использования бесплатно. Это может быть излишним и написано на Java, но хорошо документировано, поэтому его не должно быть слишком сложно обрезать и переписать на другом языке. Он в основном использует Octree с настройками для обработки действительно больших объектов и многопоточности.
Я обнаружил, что Octree предлагает лучшее сочетание гибкости и эффективности. Я начал с сетки, но было невозможно правильно подобрать размеры квадратов, а большие участки пустых квадратов занимали пространство и вычислительные мощности даром. (И это было только в двух измерениях.) Мой код обрабатывает запросы из нескольких потоков, что значительно усложняет задачу, но документация должна помочь вам обойти это, если вам это не нужно.
источник
Чтобы повысить свою эффективность, попробуйте тривиально отклонить 99% «юнитов», которые не находятся рядом с целевым юнитом, используя очень недорогой ограничивающий флажок. И я надеюсь, что вы могли бы сделать это без пространственной структуризации ваших данных. Поэтому, если все ваши юниты хранятся в единой структуре данных, вы можете попытаться обойти ее от начала до конца и, во-первых, проверить, находится ли текущая единица за пределами ограничивающей рамки интересующей единицы.
Определите ограничивающий прямоугольник для интересующей единицы, чтобы он мог безопасно отбрасывать предметы, которые не могут считаться «рядом» с ним. Проверка на исключение из ограничивающей рамки может быть дешевле, чем проверка радиуса. Однако в некоторых системах, где это было проверено, было установлено, что это не так. Два выступают почти одинаково. Это отредактировано после большого обсуждения ниже.
Первый: 2D ограничивающий прямоугольник.
По сравнению с чем-то вроде этого (в 3D):
Если объект не отклоняется тривиально, вы выполняете более дорогой и точный тест столкновения. Но вы просто ищете близость, поэтому сферный тест подходит для этого, но только для 1% объектов, которые переживают тривиальное отклонение.
Эта статья поддерживает поле для тривиального отклонения. http://www.h3xed.com/programming/bounding-box-vs-bounding-circle-collision-detection-performance-as3
Если этот линейный подход не дает нужной вам производительности, то может потребоваться иерархическая структура данных, о которой говорили другие авторы. R-деревья заслуживают рассмотрения. Они поддерживают динамические изменения. Они являются деревьями пространственного мира.
Я просто не хотел, чтобы вы пошли на все эти трудности, представляя такую сложность, если бы вы могли ее избежать. Кроме того, как насчет стоимости поддержания этой сложной структуры данных в актуальном состоянии, когда объекты перемещаются несколько раз в секунду?
Помните, что сетка представляет собой одноуровневую структуру пространственных данных. Этот предел означает, что он не является действительно масштабируемым. По мере того, как мир увеличивается в размерах, увеличивается и количество клеток, которое необходимо покрыть. В конечном итоге само количество ячеек становится проблемой производительности. Однако для мира определенного размера это даст вам огромный прирост производительности без пространственного разделения.
источник
inside = (dot(p-p0, p-p0) <= r*r)
if
утверждении). Тоже не очень реалистично. Но, честно говоря, если вы начинаете оптимизировать подобные вещи, то вы определенно начинаете не с того места.Я должен сделать это ответом, потому что у меня нет пунктов, чтобы комментировать или поднять голос. Для 99% людей, которые задают этот вопрос, ограничивающий прямоугольник является решением, описанным Ciaran. На скомпилированном языке он в мгновение ока отвергнет 100 000 ненужных единиц. Есть много накладных расходов, связанных с решениями без грубой силы; с меньшими числами (скажем, ниже 1000) они будут дороже с точки зрения времени обработки, чем проверка методом "грубой силы". И они займут намного больше времени программирования.
Я не уверен, что означает «очень большое число» в вопросе, или что другие люди, ищущие здесь ответы, будут подразумевать под этим. Я подозреваю, что мои числа выше консервативны и могут быть умножены на 10; Я лично весьма предубежден против методов грубой силы и серьезно раздражен тем, насколько хорошо они работают. Но я бы не хотел, чтобы кто-то, скажем, с 10000 единиц тратит время на причудливое решение, когда несколько быстрых строк кода помогут. Они всегда могут прийти в голову позже, если им это нужно.
Кроме того, я хотел бы отметить, что проверка ограничивающей сферы требует умножения, когда ограничивающая рамка не делает. Умножение по своей природе занимает в несколько раз больше времени, чем сложение и сравнение. Обязательно будет некоторая комбинация языка, ОС и аппаратного обеспечения, где проверка сферы будет быстрее, чем проверка коробки, но в большинстве мест и раз проверка коробки должна быть быстрее, даже если сфера отклоняет несколько не относящихся к делу единиц, коробка принимает. (И там, где сфера быстрее, новая версия компилятора / интерпретатора / оптимизатора, скорее всего, изменит это.)
источник