Я написал простой инструмент моделирования физики, который позволяет мне прыгать шары по экрану. Вы можете нажать и перетащить, чтобы запустить мяч, или вы можете создавать шары по сотне за один раз и наблюдать, как они взаимодействуют друг с другом.
[Ссылка на увеличенную версию]
Это была забавная маленькая программа для работы, и я хочу пойти дальше, если смогу. Я знаю, что они говорят, что преждевременная оптимизация - корень всего зла, но я начинаю преодолевать реальные барьеры производительности и хочу знать, может ли кто-нибудь, кто имеет опыт в разработке игр/симуляторов, помочь мне.
Проблема:
Прямо сейчас моя программа задыхается, если вы добавляете слишком много шаров (на моей машине она не может обрабатывать более 800). Если вы это сделаете, симуляция больше не будет реалистичной, и все шары перекрывают друг друга на дне.
Проблема в обнаружении столкновений. В наиболее наивном случае обнаружение столкновений является проблемой O (N ^ 2). Каждый шар проверяет каждый второй. Это очень быстро снижает производительность (даже после 100 шаров вы будете делать 10 000 проверок столкновений за цикл).
Если вы посмотрите здесь, вы можете увидеть скриншот, где я добавил несколько сотен шаров. Симулятор не может идти в ногу, и они начинают перекрывать друг друга.
[Ссылка на увеличенную версию]
В настоящее время я обнаруживаю столкновения, ища перекрывающиеся шары. Если я нахожу два шарика, которые перекрывают друг друга, я разделяю их по минимальному расстоянию перевода (MTD) или раздвигаю их. Затем я использую простое физическое уравнение, чтобы скорректировать их импульсные векторы, и после столкновения они движутся в разных направлениях.
Это прекрасно работает, за исключением случаев, когда слишком много шаров, минимальное расстояние перевода становится заметным. Они начинают перекрывать друг друга в больших количествах и постоянно толкают друг друга на дно. Чем хуже я увеличиваю "гравитацию", тем хуже. Давление на них увеличивается, и количество их сжатых/перекрывающих друг друга увеличивается.
Опять же, у меня нет проблем, пока я не попал в значительное количество N шаров.
Текущий метод оптимизации:
Обнаружение столкновения -
Смести и обрежь - (иначе. Сортировка и Сметание)
Я использую сортировку вставки на своих шарах каждый цикл цикла вдоль их оси X. Из-за характера сортировки вставок я могу использовать временную согласованность моего симулятора. Кадр за кадром, позиции шаров меняются незначительно, так что сортировке не нужно много работать. Это приводит к тому, что время линейной сортировки амортизируется во время O (N) или линейно, а не в среднем времени O (N ^ 2).
Поскольку шары отсортированы, я делаю пару предварительных проверок во втором цикле перед проверкой столкновений. Теперь мне нужно только проверить шары рядом друг с другом (потому что они отсортированы вдоль оси x), и я вырываюсь из второй циклы каждый раз, когда проверяю шар против другого шара, xmin которого больше, чем xmax текущего шара, Так что пропускает тысячи проверок.
Когда я реализовал это, это принесло огромное улучшение скорости. Тем не менее, я все еще хотел бы иметь возможность обрабатывать более 600-800 мячей. Я читал о Physics Engines, которые легко справляются с обнаружением столкновений между 10k объектами одновременно, поэтому я хотел бы подумать, что смогу достичь 1-2k с небольшой работой.
После запуска профилировщика выяснилось, что Collision Detection израсходовал около 55% моего времени, а рендеринг - около 45%. Итак, это мои две самые дорогие расходы.
Вопрос:
Можете ли вы придумать какие-нибудь лучшие алгоритмы или методы, которые позволили бы моему симулятору обрабатывать больше шаров?
Соответствующий код:
Весь проект:
svn checkout http://simucal-projects.googlecode.com/svn/ballbounce/trunk/
или, нажмите здесь, чтобы просмотреть файлы вручную в вашем браузере.
Разделы интересов:
- Pastebin: checkCollisions() - w/Sweep и Prune
- Pastebin: resolCollision() - дорогостоящая проверка и разрешение коллизий, если они еще не удалены Sweep и Prune.
- Pastebin: render() - один только рендеринг уходит примерно 45% моего времени.