Как разделить двухмерный игровой мир для лучшего обнаружения столкновений - PullRequest
14 голосов
/ 23 мая 2009

Я разрабатываю игру, которая имеет большую квадратную 2d игровую зону. Игровая зона без плиток с ограниченными сторонами (без обтекания). Я пытаюсь понять, как мне лучше разделить этот мир, чтобы повысить производительность обнаружения столкновений. Вместо того, чтобы проверять каждую сущность на предмет столкновения со всеми другими сущностями, я хочу проверять только соседние сущности на предмет столкновения и предотвращения препятствий.

У меня есть несколько особых проблем для этого игрового мира ...

  • Я хочу иметь возможность использовать большое количество объектов в игровом мире одновременно. Тем не менее,% объектов не будет сталкиваться с объектами одного типа. Например, снаряды не будут сталкиваться с другими снарядами.

  • Я хочу иметь возможность использовать большой диапазон размеров объектов. Я хочу, чтобы разница между самыми маленькими и самыми большими объектами была очень большой.

  • В игровом мире очень мало статических или неподвижных объектов.

Мне интересно использовать что-то похожее на то, что описано в ответе здесь: Дерево Quadtree vs Красно-черное для игры на C ++?

Меня беспокоит то, насколько хорошо дерево в мире сможет справляться с большими размерами в сущностях? Чтобы разделить мир на более мелкие объекты, более крупные должны будут занимать большое количество регионов, и меня беспокоит, как это повлияет на производительность системы.

Моя другая главная проблема - как правильно обновлять список занятых районов. Поскольку существует много движущихся объектов, и некоторые очень большие, кажется, что разделение мира создаст значительные накладные расходы для отслеживания того, какие объекты занимают какие области.

В основном я ищу какие-нибудь хорошие алгоритмы или идеи, которые помогут уменьшить количество обнаружений столкновений и обходов препятствий.

Ответы [ 5 ]

7 голосов
/ 23 мая 2009

На вашем месте я бы начал с простого дерева BSP (раздел двоичного пространства). Так как вы работаете в 2D, проверки с привязанными ящиками выполняются очень быстро. Вам в основном нужны три класса: CBspTree, CBspNode и CBspCut (не очень нужны)

  1. CBspTree имеет один экземпляр корневого узла класса CBspNode
  2. CBspNode имеет экземпляр CBspCut
  3. CBspCut символизирует, как вы разрезаете набор в два непересекающихся набора. Это можно аккуратно решить путем введения полиморфизма (например, CBspCutX или CBspCutY или какой-либо другой линии разреза). CBspCut также имеет два CBspNode

Интерфейс к разделенному миру будет проходить через древовидный класс, и было бы неплохо создать еще один слой поверх него, на случай, если вы захотите заменить решение BSP, например, на. четырехугольное дерево. Как только вы освоитесь с этим. Но по моему опыту, BSP будет хорошо.

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

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

Когда дело доходит до вашего первого вопроса. Если вы собираетесь использовать это подразделение только для тестирования столкновений и ничего другого, я предлагаю, чтобы вещи, которые никогда не сталкивались, никогда не вставляются в дерево. Ракета, например, как вы говорите, не может столкнуться с другой ракетой. Что означало бы, что вам даже не нужно хранить ракету на дереве.

Однако вы можете использовать bsp и для других вещей, вы не указали это, но имейте это в виду (для выбора объектов, например, с помощью мыши). В противном случае я предлагаю вам сохранить все в bsp и разрешить конфликт позже. Просто попросите bsp списка объектов в определенной области, чтобы получить ограниченный набор возможных кандидатов на столкновение, и выполните проверку после этого (предполагая, что объекты знают, с чем они могут столкнуться, или какой-то другой внешний механизм).

Если вы хотите ускорить процесс, вам также необходимо позаботиться о слиянии и разбиении , т.е. когда вещи будут удалены из дерева, многие узлы станут пустыми или количество элементов ниже некоторого уровня узла уменьшится ниже некоторого порога слияния. Затем вы хотите объединить два поддерева в один узел, содержащий все элементы. Разделение происходит, когда вы вставляете предметы в мир. Поэтому, когда количество предметов превышает некоторый порог разделения, вы вводите новый разрез, который разделяет мир на две части. Эти пороги слияния и разделения должны быть двумя константами, которые вы можете использовать для настройки эффективности дерева.

Слияние и разделение в основном используются для поддержания сбалансированности дерева и обеспечения его максимальной эффективности в соответствии с его спецификациями. Это действительно то, что вам нужно беспокоиться. Перемещение объектов из одного места и, соответственно, обновление дерева - это очень быстро. Но когда дело доходит до слияния и разделения, это может стать дорогим, если вы делаете это слишком часто.

