Я делаю игру, которая состоит из множества экранных объектов, одним из которых является игрок. Мне нужно знать, какие объекты сталкиваются на каждой итерации.
Я сделал что-то вроде этого:
for (o in objects)
{
o.stuff();
for (other in objects)
if (collision(o, other))
doStuff();
bla.draw();
}
Это имеет O (n ^ 2), что мне говорят, это плохо. Как мне сделать это более эффективно, возможно ли это? Я делаю это в Javascript, и n обычно будет меньше 30, это будет проблемой, если это останется прежним?
Ответы:
Если максимум 30 объектов, вам вообще не нужно много оптимизировать, кроме того, чтобы не проверять одни и те же две пары друг против друга более одного раза за кадр. Какой пример кода ниже будет охватывать. Но если вам интересны различные оптимизации, которые будет использовать физический движок, продолжайте читать оставшуюся часть этого поста.
Вам потребуется реализация пространственного разбиения , такая как Octree (для 3D-игр) или Quadtree (для 2D-игр). Они делят мир на подразделы, а затем каждый подраздел разделяется далее в той же усадьбе, пока они не будут разделены до минимального размера. Это позволяет очень быстро проверить, какие другие объекты находятся в том же регионе мира, что и другой, что ограничивает количество столкновений, с которыми вы должны проверять.
В дополнение к пространственному разделению я бы порекомендовал создать AABB ( ограничивающую ось рамку ) для каждого из ваших физических объектов. Это позволяет вам сравнивать AABB одного объекта с другим, что намного быстрее, чем детальная проверка между объектами для каждого элемента.
Это может быть сделано еще одним шагом вперед для сложных или больших физических объектов, где вы можете подразделить саму физическую сетку, давая каждой под-форме свою собственную AABB, с которой вы можете проверить, только если AABB двух объектов перекрываются.
Большинство физических движков деактивируют активное физическое моделирование на физических телах, как только они придут в себя. Когда физическое тело деактивировано, ему нужно только проверять наличие столкновений с его AABB в каждом кадре, и если что-то сталкивается с AABB, то оно затем активируется и выполняет более детальную проверку столкновений. Это уменьшает время моделирования.
Кроме того, многие физические движки используют «острова симуляции», где группы физических тел, которые находятся близко друг к другу, группируются вместе. Если все на острове моделирования находится в состоянии покоя, то сам остров моделирования отключается. Преимущество острова моделирования состоит в том, что все тела внутри него могут перестать проверять наличие столкновений после того, как остров неактивен, и единственная проверка каждого кадра состоит в том, чтобы увидеть, попало ли что-либо в AABB острова. Только когда что-то входит в AABB острова, каждое тело на острове должно проверять наличие столкновений. Остров симуляции также активируется, если какое-либо тело внутри него снова начинает двигаться самостоятельно. Если тело перемещается достаточно далеко от центра группы, оно удаляется с острова.
В конце концов у вас остается что-то вроде этого (в псевдокоде):
Я также рекомендовал бы не иметь столько циклов внутри таких циклов, как в приведенном выше примере, просто чтобы вы поняли, я бы разбил его на несколько функций, которые предоставляют вам ту же функциональность, что и та, что показана выше.
Кроме того, убедитесь, что вы не изменяете контейнер AABBNodes во время его циклического прохождения, поскольку это может означать пропущенные проверки коллизий. Это может звучать как здравый смысл, но вы будете удивлены, насколько легко реагировать на столкновения, вызывая изменения, которых вы не ожидаете. Например, если столкновение привело к тому, что один из сталкивающихся объектов изменил положение настолько, чтобы удалить их из AABB узла Octree, который вы проверяли, он может изменить этот контейнер. Чтобы решить эту проблему, я рекомендую вести список всех событий столкновения, которые происходят во время проверок, а затем, после того как все проверки завершены, просмотрите список и отправьте все события столкновения.
источник
Ваш пример тестирует каждую пару объектов несколько раз.
Давайте рассмотрим очень простой пример с массивом, содержащим 0,1,2,3
С вашим кодом вы получите это:
Теперь давайте посмотрим на следующий код:
Если мы снова используем массив, содержащий 0,1,2,3, у нас будет следующее поведение:
Со вторым алгоритмом мы получили 6 тестов столкновений, в то время как предыдущий запросил 12 тестов столкновений.
источник
N(N-1)/2
сравнения, которые по-прежнему O (N ^ 2) производительности.Разработайте свой алгоритм в соответствии с вашими потребностями, но сохраняйте детали реализации в инкапсуляции. Даже в Javascript применяются основные концепции ООП.
Ибо
N =~ 30, O(N*N)
это не проблема, и ваш линейный поиск, скорее всего, будет таким же быстрым, как и любая другая альтернатива. Но вы не хотите жестко кодировать предположения в своем коде. В псевдокоде у вас будет интерфейсЭто описывает, что может сделать ваш список элементов. Затем вы можете написать класс ArrayContainer, который реализует этот интерфейс. В Javascript код будет выглядеть так:
И вот пример кода, который создает 300 ограничивающих рамок и получает все пересечения. Если вы реализовали ArrayContainer и QuadTreeContainer правильно, единственное , что вам нужно будет изменить в коде изменения
var allMyObjects = new ArrayContainer()
вvar allMyObjects = QuadTreeContainer()
.Я пошел дальше и подхватил реализацию стандартного ArrayContainer здесь:
http://jsfiddle.net/SKkN5/1/
источник
Вы должны также рассмотреть типы объектов, которые могут разумно сталкиваться.
Например, игрок, вероятно, должен быть проверен на столкновение со всем, кроме его собственных пуль. Однако врагам может потребоваться проверка только против пуль игрока. Пули почти наверняка не должны сталкиваться друг с другом.
Для эффективной реализации этого вы, вероятно, хотите хранить отдельные списки объектов, по одному для каждого типа объекта.
источник