
Это пятая статья в серии, которая раскрывает советы по оптимизации для ваших проектов в Unity. Используйте их в качестве руководства для работы на более высоких частотах кадров с меньшими ресурсами. После того как вы попробуете эти лучшие практики, обязательно ознакомьтесь с другими страницами в серии:
Физика может создавать сложный игровой процесс, но это имеет свою цену производительности. Как только вы узнаете эти затраты, вы сможете настроить симуляцию для их адекватного управления. Используйте эти советы, чтобы оставаться в пределах целевой частоты кадров и создавать плавное воспроизведение с помощью встроенной физики Unity (NVIDIA PhysX).
Смотрите наши последние руководства по оптимизации для разработчиков и художников Unity 6:
Меши, используемые в физике, проходят процесс, называемый готовкой. Это подготавливает меш, чтобы он мог работать с физическими запросами, такими как трассировка лучей, контакты и так далее.
У MeshCollider есть несколько ОпцийГотовки, которые помогут вам проверить меш для физики. Если вы уверены, что вашему мешу не нужны эти проверки, вы можете отключить их, чтобы ускорить время готовки.
В ОпцияхГотовки для каждого MeshCollider просто снимите отметки с EnableMeshCleaning, WeldColocatedVertices и CookForFasterSimulation. Эти опции ценны для процедурно сгенерированных мешей во время выполнения, но могут быть отключены, если ваши меши уже имеют правильные треугольники.
Также, если вы нацелены на ПК, убедитесь, что у вас включена опция Использовать Быструю Среднюю Фазу. Это переключает на более быстрый алгоритм из PhysX 4.1 во время средней фазы симуляции (что помогает сузить небольшой набор потенциально пересекающихся треугольников для физических запросов).
Узнайте больше в документации по ОпциямГотовки.

Если вы генерируете меши процедурно во время игры, вы можете создать Mesh Collider во время выполнения. Однако добавление компонента MeshCollider непосредственно к мешу готовит/выпекает физику в основном потоке. Это может потреблять значительное время ЦП.
Используйте Physics.BakeMesh, чтобы подготовить меш для использования с MeshCollider и сохранить выпеченные данные вместе с самим мешем. Новый MeshCollider, ссылающийся на этот меш, будет повторно использовать эти предварительно выпеченные данные (вместо того, чтобы снова выпекать меш). Это может помочь сократить время загрузки сцены или время инстанцирования позже.
Чтобы оптимизировать производительность, вы можете перенести обработку мешей на другой поток с помощью C# job system.
Смотрите этот пример для получения подробной информации о том, как запекать меши на нескольких потоках.

В Настройках Игрока проверьте Предварительная запекание коллизий всякий раз, когда это возможно. Мы также рекомендуем просмотреть настройку Матрицы коллизий, чтобы убедиться, что объекты игрока и игровые механики находятся в правильных слоях.
Удаление обратных вызовов из триггеров для ненужных слоев может быть большим выигрышем, поэтому постарайтесь упростить вашу Матрицу коллизий слоев. Вы можете редактировать ваши Настройки физики через Настройки проекта > Физика.
Узнайте больше в документации по Матрице коллизий.

Физические движки работают, выполняя фиксированный временной шаг. Чтобы увидеть фиксированную частоту, с которой работает ваш проект, перейдите в Правка > Настройки проекта > Время.
Поле Фиксированный временной шаг определяет временной интервал, используемый на каждом шаге физики. Например, значение по умолчанию 0,02 секунды (20 мс) эквивалентно 50 fps или 50 Гц.
Поскольку каждый кадр в Unity занимает переменное количество времени, он не идеально синхронизирован с физической симуляцией. Движок считает до следующего временного шага физики. Если кадр работает немного медленнее или быстрее, Unity использует прошедшее время, чтобы знать, когда запустить физическую симуляцию в нужный временной шаг.
В случае, если кадр занимает много времени для подготовки, это может привести к проблемам с производительностью. Например, если ваша игра испытывает всплеск (например, создание множества объектов игры или загрузка файла с диска), кадр может занять 40 мс или более для выполнения. С учетом стандартного фиксированного временного шага в 20 мс это приведет к тому, что две физические симуляции будут выполняться в следующем кадре, чтобы "наверстать" переменный временной шаг.
Дополнительные физические симуляции, в свою очередь, добавляют больше времени для обработки кадра. На платформах низкого уровня это потенциально приводит к спирали ухудшения производительности.
Следующий кадр, который занимает больше времени для подготовки, также увеличивает количество накопленных физических симуляций. Это приводит к еще более медленным кадрам и еще большему количеству симуляций, которые нужно выполнить за кадр. Результат — все хуже и хуже производительность.
В конечном итоге время между обновлениями физики может превысить максимальный допустимый временной шаг. После этого предела Unity начинает пропускать обновления физики, и игра начинает подтормаживать.
Чтобы избежать проблем с производительностью в физике:
При необходимости вручную симулируйте шаг физики, выбрав режим симуляции во время фазы обновления кадра. Это позволяет вам контролировать, когда выполнять шаг физики. Передайте Time.deltaTime в Physics.Simulate, чтобы синхронизировать физику с временем симуляции. Этот подход может вызвать нестабильность в физической симуляции в сценах с сложной физикой или сильно изменяющимися временными интервалами кадров, поэтому используйте его с осторожностью.
Узнайте больше в документации Physics.Simulate.

