Unity 6.3
0 онлайн 78 гостей 3 в системе
Вход
Программирование в Unity Шаг 122 из 210

Unity передовые методы программирования

Существуют некоторые уникальные особенности среды программирования Unity, которые требуют дополнительного внимания при написании кода по сравнению со стандартными C#/.NET проектами. Ниже приведено резюме ключевых вопросов, которые следует учитывать при написании кода для Unity приложений, вместе с лучшими практиками, чтобы помочь вам избежать общих ловушек.

Unity Жизненный цикл объекта и ссылки

При написании C# в Unity, будьте осторожны при сравнении объектов на равенство с другими объектами или с нулем. Для типов, наследующих от UnityEngine.Object, Unity использует пользовательскую версию C# операторы равенства и неравенства. Это означает проверку нуля myGameObject == null может оценить true (и наоборот myGameObject != null может оценить false) даже если myGameObject технически обладает действительным C# Дополнительные сведения о особенностях этого поведения см. в разделе «Сравнение объектов». Операторы равенства.

Настраиваемое поведение равенства и жизненный цикл объекта Unity имеют несколько последствий для вашего кода:

  • В обстоятельствах, когда вы хотите быть уверены в исключении уничтоженных объектов из вашей проверки, убедитесь, что вы используете if (obj == null), а не ReferenceEquals для объектов Unity.
  • В тех случаях, когда вы хотите проверить наличие нулевых ссылок на C#, используйте ReferenceEquals или сначала перенесите в System.Object.
  • При сравнении двух объектов Unity на равенство, помните, что obj1 == obj2 может возвращать true, даже если обе ссылки являются разными объектами C#, если один или оба были уничтожены и воссозданы, например через Undo.
  • Пользовательский оператор равенства медленнее, чем стандартный C#. Обычно это не проблема, но помните об этом при масштабировании и в горячих путях.
  • Не кэшируйте компоненты через разгрузки сцен без охраны, потому что они могут быть уничтожены, но остаются как объекты оболочки C# без неуправляемого аналога.
  • Не храните сильные ссылки на крупные ассеты в статических полях: такие ссылки сохраняются между сценами и мешают выгрузке.
  • При уничтожении объектов, Object.DestroyImmediate доступны только Редактору, так что используйте Object.Destroy во время выполнения и оставьте Unity запланировать уничтожение.

Избегайте C# финализаторов

Не используйте C# финализаторы в коде времени выполнения, по следующим причинам:

  • Они работают на отдельных потоках финализатора, и Unity APIs обычно требуют главного потока.
  • Они работают недетерминистически, что приводит к непредсказуемому поведению.
  • Они могут вообще не запускаться, если только приложение не собирает мусор и не ждет.
  • Исключения, возвращаемые финализаторами, могут привести к остановке приложения, если они не обрабатываются специально.
  • Само их существование увеличивает накладные расходы мусорщиков.

Накладные расходы и ассигнования на сбор мусора

Одним из наиболее важных рисков производительности, о которых следует помнить в Unity приложениях, является код выполнения, который выделяет память и увеличивает нагрузку сборщика мусора, особенно в горячих путях.

Чтобы избежать этого, примените следующие методы кодирования:

Более подробные инструкции и примеры этих проблем см. в Оптимизация кода для управляемой памяти.

Сведения о отслеживании и сокращении нагрузки сборщика мусора см. в Управляемая память.

MonoBehaviour Оптимизация цикла обновления

Традиционный подход во многих проектах Unity — использовать компоненты-скрипты MonoBehaviour, чтобы регулярно обновлять состояние игры через встроенные обратные вызовы, такие как MonoBehaviour.Update, MonoBehaviour.FixedUpdateи MonoBehaviour.LateUpdate, которые обычно запускаются много раз в секунду.

Это простая модель, которая может хорошо работать при правильном использовании, но у нее есть некоторые ключевые риски производительности, которые обычно поражают неопытных разработчиков:

  • Реализация по умолчанию Unity на кадр или других регулярных функций событий может плохо масштабироваться. Update функции влекут небольшие накладные расходы на внутреннее управление Unity и взаимодействие с нативным слоем. Когда таких скриптов MonoBehaviour много, суммарные издержки могут заметно сказаться на производительности.
  • Тот факт, что встроенные обновления запускаются очень часто, делает их горячим кодовым путем и увеличивает эффект любых неэффективных, памятоемких операций, которые вы размещаете в них. Общей неправильной схемой неопытных пользователей является наличие многих MonoBehaviour скриптов с Update функциями, которые запускаются без необходимости большую часть времени, или которые ненужны памятоемкими, когда они запускаются.

