Сравнение Unity и Unreal Engine — популярная тема для дискуссий среди разработчиков игр и частый запрос у начинающих. В интернете достаточно статей на эту тему, но большинство из них сосредоточены на наиболее очевидных отличиях. Кроме того даже для них обычно не раскрывается, как эти отличия влияют на реальную ежедневную работу разработчиков.
Меня зовут Константин Усачёв и примерно 14 лет я занимаюсь разработкой игр. Я был частью команд по разработке игр как на Unity, так и на Unreal Engine. При этом с Unreal Engine опыта у меня намного больше и именно ему я отдаю своё личное предпочтение, поэтому не все моменты в этой статье будут объективными. Однако буду рад прочитать в комментариях если где-то заблуждаюсь в силу своих пробелов и почерпнуть что-то новое.
C# vs C++
Одно из явных различий между двумя движками — основной язык разработки. Для Unity это C# скрипты, а в Unreal Engine это С++.
Считается, что C++ сложнее, чем C#. Это справедливо, но тут важно отметить, что в Unreal Engine используется не совсем “честный” C++, а с большим количеством UE-специфичного сахара, который делает его использование намного проще обычного C++. Что уж говорить, даже STL там свой. Но сложность самого синтаксиса языка — не основное различие. Я бы отметил следующие практические последствия разных языков разработки, из-за которых C# в Unity выигрывает по сравнению с C++ в Unreal Engine:
C# и организация работы с кодом в Unity позволяет редактору подхватывать изменения в коде без перезапуска редактора. UE тоже поддерживает это в некотором виде, но ограниченно, в т.ч. из-за ограничений самого языка программирования. Хоть в Unity и происходит постепенная деградация скорости подхвата изменений по мере роста проекта, но на старте проекта это сильно ускоряет итерации.
Благодаря тому, что C# может автоматически инструментироваться, то профилирование и отладка кода в Unity проще и удобнее, чем в UE, где инструментировать приходится вручную, а при отладке теряется часть информации.
Уровень поддержки C# со стороны IDE качественнее, чем для C++, особенно в Visual Studio. Благодаря JetBrains Rider различия в поддержке сильно снизились, но всё равно опыт работы в IDE для C# будет приятнее.
C# поддерживает менеджер пакетов NuGet, поэтому нет необходимости хранить в репозитории большинство внешних зависимостей, и можно без труда использовать множество не-Unity-специфичных библиотек. Поэтому для C# доступно больше кода/библиотек, которые можно использовать без модификаций, чем для C++ и UE.
Встроенная поддержка синтаксисом C# асинхронности позволяет делать более простой и наглядный код при работе с асинхронными вызовами.
Для сетевых игр при использовании Unity есть возможность иметь один язык разработки для игры и для игрового бэкенда. Маловероятно что вы захотите писать игровой бэкенд на C++, а вот C# — популярный выбор. Это позволяет переиспользовать некоторые куски кода в игре и в бэкенде, которые иначе пришлось бы дублировать, а также облегчает переход разработчиков между командами.
Иными словами, проблема разного язык разработки не в том, что может понадобиться больше времени на его изучение для новичков, а в некотором постоянном оверхеде на разработку в UE по сравнению с Unity. Я намеренно не говорю тут про разницу в производительности итогового кода, про которую почему-то часто упоминают в других сравнениях, так как практическая разница при реальном использовании сильно преувеличена.
Unity Visual Scripting vs Blueprints
И в Unity, и в Unreal Engine есть возможность использовать визуальный язык для написания частей логики. В Unity раньше был сторонний пакет Bolt, теперь же он интегрирован под названием Unity Visual Scripting (UVS). В UE с первых общедоступных версий движка это Blueprint’ы. И тут Blueprint’ы оставляют UVS далеко позади.
Blueprint’ы присутствуют и поддерживаются с первых общедоступных версий UE, благодаря чему они интегрированы и являются неотъемлемой частью всех частей движка.
В интернете большое количество информации по их использованию. Порой C++ вариант выполнения каких-то действий сложнее найти, чем на Blueprint'ах..
Может показаться, что визуальный язык разработки — это выбор начинающих разработчиков, которые ещё не разобрались в “правильном” варианте разработки. Однако тут всё не настолько категорично. Есть области, в которых Blueprint’ы используются и в профессиональных проектах с большими командами:
Фаза прототипирования/препродакшна — длительный этап поиска и итерирования core-механик, когда в следующий этап попадут в основном идеи. Разработка прототипов на Blueprint’ах зачастую выходит быстрее, чем на C++.
Написание логики UI — область, где по разным причинам скриптовые языки всегда пользовались популярностью, как например Lua в игре World of Warcraft, Action Script в популярном раньше middleware для UI Scaleform, и JavaScript в UI middleware Coherent. В случае проектов на UE большая часть логики UI делается именно на блюпринтах.
Материалы, анимации, процедурный звук, behaviour tree для AI, системы частиц и многое другое делается с использованием Blueprint’ов. На самом деле не всё это — те же блюпринты, которые используются для игровой логики, но до смешения похожие вариации.
Скорее всего в любом UE проекте вы найдёте и части игровой логики, которые по каким-то причинам сделаны именно на Blueprint’ах.
Особенно наличие визуального языка разработки полезно для новичков и для дизайнеров. Я знаю классные запущенные инди-проекты, полностью сделанные на Blueprint’ах разработчиками без опыта в программировании. UE и Blueprint’ы действительно позволяют подобное.
Система организации логики
Здесь подходы Unity (не берём DOTS) и Unreal Engine схожи. У нас есть игровые сущности, логика которых настраивается путём создания и добавления компонентов логики — MonoBehaviour в Unity и ActorComponent в UE. Но есть различия, которые делают подход UE на мой взгляд чуть более удобным:
В Unity игровые сущности (Game Object’ы) могут составлять иерархию, в каждый из которых могут добавляться свои компоненты. Это с одной стороны позволяет достигать большой гибкости и выстраивать сложные композиции, а с другой стороны приводит к тому, что эти композиции получаются действительно сложными — прежде всего для дизайнеров, но в какой-то момент и для программистов.
Частично следствием предыдущего пункта является то, что в Unity распространена практика, когда в одних компонентах задаются ссылки на другие компоненты той же игровой сущности на другом уровне иерархии. Это дополнительно усложняет структуру объекта, правила построения которой становятся неочевидны без изучения кода. Можно сказать, что префабы частично решают эту проблему, т.к. позволяют программистам создавать базовый объект, который дизайнеры используют. Но именно что частично. Они не защищает дизайнеров от внесения незапланированных программистами изменений способных поломать логику объекта и не предоставляют список только разрешённых настроек.
В Unreal Engine можно создавать не только свои компоненты, но и базовую сущность, Actor в терминологии UE, в которой уже строго задавать всю структуру компонентов. При этом некоторые компоненты сами могут выстраиваться в иерархию. В результате стандартный подход в организации проекта выглядит так: программисты предоставляют дизайнерам готовую к использованию комплексную сущность, где им доступно к настройке только то, что им может понадобиться. Это сильно упрощает работу дизайнеров и взаимодействие с ними, т.к. все создаваемые объекты получаются самодокументируемыми.
Таким образом в Unity дизайнеру требуется больше времени на вникание в новый проект и сложнее поддерживать общее понимание, что и как настраивается.
Система ресурсов
Формат хранения
Unreal Engine использует бинарный формат хранения ресурсов. Все игровые ассеты хранятся на диске в бинарном виде, причём данные и метаданные в едином файле. Unity же использует текстовый YAML формат и разносит данные и метаданные по разным файлам, а в случае импортируемых ассетов наподобие fbx или png файлов использует именно их, добавляя лишь файл с метаданными. Эти незначительное на первый взгляд различие имеет следующие последствия:
Благодаря текстовому формату хранения Unity несколько удобнее для работы с системами контроля версий. В случае Git’а репозиторий будет чуть компактнее, а LFS хранилище будет медленнее разрастаться, поскольку изменение одного свойства у объекта на уровне или в массивном префабе не приводит к переотправке в репозиторий всего ассета целиком.
В Unity при конфликте изменений, когда несколько разработчиков параллельно делают изменения в одних и тех же ассетах, иногда получается эти изменения автоматически слить воедино. В случае UE автоматически не получится: необходимо будет делать это вручную.
В UE при внесении изменений в метаданные импортированного ассета, например, при изменении параметров Mipmap для текстуры, весь ассет текстуры будет перезаписан.
В Unity при просмотре истории изменений благодаря текстовым ассетам иногда можно понять суть этих изменений. В UE тоже есть возможность посмотреть дифф, но это потребует запуск редактора, что не так удобно, как web-интерфейс хостинга Git репозитория.
В Unity импортируемые ассеты остаются в изначальном формате, поэтому, скажем, просмотреть список моделей или текстур можно из проводника, и можно открыть/отредактировать их без запуска редактора Unity. В UE же необходимо будет всё делать из редактора.
В Unity можно хранить в проекте и иметь доступ из проекта к своим текстовым форматам, с которыми например кроме игры работает ещё какой-то внешний инструментарий. Например, можно хранить csv или json файлы.
Адресация
В Unity для каждого ассета генерируется дополнительный файл с его метаданными, в том числе с его глобально уникальным идентификатором, и именно по этому идентификатору происходит адресация всех ресурсов.
В UE же адресация ассетов происходит по полному пути к файлу. Это приводит к тому, что в UE нельзя манипулировать ассетами за пределами редактора, так как это может привести к поломанным ссылкам. При перемещении ассетов в редакторе на его месте создаётся так называемый редиректор, который позволяет не вносить изменения во все остальные ресурсы, ссылающиеся на него, что не очень удобно — это часто путает новичков. Можно попросить редактор удалить этот редиректор и внести изменения в зависимые ресурсы, что увеличивает количество затрагиваемых файлов при изменениях, и в паре с бинарными ассетами добавляет неудобств для совместной разработки в большой команде.
В Unity дополнительный файл метаданных тоже иногда приводит к путанице. Например, когда его забывают отправить в репозиторий или когда ассет дублируют вместе с мета-файлом не внутри редактора и происходит дублирование идентификатора.
В заключение этой части можно сказать, что ресурсная система Unity удобнее для работы разработчиков, интеграции с внешним инструментарием и для инфраструктуры разработки.
Производительность редактора
В сравнении рассматриваемых игровых движков Unreal Engine часто выделяется как более производительный и предназначенный для более ресурсоёмких игр. Не возьмусь утверждать по этому моменту, но что точно — UE гораздо более требователен к производительности рабочего места, чем Unity. Хоть на объёмном проекте производительность Unity редактора начинает деградировать в отличие от UE, в целом для разработки на Unreal Engine потребуется более мощный компьютер. Например, RTX 3060 уже не хватает для комфортной работы в редакторе UE над VR проектом при запущенной трансляции картинки на шлем. В Unity такая связка прекрасно работает на том же железе.
CI
Как ни странно, выбранный движок влияет и на CI. Докер-образ для сборки Unity проекта занимает примерно 8 GB и доступен в готовом виде, для UE же докер образ занимает 60-70 GB. К тому же, доступный от Epic Games образ вам скорее всего не подойдёт, так как он позволяет делать только сборку под Linux, а для Android, например, уже нет. Поэтому придётся собирать свой, на что уйдёт немало времени, поскольку это не особо документированная область, а сама сборка займёт несколько часов и 350GB места на билдере. И это тот случай, когда размер имеет значение, так как он напрямую влияет на организацию и стоимость CI. Стоит отметить, что с другой стороны на CI-пайплайны влияет необходимость лицензий для Unity, что тоже несколько усложняет их.
Поддержка Multiplayer
Это основное на мой взгляд преимущество Unreal Engine, который в отличие от Unity изначально создавался как игровой движок для клиент-серверных игр. И это отложило отпечаток буквально на всех его частях, что сделало разработку клиент-серверных игр на UE намного проще, а результат качественнее. И тут речь не столько про саму возможность разработки мультиплеерной игры, которая в Unity безусловно есть, сколько про встроенную разностороннюю поддержку этого. Из практических примеров того, что можно в UE и нельзя в Unity, можно выделить следующее:
Запустить сервер и несколько подключенных к нему клиентов игры по нажатии одной кнопки.
Остановиться во время тестирования на брейкпоинте с остановкой как клиентов, так и сервера, поскольку все они могут выполняться в одном потоке, отладить код и вернуться в игру, не получив отключение клиентов по таймауту.
В тесте запустить клиенты и сервер и манипулировать ими с искусственным управлением течением времени и тиков миров, надёжно тестируя клиент-серверную логику.
Весь конкретный сетевой функционал, конечно, можно реализовать и на Unity, но потребуется изучить несколько альтернативных вариантов применяемых для этого middleware. Информацию придётся искать по крупицам, так как существует большая фрагментация по решениям, а заметную часть придётся реализовывать самостоятельно. В UE сетевую игру разрабатывать будет гораздо проще, имея единый подход, встроенный инструментарий и доступную из коробки надёжную реализацию основных механик.
Foundation Classes
В моём понимании Unity считается простым в том числе потому, что он не предоставляет большого количества базовых игровых механик. Например, если вам нужно сделать перемещение игрока, то вы сами придумываете, как он будет двигаться, где разместить эту логику, кто будет отвечать за обработку инпута и за камеру. Что-то можно найти в магазине, но примерно всё в магазинах (это справедливо и для Unity, и для Unreal Engine) невысокого качества, и после интеграции приходится поддерживать самостоятельно.
Это делает сам старт разработки игры на Unity несколько быстрее, чем на UE, особенно когда нет опыта, поскольку нет необходимости в изучении существующих концепций. Однако на средней стадии разработки игры это приводит к большим сложностям с постоянным преодолением сопротивления неоптимальных прошлых принятых решений — особенно когда дело доходит до сетевых игр, в которых UE, как изначально заточенный под них движок, уже позаботился о многих распространённых потребностях типовых клиент-серверных игр.
Testability
Также одно из заметных преимуществ Unreal Engine, которое влияет на конечное качество игры — это возможность писать полноценные тесты. Из-за используемых подходов в Unity невозможно писать не только клиент-серверные тесты, но и нормально тестировать поведение локальных объектов. Основная причина — это большое количество завязок на статические переменные и синглтоны, которые не позволяют запустить несколько экземпляров карты одновременно или незаметно для тестируемого кода манипулировать течением времени. Можно ускорить течение времени, но всё равно останется завязка на реальное время, что делает тесты нестабильными и сложными для поддержки.
Также тесты в Unity зачастую требуют дополнительной специальной поддержки тестируемости в логике. В случае UE это скорее исключение. Но возможно я просто не сталкивался с хорошей организацией тестов в Unity.
Заключение
Особого заключения нет. Оба движка имеют право на жизнь, и это очень здорово, что есть выбор и конкуренция. Мои предпочтения на стороне Unreal Engine как полноценного движка для разработки игр со всем необходимым и без потребности искать сторонние решения для каких-то частей. Хочется почитать в комментариях другие мнения и узнать, если я где-то заблуждаюсь.
Комментарии (13)
QNikki
24.06.2024 13:30Можно подробнее рассказать про тесты на UE? Работаю в юнити, и мы используем Unity Test Framework , едитор тесты для валидации разных типов ассетов, и юнит тестирования систем. и плей моды тесты для более чего сложно, когда нужен мир, тестирования взаимодействия юнити и систем и тд. И это довольно просто
DrVirtual Автор
24.06.2024 13:30Пример теста, который можно сделать на UE:
1. Создаём сервер с какой-то картой
2. Подключаем два клиента
3. Проверяем что на сервере и на обоих клиентах появились оба аватара
4. Первый аватар прожимает абилку невидимости
5. Проверяем, что на втором клиенте исчезла информация про первого аватара
6. Ждём окончания действия абилки
7. Проверяем, что на втором клиенте вновь появилась информация про первого аватара
Это про сетевой тест, но насколько я понял, в Unity и с несетевыми тестами есть проблемы (выдуманный тест чисто для примера):
1. Создаём мир с какой-то картой
2. Спавним игрока
3. Проверяем, что каждую минуту игрок получает какое-то количество очков за то что он жив
4. Проверяем, что через 15 минут матч завершается
Как понимаю в Unity потребуется магия с ускоренным течением времени, когда реальное время выполнение теста всё равно будет фиксированным и не будет зависеть от производительности билдера. В случае UE все тесты выполняются за миллисекунды/максимум секунды если там прокручивается несколько минут игрового времени. Но может в Unity тоже можно прокручивать время детерминировано и как хочешь и я просто не нашёл как это сделать.xorx
24.06.2024 13:30Это про сетевой тест, но насколько я понял, в Unity и с несетевыми тестами есть проблемы (выдуманный тест чисто для примера):
Нет такой проблемы. В Unity тебя никто не заставляет привязываться к реальному времени. Я бы наоборот сказал, что в Unreal сложнее, так как в C# инструментарий для тестирования более развит.
UranusExplorer
24.06.2024 13:30Уровень поддержки C# со стороны IDE качественнее, чем для C++, особенно в Visual Studio. Благодаря JetBrains Rider различия в поддержке сильно снизились, но всё равно опыт работы в IDE для C# будет приятнее.
А чего конкретно больше всего вам не хватает в студии для плюсов в сравнении с шарпом?
xorx
24.06.2024 13:30Да любой рефактор. Переименование перемнной в плюсах - боль, в шарпе одним кликом за милисекунды. Всё что выходит за пределы файлы в плюсах заставляет студию задуматься минут на 5-10 в скольконибуть большом проекте.
Darell_Ldark
Вот тут бы поподробнее, на самом деле. Почему преувеличена? На чем базируется такое мнение? Возможно, есть ссылка на бенчмарки для сравнения?
Понятно, что в общей картине язык внесет очень небольшой импакт на производительность, но все же.
jonic
Шарп сильно проще чем плюсы анрила с своими макросами и препроцесосром. Фактически - у Анрила своя вариация плюсов с сахаром и правилами.
Darell_Ldark
Я согласен с тем, что шарпы проще (но с некоторыми оговорками), но мне интересно больше про последнее, где говорится, что разница в использовании преувеличена. Звучит просто странно, ведь C# требует рантайм, а C++ нет. Ссылку бы на источник таких сведений.
jonic
А, в этом плане. Я думаю рантайм маленький. А если говорить про игры, то игры в целом на каждый кадр делают дофига операций и приходится играть с оптимизацией. Тот же Юнити вообще для мобилок топ, а там железо сами понимаете.. в итоге узкое место всегда производительность gpu.
DrVirtual Автор
Речь про эффект на производительность всей игры. Вы сами ответили в целом, что пользовательский код - меньшая часть всех вычислений.
Поэтому на практике в рамках оптимизации обычно приходится оптимизировать не скорость выполнения конкретных кусков пользовательского кода, а снижать необходимое количество вызовов к API движка. Но в некоторых статьях пишут, что C++ быстрее чем C#, создавая впечатление, что поэтому игра на UE будет заведомо производительнее - это вот как раз преувеличение.
Darell_Ldark
Тогда понял, что Вы имели в виду. Соглашусь, это и правда может создавать некорректное впечатление. Но Ваша формулировка тоже несколько размыта и, вероятно, лучше ее несколько уточнить.
Спасибо за ответ!
seldemirov
Производительность c# очень сильно зависит от используемого компилятора.
В тренде по переходу на .net core есть хорошее сравнение от разраба из MS
https://forum.unity.com/threads/unity-future-net-development-status.1092205/page-50
Там же хорошее обсуждение по производительности компилируемого кода в целом
express
Снижение производительности можно узнать, сравнив системные требования игр. Какая-нибудь условная Firewatch будет предъявлять примерно те же системные требования, что и What Remains of Edith Finch при несравнимо худшей графике. Какой-то сногсшибательной по графике игры на Unity вообще не знаю. Если знаете - поделитесь.