Физический движок Unity работает в два этапа:
Настройка по умолчанию для широкой фазы Sweep and Prune BroadPhase (Правка > Настройки проекта > Физика > Тип широкой фазы) может генерировать ложные срабатывания для миров, которые в целом плоские и имеют много коллайдеров. Если ваша сцена большая и в основном плоская, избегайте этой проблемы и переключитесь на Автоматическую обрезку по коробке или Многократную обрезку по коробке. Эти параметры делят мир на сетку, где каждая ячейка сетки выполняет сметку и обрезку.
Многократная обрезка по коробке позволяет вам вручную задать границы мира и количество ячеек сетки, в то время как Автоматическая обрезка по коробке рассчитывает это за вас.
Смотрите полный список свойств физики здесь.

Если вы хотите более точно смоделировать конкретное физическое тело, увеличьте его Rigidbody.solverIterations.
Это переопределяет Physics.defaultSolverIterations, который также можно найти в Правка > Настройки проекта > Физика > Значения по умолчанию для итераций решателя.
Чтобы оптимизировать ваши физические симуляции, установите относительно низкое значение в defaultSolveIterations проекта. Затем примените более высокие пользовательские значения Rigidbody.solverIterations к отдельным экземплярам, которым требуется больше деталей.
Получите больше информации о Rigidbody.solverIterations.

По умолчанию Unity не синхронизирует изменения в Transforms с физическим движком автоматически. Вместо этого он ждет следующего обновления физики или пока вы вручную не вызовете Physics.SyncTransforms. Когда это включено, любой Rigidbody или Collider на этом Transform или его дочерних объектах автоматически синхронизируется с физическим движком.
Когда вручную синхронизировать
Когда autoSyncTransforms отключен, Unity синхронизирует преобразования только перед шагом физической симуляции в FixedUpdate или когда это явно запрашивается через Physics.Simulate. Вам может потребоваться выполнить дополнительные синхронизации, если вы используете API, которые читают напрямую из физического движка между изменениями Transform и обновлением физики. Примеры включают доступ к Rigidbody.position или выполнение Physics.Raycast.
Лучшие практики производительности
Хотя autoSyncTransforms обеспечивает актуальные физические запросы, это влечет за собой затраты на производительность. Каждый вызов API, связанный с физикой, заставляет выполнять синхронизацию, что может ухудшить производительность, особенно при множественных последовательных запросах. Следуйте этим лучшим практикам:
Узнайте больше о Physics.SyncTransforms.

Массивы контактов, как правило, значительно быстрее, и поэтому общая рекомендация - использовать их, а не повторно использовать коллизионные обратные вызовы, однако учитывайте следующее, если у вас есть конкретный случай использования для этого.
Обратные вызовы MonoBehaviour.OnCollisionEnter, MonoBehaviour.OnCollisionStay и MonoBehaviour.OnCollisionExit принимают экземпляр коллизии в качестве параметра. Этот экземпляр коллизии выделяется в управляемой куче и должен быть собран сборщиком мусора.
Чтобы уменьшить количество создаваемого мусора, включите Physics.reuseCollisionCallbacks (также найдено в Настройки проектов > Физика > Повторное использование коллизионных обратных вызовов). При активном использовании Unity назначает только одну пару столкновений для каждого обратного вызова. Это уменьшает количество отходов для сборщика мусора и улучшает производительность.
Общая рекомендация - всегда включать повторное использование обратных вызовов столкновений для повышения производительности. Вы должны отключать эту функцию только для устаревших проектов, где код зависит от отдельных экземпляров класса Collision, что делает нецелесообразным хранение отдельных полей.
Узнайте больше о Physics.reuseCollisionCallbacks.

