Баланс C++ и Blueprints в UE4

Все в кучке: С++, Blueprints и результат http://www.rionix.com
Все в кучке: С++, Blueprints и результат http://www.rionix.com

В продолжение моего прошлого поста:

В этот раз поговорим про С++ и Blueprints и о том, как они могут повлиять на память и производительность игры. И, как обычно, категорически приветствуется обсуждение статьи в комментариях! Делимся своим опытом и становимся лучше 😁

C++ vs Blueprints

Можно ли написать игру используя только Blueprints? Можно. Можно ли написать игру используя только C++? Можно. А что тогда выбрать? А выбирать не надо, надо использовать оба языка. Главное - правильно соблюсти баланс.

Можно писать на чем-нибудь другом? Конечно: Python, C #, SkookumScript, Lua и др. Вот только, за исключением питона - это неофициальные плагины. Да и на питоне можно писать код только для редактора, но не самой игры. Это плохо? С точки зрения эпиков - С++ и блюпринты покрывают все потребности разработчиков. Я с ними согласен. Вы - решайте сами.

С++ - сложнааааа!!! В общем случае - да 😃 Если говорить об UE4, то не сложнее C # в Unity. Если не лезть глубоко внутрь исходников движка, то ужасы С++ обойдут стороной. Основная сложность любого движка заключается в его структуре, в понимании взаимодействия его модулей. UE4 не исключение.

Особенности работы движка

  • Если что-то подгружаем в С++ (ConstructorHelpers), то это будет загружено при старте модуля. Грубо говоря, в момент старта игры. Неважно, используется это или нет, создан этот объект или нет. И висеть все будет в оперативке или видеопамяти на протяжении всей работы модуля.
  • Зависимости (references). Например, меш зависит от материала(ов), а материал зависит от текстур. Когда мы грузим меш, подтягиваются и все его зависимости. Ням-ням-ням и кирпичик из 8-ми вершин, тянет за собой пару-тройку текстур в 4К.
  • Чтобы вызвать маленькую функцию из какого-нибудь блюпринта, движок загружает его весь. Благодаря зависимостям, это может потянуть за собой половину проекта, включая текстуры и звуки.
  • Блюпринты сильно медленнее С++, даже нативизированные. Часто звучат такие цифры: от 7 до 30 раз.

C++ и Blueprints

Даже если вы решили писать игру только на Blueprints, создавайте именно С++ проект. В скором времени вы убедитесь, что многие вещи гораздо проще решить с использованием С++. А переконвертировать Blueprint проект в С++ не так и просто.

Первое и основное правило - если что-то можно написать на С++, пишите на С++. Основная прелесть в том, что зависимостей в С++ нет. Нужно вызвать метод класса - не проблема, нужно скастовать к другому типу - все хорошо, кастуем. Blueprints такое не прощают.

Одно условие - не надо в конструкторе загружать ассеты. Откуда взялось такое условие? Дело в том, что для каждого AActor автоматически создается его default версия. И, естественно, для нее вызывается конструктор.

Эм... а блюпринты куда? А блюпринты грузятся только тогда, когда они нужны. И в этом их прелесть - ассеты подтянутся, только тогда, когда блюпринт начали использовать (в другом блюпринте или поместили на уровень). Поэтому, часто разбивают код так: всю логику работы выносят в базовый класс на С++, а настройки, меши, материалы и эффекты - в блюпринты-наследники. Даже если этот блюпринт-наследник будет единственным.

Дальше я буду писать только о блюпринтах, потому что не всегда получается следовать "первому и основному правилу".

Блюпринты великолепны для прототипирования. Вот только с усложнением кода, становится все сложнее разбираться в получившейся "лапше". Как только идея устаканилась, старайтесь перенести ее на С++. Анализ кода, поиск и читаемость - не лучшие стороны блюпринтов:

Надо сводить к минимуму зависимость блюпринтов друг от друга. Зависимость Controller и Character обоснована - они не могут работать друг без друга. Но, ситуация, когда А тянет за собой В, который тянет C и D и все дальше и глубже, приведет к печальным последствиям. Причем простейший "Cast to" уже введет зависимость и начнет грузить другой блюпринт! Интерфейсы или взаимодействие на уровне С++ помогут разрулить ситуацию.

Циклические зависимости могут привести проект в полностью нерабочее состояние. У меня такое тоже было... Пришлось переписывать весь код игры.

Я написал блюпринт и хочу использовать его переменную или вызвать его функцию в С++. Это возможно? Если коротко, то: Да. Однако, по словам эпиков: "Если бы вы видели этот код, то никогда не захотели бы его использовать". Так что правильный ответ: Нет. Хотите что-то использовать в С++ из блюпринтов - объявите это в С++.

Производительность блюпринтов. Ее достаточно для игровой логики. Что-то сложнее, лучше выносить в С++. Крайне не рекомендуется делать блюпринты с Tick'ом. "Тикать" лучше на С++. С другой стороны, нет ничего страшного, если блюпринт изредка включает свой тик на короткое время, или тикает не каждый кадр, а пару раз в секунду. Также стоит помнить, что Tick - это просто таймер по умолчанию. Вынос "тикающего" кода в другие таймеры или Timeline не исправит ситуацию.

Все блюпринты, унаследованные от AActor, создаются по умолчанию с включенным Tick. Это можно поправить настройками проекта (Can Blueprints Tick by Default). Но лучше всего перепроверять вручную соответствующие галочки. Ну и консольная команда "dumpticks" в помощь.

Что еще посмотреть или почитать?

Я не рассказал про hard references и soft references. Исправляюсь тут:

Отличный пример того, как правильно сбалансировать С++ и Blueprints, показан в демо-проекте Action RPG:

И советую посмотреть эти видео:

Blueprints In-depth - Part 1 | Unreal Fest Europe 2019
Blueprints In-depth - Part 2 | Unreal Fest Europe 2019

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

9393
78 комментариев

Автор продолжай ) не хватает как раз базовых статей на эту тему

11
Ответить

Я постараюсь сделать статью с подборкой на видео-туториалы и статьи для начала разработки. Если до этого, никто меня не опередит, конечно.

6
Ответить

По мне, так наоборот, для входа в процесс куча информации, а вот углубленно уже самому надо все изучать

2
Ответить

С++ в анриле не настолько сложный, насколько геморный. Апиха богатая, располагает к правильной архитектуре приложения, но все эти кучи макросов перед каждым методом, которые еще и не автокомплитятся в ide, просто выводит из себя))
Поэтому стараюсь делать так, как и пишет автор – сначала писать логику на bp (в тех случаях, когда это возможно), и лишь когда она ±фиксируется, переписывать тяжелые ее куски на плюсы.

А статья хорошая, ссылки полезные, спасибо :)

4
Ответить

Что имеешь в виду под "не автокомпилятся в ide"?

2
Ответить

Ну дак блупринты в основном расчитаны как раз на тех, кто в программирование особо вникать не хочет. Смысл советовать им юзать С++ и Блупринты, если те, кто сидит на блупринтах скорее всего не могут в С++, а те кто может, так и делает. Сложна.

2
Ответить

Иногда код на блюпринтах получается чище, понятнее и элегантнее, чем на С++.

5
Ответить