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

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

Моей первой мыслью было использовать std::list, но я никогда раньше не делал ничего подобного, поэтому я ищу мнения экспертов по этому поводу.

Какой контейнер следует использовать?

Редактировать: я не просто удаляю спереди: объекты можно убивать в любом порядке, но они всегда добавляются в конец списка, поэтому последний элемент является самым новым.


person Rookie    schedule 19.06.2012    source источник
comment
линейно зациклить список объектов -- это тавтология, я полагаю? Как насчет queue? Кроме того, какой порядок вы следуете при убийстве объектов? Это элемент LIFO/FIFO/Maximum или что-то в этом роде?   -  person dirkgently    schedule 19.06.2012
comment
вы можете проверить эту блок-схему STL. Просто, чтобы получить ссылку полезно. !введите здесь описание изображения   -  person WeaselFox    schedule 19.06.2012
comment
Он устарел, и был вопрос о более новой версии для C++11, к сожалению, я больше не могу ее найти... Если бы кто-нибудь смог найти ответ на вопрос, было бы здорово.   -  person Matthieu M.    schedule 19.06.2012
comment
@dirkgently, я убиваю объекты в случайном порядке.   -  person Rookie    schedule 19.06.2012
comment
Имхо, информации слишком мало. Как бы вы наложили ограничения на ваши отдельные операции? Если все в порядке, какая амортизированная стоимость вас устраивает? Как вы думаете, подходит ли вам deque?   -  person dirkgently    schedule 19.06.2012
comment
Не имеет прямого отношения к вашему вопросу, но вы можете захотеть переработать свои объекты вместо того, чтобы постоянно создавать и удалять их. Использование некоторого пула объектов может помочь повысить производительность. en.wikipedia.org/wiki/Object_pool_pattern   -  person Emile Cormier    schedule 19.06.2012
comment
@EmileCormier, я не могу переработать, потому что порядок объектов должен быть отсортирован по времени, поэтому рендеринг будет правильным.   -  person Rookie    schedule 19.06.2012
comment
@dirkgently, я не уверен, что еще я могу сказать ... я не думаю, что deque подходит, так как я удаляю в случайных местах, и порядок должен быть отсортирован по времени для правильного рендеринга.   -  person Rookie    schedule 19.06.2012
comment
@Rookie: я не говорю о том, чтобы пометить объект как удаленный в вашем контейнере. Я говорю об объектах, которые возвращаются в свободный список (пул), а не удаляются. Когда вам нужно создать новый объект, он получает его из списка свободных, а не из кучи. Как только вы извлекаете объект из списка свободных, вы повторно инициализируете его до пригодного для использования состояния (например, устанавливаете правильную отметку времени). Динамическое выделение объектов из кучи может оказаться на удивление медленной операцией.   -  person Emile Cormier    schedule 19.06.2012
comment
@MatthieuM. Вы думаете об этом вопросе? ?   -  person Blastfurnace    schedule 19.06.2012


Ответы (3)


Если вы не уверены что ты делаешь. Только когда вы знаете, что это не сработает для вас, вы должны выбрать что-то другое.

Тем не менее, если вы регулярно добавляете содержимое в заднюю часть контейнера и удаляете его в начало, вам, вероятно, понадобится std::deque. [Изменить] Но, похоже, это не то, чем вы занимаетесь.

Я думаю, вам может понадобиться два контейнера: один для поддержания порядка вставки и один для дерева квадрантов. В Интернете есть множество реализаций дерева квадрантов, поэтому я сосредоточусь на другой. Использование std::list, как вы предлагаете, сделает операцию удаления самой быстрее, чем vector или deque. Это также имеет то преимущество, что позволяет хранить итераторы, потому что list не делает недействительными другие итераторы при удалении элемента. Ваши объекты в дереве квадрантов могут поддерживать итератор в списке порядка вставки. Когда вы удаляете элемент из дерева квадрантов, вы можете удалить его и из списка за время O(1).

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