Статические коллайдеры - это GameObjects с компонентом Collider, но без Rigidbody.
Обратите внимание, что вы можете перемещать статический коллайдер, вопреки термину "статический". Для этого просто измените положение физического тела. Накопите изменения позиции и синхронизируйте перед обновлением физики. Вам не нужно добавлять компонент Rigidbody к статическому коллайдеру только для того, чтобы переместить его.
Однако, если вы хотите, чтобы статический коллайдер взаимодействовал с другими физическими телами более сложным образом, дайте ему кинетический Rigidbody. Используйте Rigidbody.position и Rigidbody.rotation, чтобы перемещать его вместо доступа к компоненту Transform. Это гарантирует более предсказуемое поведение со стороны физического движка.
Примечание. Если отдельный статический коллайдер 2D нужно переместить или перенастроить во время выполнения, добавьте компонент Rigidbody 2D и установите его в тип тела Статический, так как имитировать коллайдер 2D быстрее, когда у него есть собственный Rigidbody 2D. Если группе коллайдеров 2D нужно переместиться или перенастроиться во время выполнения, быстрее сделать так, чтобы все они были дочерними элементами одного скрытого родительского Rigidbody 2D, чем перемещать каждый GameObject по отдельности.
Получите больше информации о Rigidbodies.
Чтобы обнаружить и собрать коллайдеры в 3D проектах на определенном расстоянии и в определенном направлении, используйте лучевые запросы и другие физические запросы, такие как BoxCast. Обратите внимание, что
Физические запросы, которые возвращают несколько коллайдеров в виде массива, такие как OverlapSphere или OverlapBox, должны выделять эти объекты в управляемой куче. Это означает, что сборщик мусора в конечном итоге должен собрать выделенные объекты, что может снизить производительность, если это произойдет в неподходящее время.
Чтобы уменьшить эту накладную работу, используйте версии NonAlloc этих запросов. Например, если вы используете OverlapSphere для сбора всех потенциальных коллайдеров вокруг точки, используйте OverlapSphereNonAlloc вместо этого.
Это позволяет вам передать массив коллайдеров (параметр результатов) для использования в качестве буфера. Метод NonAlloc работает без генерации мусора. В противном случае он функционирует как соответствующий метод с выделением памяти.
Обратите внимание, что вам нужно определить буфер результатов достаточного размера при использовании метода NonAlloc. Буфер не увеличивается, если он исчерпывает пространство.
2D Physics
Обратите внимание, что вышеуказанный совет не применим к запросам физики 2D, потому что в системе физики 2D Unity методы не имеют суффикса "NonAlloc". Вместо этого все методы физики 2D, включая те, которые возвращают несколько результатов, предоставляют перегрузки, которые принимают массивы или списки. Например, в то время как система физики 3D имеет методы, такие как RaycastNonAlloc, 2D эквивалент просто использует перегруженную версию Raycast, которая может принимать массив или List в качестве параметра, например:
var results = new List();
int hitCount = Physics2D.Raycast(origin, direction, contactFilter, results);
Используя перегрузки, вы можете выполнять запросы без выделения памяти в системе физики 2D без необходимости в специализированных методах NonAlloc.
Узнайте больше в документации метода NonAlloc.
Вы можете выполнять запросы raycast с помощью Physics.Raycast. Однако, если у вас есть большое количество операций raycast (например, вычисление линии видимости для 10,000 агентов), это может занять значительное количество времени ЦП.
Используйте RaycastCommand, чтобы пакетировать запрос с использованием системы заданий C#. Это разгружает работу с основного потока, чтобы raycast могли происходить асинхронно и параллельно.
Смотрите пример в документации RaycastCommands.
Используйте окно отладки физики (Window > Analysis > Physics Debugger), чтобы помочь устранить любые проблемы с коллайдерами или несоответствия. Это показывает индикатор с цветовой кодировкой объектов игры, которые могут сталкиваться друг с другом.
Для получения дополнительной информации смотрите документацию отладчика физики.


Найдите больше лучших практик и советов в центре лучших практик Unity. Выберите из более чем 30 руководств, созданных экспертами отрасли, инженерами и техническими художниками Unity, которые помогут вам эффективно разрабатывать с помощью инструментов и систем Unity.