Ничего себе. Существует много ошибок в том коде, который вы опубликовали (и, возможно, код, который вы не опубликовали) Вот что вам нужно сделать, чтобы улучшить производительность, примерно в порядке убывания важности / необходимости:
Измерение производительности. На самом базовом уровне счетчик частоты кадров (или, что еще лучше, счетчик времени кадров). Вы хотите проверить, что вы делаете вещи лучше.
Не выделяйте память во время игрового цикла. Лучший способ проверить, действительно ли вы - это использовать CLR Profiler . Хотя вы, возможно, не используете new
(для выделения class
типов, structs
в порядке), меня не удивит, если большая часть этого LINQ выделяет память за кулисами.
Обратите внимание, что ToString
выделит память. Существуют способы выделения номеров (используя StringBuilder
), если они вам нужны.
Эта статья дает больше информации.
Не используйте LINQ. LINQ - это простой и удобный и абсолютно не самый быстрый и не самый эффективный способ управления коллекциями.
Используйте подход, основанный на данных. Ключевая идея, лежащая в основе подхода, управляемого данными, заключается в поддержании когерентности кэша ( больше информации ). То есть: все ваши Bullet
данные линейно хранятся в памяти. Чтобы сделать это, убедитесь, что Bullet
является struct
, и вы храните их в List<Bullet>
. Это означает, что когда один Bullet
загружается в кэш ЦП, он приносит с собой другие (память загружается в кэш большими блоками), сокращая время, которое ЦП тратит на ожидание загрузки памяти.
Чтобы быстро удалить маркеры, замените тот, который вы удаляете, последним маркером в списке, а затем удалите последний элемент. Это позволяет удалять элементы, не копируя большую часть списка.
Используйте SpriteBatch
для повышения производительности. Сделайте отдельную партию спрайтов (блок Begin()/End()
) для ваших пуль. Используйте SpriteSortMode.Deferred
- это самый быстрый режим. Делать сортировку (как подразумевается вашим CurrentDrawDepth
) медленно! Убедитесь, что все ваши пули используют одну и ту же текстуру (при необходимости используйте атлас текстуры). Помните, что пакетирование - это только повышение производительности, если последовательные спрайты имеют общую текстуру. ( Подробнее )
Если вы используете SpriteBatch
, тогда , вероятно, будет быстрее отрисовывать все ваши спрайты, а затем позволить графическому процессору отбирать их, если они не на экране.
(Необязательно) Ведение отдельного списка для каждого поведения . Это уменьшает количество ветвлений в вашем коде и может потенциально сделать сам код (т. Е. Инструкции, а не данные) более когерентным. В отличие от вышеперечисленных пунктов, это даст лишь небольшое улучшение производительности, поэтому используйте его только в случае необходимости.
(ПРИМЕЧАНИЕ. Помимо этого, эти изменения сложно реализовать, они затруднят чтение вашего кода и даже могут сделать его медленнее. Внедряйте их только в случае крайней необходимости и измеряйте производительность.)
(Необязательно) Вставьте свой код в код. Как только вы начнете получать тысячи кодов, вам может потребоваться встроить свой код (удалить вызовы методов), чтобы снизить производительность. Компилятор C # не встроен, а JIT делает это только немного, поэтому вам нужно вставить вручную. Вызовы методов включают в себя такие вещи, как операторы +
и *
, которые вы можете использовать для векторов - их вставка улучшит производительность.
(Необязательно) Используйте пользовательский шейдер. Если вы хотите еще большей производительности, чем простое использование SpriteBatch
, напишите пользовательский шейдер, который берет ваши данные Bullet
и рассчитывает как можно больше на GPU .
(Необязательно) Сделайте ваши данные еще меньше и (если возможно) неизменными. Сохраните ваши начальные условия (положение, направление, метку времени) в вашей структуре Bullet
. Затем используйте основные уравнения движения , чтобы вычислить ток положение / скорость / и т. Д. Только по мере необходимости. Вы можете часто получать эти расчеты бесплатно, поскольку у вас, вероятно, неиспользуемое время процессора, пока оно ожидает память.
Если ваши данные неизменны, то вы можете избежать передачи их на GPU каждый кадр! (Если вы добавляете / удаляете маркеры, вам придется обновлять их на GPU в этих кадрах).
Если бы вы реализовали все эти элементы, я думаю, вы могли бы получить до 7 миллионов пуль на хорошей машине. Хотя это, вероятно, оставит немного процессорного времени до конца вашей игры.