Этого можно избежать, введя какую-то ленивую систему слияния и разделения, то есть у вас есть какая-то грязная пометка или изменение счетчика. Пакетируйте все операции, которые можно пакетировать, то есть перемещение 10 объектов и вставка 5 могут быть одним пакетом. По завершении этого пакета операций вы проверяете, не является ли дерево грязным, а затем выполняете необходимые операции слияния и / или разделения.

Оставьте несколько комментариев, если вы хотите, чтобы я объяснил дальше.

Ура!


Редактировать

Есть много вещей, которые можно оптимизировать в дереве. Но, как вы знаете, преждевременная оптимизация является корнем всего зла. Так что начните с простого. Например, вы можете создать некую общую систему обратного вызова, которую вы можете использовать при обходе дерева. Таким образом, вам не нужно запрашивать дерево, чтобы получить список объектов, которые соответствуют ограниченному полю «вопрос», вместо этого вы можете просто пройти по дереву и выполнить этот обратный вызов каждый раз, когда вы что-то нажимаете. «Если это ограниченное поле, которое я предоставляю, пересекает вас, то выполните этот обратный вызов с этими параметрами»

5 голосов
/ 23 мая 2009

Вы определенно хотите проверить этот список ресурсов по обнаружению столкновений из gamedev.net . Он полон ресурсов с соглашениями по разработке игр.

Только для обнаружения коллизий, проверьте их полный список статей и ресурсов .

4 голосов
/ 23 мая 2009

Меня беспокоит, насколько хорошо будет дерево Подразделение мира сможет обрабатывать большие различия в размерах юридические лица? Разделить мир достаточно для малых предприятий большие должны будут занимать большое количество регионов и я обеспокоен тем, как это повлияет производительность системы.

Используйте четырехугольное дерево. Для объектов, которые существуют в нескольких областях, у вас есть несколько вариантов:

  • Храните объект в обеих ветвях до упора. Все заканчивается в листовых узлах, но вы можете получить значительное количество дополнительных указателей. Может подойти для статичных вещей.

  • Разделите объект на границе зоны и вставьте каждую часть в соответствующие места. Создает много боли и плохо определено для многих объектов.

  • Храните объект в самой низкой точке дерева, которую вы можете. Наборы объектов теперь существуют в листовых и неконечных узлах, но каждый объект имеет один указатель на него в дереве. Вероятно, лучше всего подходит для объектов, которые собираются двигаться.

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

Моя другая главная проблема - как правильно вести список занятых районы в курсе. Так как есть много движущихся объектов, а некоторые очень большие, кажется, что деление мир вверх создаст значительный сумма накладных расходов для отслеживания какие объекты занимают какие регионы.

Будут накладные расходы на поддержание ваших сущностей в правильных местах дерева каждый раз, когда они перемещаются, да, и это может быть важно. Но суть в том, что вы выполняете намного меньше работы в своем коде коллизий. Даже если вы добавляете некоторые издержки с обходом дерева и обновляете, оно должно быть намного меньше, чем накладные расходы, которые вы только что удалили, используя дерево вообще.

Очевидно, что в зависимости от количества объектов, размера игрового мира и т. Д., Компромисс может не стоить того. Обычно это оказывается победой, но трудно понять, не делая этого.

2 голосов
/ 23 мая 2009

Есть много подходов. Я бы порекомендовал установить некоторые конкретные цели (например, x тестов столкновений в секунду с соотношением y между самыми маленькими и самыми большими объектами) и выполнить несколько прототипов, чтобы найти самый простой подход, который достигает этих целей. Вы можете быть удивлены тем, как мало работы вам нужно, чтобы получить то, что вам нужно. (Или это может быть тонна работы, в зависимости от ваших данных.)

Многие ускоряющие структуры (например, хороший BSP) могут занять некоторое время для настройки и, как правило, не подходят для быстрой анимации.

Существует много литературы по этой теме, поэтому потратьте некоторое время на поиск и исследование, чтобы придумать подходы списка кандидатов. Смоделируйте их и профиль.

1 голос
/ 26 мая 2009

Я бы соблазнился просто наложить грубую сетку на игровую зону, чтобы сформировать 2D-хэш. Если сетка по крайней мере имеет размер самой большой сущности, тогда у вас есть только 9 квадратов сетки для проверки на наличие коллизий, и это намного проще, чем управление квад-деревьями или произвольными деревьями BSP. Затраты на определение того, в каком крупном квадрате сетки вы находитесь, обычно составляют всего 2 арифметические операции, и при обнаружении изменения сетка просто должна удалить одну ссылку / идентификатор / указатель из списка одного квадрата и добавить его в другой квадрат.

Дальнейшая выгода может быть получена от удержания снарядов вне системы поиска сетки / дерева / и т. Д. - поскольку вы можете быстро определить, где снаряд будет находиться в сетке, вы знаете, какие квадраты сетки запрашивать у потенциальных коллидеров. Если вы проверяете столкновения с окружающей средой для каждого снаряда по очереди, другие субъекты не должны затем проверять наличие столкновений с снарядами в обратном порядке.

...