person Michael Kristofik    schedule 19.06.2012
comment
Я уже использую вектор, но он будет тратить много памяти, потому что порядок рендеринга должен соответствовать времени, когда объект был добавлен в него. И не только память, она будет работать намного медленнее, когда я перебираю миллионы элементов в этом списке, для которых установлено значение dead. Я не просто удаляю с фронта, в какой игре это произойдет...? - person Rookie; 19.06.2012
comment
Только когда вы знаете, что это не сработает для вас - что мы знаем, потому что нам нужны указатели на элементы, чтобы они оставались действительными, когда элементы добавляются/удаляются из контейнера. - person Steve Jessop; 19.06.2012
comment
Я обновил свой ответ некоторыми комментариями к std::list. Выбор контейнера зависит от компромиссов. Хороший справочник по стандартной библиотеке будет охватывать их. Не зная точно, что делает ваша программа, мы можем только догадываться, что может работать лучше. Я думаю, что вам лучше всего попробовать и посмотреть, что произойдет. А затем дайте нам знать, что вы узнали. :-) - person Michael Kristofik; 19.06.2012
comment
@SteveJessop, что не так с вектором (или любым контейнером) std::shared_ptr? - person Michael Kristofik; 19.06.2012
comment
@MichaelKristofik, представьте себе игру, в которой объекты появляются в случайное время, и вы стреляете в них в случайные места в случайное время. Кроме того, я должен иметь возможность использовать quadtree для оптимизации поиска ближайшего объекта. И объекты будут иногда перекрываться, но это перекрытие должно быть отсортировано по времени, поэтому два объекта, созданные последовательно рядом друг с другом, также должны перекрывать друг друга в этом правильном порядке. - person Rookie; 19.06.2012
comment
@Michael: должно быть в порядке, но я (ошибочно) думал, что вы исключаете это, говоря о важности смежности. - person Steve Jessop; 19.06.2012
comment
@SteveJessop, я представлял себе этап отбраковки в конце игрового цикла OP, то есть прокручивание контейнера и удаление всех элементов dead. Расположение вектора в непрерывной памяти поможет этой операции быть быстрой, даже если мы будем удалять из середины. Все, что я говорю, это то, что не сбивайте вектор, пока не попробуете. :-) - person Michael Kristofik; 19.06.2012
comment
Снова обновил свой ответ, добавив больше идей, пытаясь отразить то, что все уже сказали. - person Michael Kristofik; 19.06.2012

Я думаю, boost::stable_vector соответствует вашим потребностям в удалении\итерации.

person Viktor Sehr    schedule 19.06.2012
comment
Я сомневаюсь в этом. Поскольку объекты всегда добавляются в конце, это больше похоже на разреженную очередь. - person Matthieu M.; 19.06.2012

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

К счастью, есть 4 контейнера, которые достаточно легко справятся с этой задачей: std::vector, std::list, std::deque и std::set. Если вы используете стандартные идиомы контейнеров (например, begin, end, erase, insert и, в меньшей степени, push_front, pop_back, front, back), вы можете использовать любой контейнер, который вам нравится. С этими 8 операциями вы можете переключаться между std::vector, std::list и std::deque, а только с первыми 4 вы также можете использовать std::set. Напишите свой код, а затем вы сможете легко переключаться между различными типами контейнеров и выполнять небольшое профилирование, чтобы сравнить производительность и накладные расходы памяти или что-то еще.

Интуитивно можно предположить, что std::list будет хорошей ставкой, и, возможно, std::set тоже сработает. Но вместо того, чтобы делать предположения, просто используйте общие инструменты, которые предоставляет вам библиотека шаблонов, и профилируйте и оптимизируйте вещи позже, когда у вас будут значимые данные о производительности для работы.

person Rook    schedule 19.06.2012
comment
Я добавил больше информации в свое редактирование, я не уверен, что еще мне нужно сказать. Представьте себе игру, в которой объекты появляются через случайные промежутки времени, затем вы снимаете их в случайных местах, но порядок рендеринга объектов должен соответствовать времени их добавления, поэтому я не могу рендерить его в случайном порядке, иначе рендеринг будет выглядеть слишком случайным, когда очень часто добавляемые и близлежащие объекты будут перекрываться. И эти объекты должны быть доступны через quadtree. - person Rookie; 19.06.2012
comment
Требование quadtree является второстепенным, поскольку оно не имеет ничего общего с работой вашего контейнера порядка рендеринга. Вам просто нужно убедиться, что, когда вы добавляете или удаляете элемент из одного из контейнеров, вы делаете то же самое и с другим контейнером. - person Rook; 19.06.2012