Я пишу небольшую аркадную игру на C++ (многонаправленный двумерный космический шутер) и заканчиваю часть обнаружения столкновений.
Вот как я это организовал (я просто придумал, чтобы это была дерьмовая система):
Каждый корабль состоит из круглых компонентов - количество компонентов в каждом корабле произвольно (больше компонентов, больше циклов процессора). У меня есть расстояние maxComponent, которое я вычисляю при создании корабля, которое, по сути, является самой длинной линией, которую я могу провести от центра корабля до края самого дальнего компонента. Я отслеживаю вещи на экране и использую это maxComponentDistance, чтобы увидеть, достаточно ли они близки, чтобы столкнуться.
Если они находятся в непосредственной близости, я начинаю проверять, пересекаются ли компоненты разных кораблей. Здесь возникает мой вопрос об эффективности.
У меня есть (x, y) местоположения компонента относительно центра корабля, но это не учитывает то, как корабль в настоящее время вращается. Я считаю их относительными, потому что не хочу пересчитывать компоненты каждый раз, когда корабль движется. Итак, у меня есть небольшая формула для расчета поворота, и я возвращаю 2d-вектор, соответствующий положению с учетом поворота относительно центра корабля.
Обнаружение столкновений находится в GameEngine и использует 2d-вектор. Мой вопрос о возвращаемых типах. Должен ли я просто создавать и возвращать объект 2d-вектора каждый раз, когда вызывается эта функция или же должен ли я предоставить этому компонентному объекту дополнительную частную двумерную векторную переменную, отредактировать частную переменную при вызове функции и вернуть указатель на этот объект?
Я не уверен в эффективности распределения памяти по сравнению с постоянной, редактируемой частной переменной. Я знаю, что память также должна быть выделена для частной переменной, но не каждый раз, когда она проверяется на конфликты, а только при создании нового компонента. Компоненты не постоянны в моем окружении, так как они удаляются при уничтожении корабля.
Это моя главная дилемма. Я также был бы признателен за любые указатели с дизайном моей реальной системы обнаружения столкновений. Это мой первый раз, когда я его взламываю (возможно, стоило немного почитать)
Заранее спасибо.





Вы должны абсолютно постараться избегать выделения памяти для вашего вектора-компонента при каждом вызове функции-получателя. Вместо этого делайте распределение как можно реже. Например, это можно сделать при изменении компонентного состава корабля или даже реже (из-за перераспределения).
Конечно, вы также можете исследовать пулы памяти, где вы заранее выделяете множество таких компонентов и помещаете в пул, чтобы вы могли выделить новый компонент в постоянное время.
В качестве общего (и приносим свои извинения, если это слишком очевидно) при выполнении такого рода обнаружения столкновений: возводите расстояния в квадрат, а не вычисляйте квадратные корни. :)
Не используйте 2D-вектор. Скорее используйте vector из point. То же самое и для обнаружения столкновений. Использование 2D-вектора здесь - неправильная структура данных.
В зависимости от содержимого функции компилятор сможет выполнить NRVO (то есть оптимизация именованного возвращаемого значения), что означает, что в оптимальном случае возврат вектора имеет накладные расходы нет, т.е. он никогда не копируется. Однако это происходит только тогда, когда вы используете возвращаемое значение вашей функции для инициализации нового экземпляра, и когда компилятор может отслеживать пути выполнения внутри функции и видеть, что для каждого пути возврата возвращается один и тот же объект. Рассмотрим следующие два:
vector<int> f(int baz) {
vector<int> ret;
if (baz == 42)
ret.push_back(42);
return ret;
}
vector<int> g(int baz) {
if (baz == 42)
return vector<int>(1, 42);
else
return vector<int>();
}
Компилятор может выполнять NRVO для вызовов f, но не для g.
Я не возражаю, но замечу, что возвращение вектора все равно не очень "C++ - иш". Templatize, возьмите итератор вывода в качестве параметра, такого как std :: copy, и если вызывающий хочет его в векторе, он может использовать std :: back_inserter, который будет встроен. По-прежнему нет накладных расходов, и вызывающие абоненты получают большую гибкость.
Итак, в этом случае код выглядит следующим образом: «template <typename IT> void f (int baz, IT it) {if (baz == 42) * it = 42;}». Вызывающий выполняет "vector <int> foo; f (42, back_inserter (foo));". Или вы можете взять итератор по неконстантной ссылке, но алгоритмы AFAIK STL этого не делают, они принимают итераторы по значению.
... конечно, все это предполагает, что вызывающие абоненты могут использовать ваши шаблоны, что в вашем собственном коде является безопасным предположением, но падает, если шаблон находится на границе API библиотеки, а ваш компилятор / компоновщик не поддерживает внешние шаблоны . Что, давайте посмотрим правде в глаза, это не так.
Ненавижу кого-то пометить. Но vector <> - совершенно неподходящий инструмент для работы! Сворачивание его собственного класса тривиально, дешево для копирования, позволяет добавлять специальные методы Vector2D и не требует постоянных действий по созданию / удалению! (Хотя ссылка NRVO была полезной.)
onebyone: алгоритмы STL не часто генерируют данные, и их интерфейс определенно не нацелен на эту задачу. Ничто в C++ не заставляет вас соблюдать это ограничение. Утверждать, что это не C++ - довольно сильно. Тем не менее, я никогда не писал код, как указано выше.
мри: что угодно. Я просто хотел указать, что 2D-массив (или 2D-вектор, т.е. предположительно вложенный вектор) - это совершенно неправильный инструмент для работы. vector<point> превосходит безусловно. Конечно, написание собственной структуры данных часто даже лучше, но так же часто это ненужно и излишне.
mree: Забудьте, что я сказал. Я только заметил, что OP, вероятно, относится к «вектору» в математическом смысле, а не в смысле C++.
Если ваш 2D-вектор просто:
class Vector2D { double x, y; };
Тогда непременно верните его! Например:
Vector2D function( ... );
Или пройдите по ссылке:
void function( Vector2D * theReturnedVector2D, ... );
Избегайте любой ценой:
vector<double> function(...);
Постоянное выделение / освобождение кучи, присущее классу Vector, является ошибкой!
Копирование собственного класса Vector2D очень дешево с вычислительной точки зрения. В отличие от Vector <>, ваш собственный класс Vector2D может включать любые методы, которые вам нравятся.
Я использовал эту функцию в прошлом для включения таких методов, как distanceToOtherPointSquared (), scanfFromCommandLineArguments (), printfNicelyFormatted () и operator [] (int).
или я должен предоставить этому компонентному объекту дополнительную частную двумерную векторную переменную, отредактировать частную переменную при вызове функции и вернуть указатель на этот объект?
Остерегайтесь многократных вызовов функций, делающих недействительными предыдущие данные. Это рецепт катастрофы!
Есть большая разница между распределением памяти в куче и в стеке. Выделение в куче, например, использование new / delete или malloc / free очень медленное. Размещение в стеке действительно довольно быстрое. Со стеком обычно медленная часть копирует ваш объект. Так что следите за вектором и т. д., Но возвращение простых структур, вероятно, нормально.
Если иногда между изменениями компонентов происходит несколько изменений компонентов, а иногда - несколько изменений между изменениями компонентов, то выделение при первом использовании может быть подходящим вариантом. В однопоточном варианте использования накладные расходы на проверку того, кэшировано ли нужное вам значение, должны быть незначительными.