Для уменьшения этих рисков рассмотрите следующие варианты:

  • Для лучшей масштабируемости при большом числе сущностей рассмотрите возможность перевода проекта на архитектуру, ориентированную на данные, с использованием Entity Component System (ECS) Unity.
  • Если вы используете архитектуру на основе MonoBehaviour:

Безопасность потока

В то время как Unity имеет многопоточность, основное время выполнения является однопоточной и большинство APIs в пространствах имен UnityEngine и UnityEditor могут быть вызваны только из главного потока. Не ссылайтесь на GameObjects, Transforms, Components или asset APIs из фоновых потоков. Никогда не используйте await с Task.Result или Task.Wait на главном потоке, так как это приводит к тупикам.

При работе с по сути асинхронными и долговременными операциями, Unity обеспечивает Awaitable класс как специфичную для Unity альтернативу .NET Task. Awaitable использует группирование объектов для сокращения распределений и знает о специфических для Unity концепциях, таких как Update и FixedUpdate, что позволяет вам await задачи и запланировать их возобновление в определенных точках в цикле Player. Дополнительные сведения см. в Асинхронное программирование с помощью класса Awaitable.

Для более короткой, но более вычислительно интенсивной параллельной работы, Unity обеспечивает система работы, которые могут быть Burst Составлено. Для получения дополнительной информации см. Написать многопоточное программное обеспечение с помощью системы заданий.

Соображения, касающиеся компиляции

Для оптимальной производительности важно думать не только о том, как вы пишете код, но и о том, как он компилируется. Наивная компиляция кода вместо активного определения того, для каких контекстов определенные исходные файлы или области вашего кода актуальны, налагает следующие издержки:

  • Увеличение размера сборки, если в нее включен ненужный код.
  • Время, затраченное на компиляцию и перекомпиляцию для применения изменений. Это особенно может повлиять на время итерации в Редакторе.
  • Потенциальные ошибки во время выполнения, если включен неподходящий код для данной платформы или контекста.

Unity предоставляет несколько механизмов, помогающих вам контролировать, какие части вашего кода компилируются для различных платформ и контекстов:

  • Вы можете использовать определения сборок для группировки исходных файлов в сборки, которые могут быть скомпилированы отдельно. Это позволяет изолировать код только для редактора от кода времени выполнения и код для конкретной платформы от межплатформенного кода. Дополнительные сведения см. в Организация скриптов в сборки.
  • Вы можете использовать #if директивы с скриптовые символы исключить определенные области кода из компиляции в зависимости от целевой платформы или контекста. Например, защищайте код только для редактора за #if UNITY_EDITOR для исключения его из сборок в режиме выполнения. Дополнительные сведения о различных методах Unity предложения для условного включения или исключения кода, см. Условная компиляция.
  • Ключевая концепция, специфическая для Unity, заключается в следующем: домен перезагрузка в редакторе при входе в режим Play и выходе из него, а также при перекомпиляции скриптов. Перезагрузка домена занимает время и увеличивает длительность итераций при написании и проверке кода в редакторе. Чтобы ускорить итерации, перезагрузку домена при входе в Play можно отключить, но тогда сбрасывать статическое состояние придётся вручную. Подробнее см. Перезагрузка кода и сцены при входе в режим воспроизведения.

Инструменты анализа Unity

Unity предоставляет множество инструментов, которые помогут вам выявить узкие места и написать более производительный код. Project Ревизор инструмент может проанализировать код вашего проекта, чтобы выявить общие проблемы производительности и предложить исправления. Profiler может помочь вам выявить узкие места производительности во время выполнения в вашем коде, предоставляя подробную информацию об использовании CPU и GPU, выделении памяти и многом другом. Вы также можете создать анализаторы Roslyn для обеспечения стандартов кодирования и выявления проблем производительности, специфичных для вашего проекта.

Дополнительные сведения о создании пользовательских анализаторов Roslyn и генераторов источников см. в Анализаторы Roslyn и генераторы источников.

Для получения дополнительной информации о набор инструментов анализа Unity, см. Оптимизация.

Дополнительные ресурсы