Статья является переводом странички с официальной документации UE4, которая на данный момент является удалённой. То есть, пока страничка снова не появится в документации (этого уже давно не происходит), данный контент является эксклюзивным и только на XYZ Media 🙂 .
Blueprint классы, так же, как и C++ классы, нужно скомпилировать, прежде чем использовать в игре. Чтобы запустить процесс компиляции, преобразование графов и свойств в класс, нужно нажать кнопку Compile в Blueprint editor.
У каждой ноды в blueprint есть свой обработчик называемый Node Handler’ом.
FKismetCompilerContext — Класс, выполняющий работу по компиляции. Для каждой компиляции создается новый экземпляр. Хранит ссылку на компилируемый blueprint класс.
FKismetFunctionContext — Содержит информацию для компиляции одной функции, например ссылку на связанный blueprint, свойства и сгенерированную UFunction.
FNodeHandlingFunctor — Вспомогательный класс, который обрабатывает один класс в компиляторе (синглтон). Содержит функции для регистрации пин-соединений и генерирования скомпилированных операторов (statemants).
FKismetCompiledStatement — Компилятор переводит ноды в набор скомпилированных операторов (statemants), которые бэкэнд (backend) переводит в операции с байт-кодом.
FKismetTerm — Термин. В графе — литерал, константа или ссылка на переменную. Каждое пин-соединение связано с одним из них. Вы также можете создавать свои собственные термины NodeHandlingFunctorдля временных переменных, промежуточных результатов и т.д.
Стандартный процесс (полной) компиляции Blueprint описан ниже:
Один и тот же UBlueprintGeneratedClass очищается и повторно используется снова и снова, так что указатели на класс не нужно фиксировать. CleanAndSanitizeClass() перемещает свойства и функции из класса в класс «корзину» во временном пакете, а затем очищает все данные в классе.
Компилятор выполняет иттерации по массиву NewVariables в blueprint, а также по некоторым другим местам (construction scripts и т.д.), Чтобы найти все UProperties, необходимые классу, а затем в функции CreateClassVariablesFromBlueprint() создает UProperties в области UClass.
Компилятор создает список функций для класса, обрабатывая события, обычные функции и предварительно компилирует их, то есть вызывает PrecompileFunction() для каждой обработанной функции и события.
Обработка событий
Обработка событий выполняется функцией CreateAndProcessUberGraph(). Она копирует все события в один большой, временный граф. Затем для каждой ноды события в blueprint создается заглушка функции(function stub), а для каждого события создается FKismetFunctionContext.
Обработка функций
Обработка обычных функций в blueprint выполняется функцией ProcessOneFunctionGraph() , которая дублирует каждую функцию во временный blueprint. FKismetFunctionContext так-же, создается для каждой функции.
Предварительная компиляция функций
Предварительная компиляция функций выполняется функцией PrecompileFunction() каждого FKismetFunctionContext. Эта функция выполняет следующие действия:
Планирует выполнение и рассчитывает зависимости данных.
Удаляет все незапланированные ноды, не зависящие от данных.
Запускает функцию RegisterNets(), которая находится на каждом обработчике ноды(Node Handler), для каждой ноды в функции.
Прошлое действие создает FKismetTerms для значений внутри функции.
Создает UFunction и связанные свойства.
Теперь, когда компилятор знает обо всех UProperties и UFunctions, он может сформировать класс. На этом этапе он, по сути, имеет заголовок класса — без заключительных флагов и метаданных — а также Стандартные свойства объекта класса (CDO).
Этот шаг состоит в создании объектов FKismetCompiledStatment для оставшихся нодов, что выполняется с помощью функций Compile(), а также — AppendStatementForNode() из Node Handler’а. Эта функция может создавать FKismetTerm при компиляции , если они используются только локально.
Чтобы завершить компиляцию класса, компилятор завершает флаги класса и распространяет флаги и метаданные из родительского класса, прежде чем, наконец, выполнить несколько заключительных проверок, чтобы убедиться, что при компиляции всё прошло хорошо.
Бэкенд преобразует набор операторов(statments) из контекста каждой функции в байт-код. Используются два бэкэнда:
FKismetCompilerVMBackend — преобразует FKCS в байт-код виртуальной машины UnrealScript, который затем сериализуется в массив скриптов функции.
FKismetCppBackend — выдаёт C ++ — подобный код, только для целей отладки.
Используя специальную функцию CopyPropertiesForUnrelatedObjects() , компилятор копирует значения из старого CDO класса в новый CDO (об этом, ещё раз, в следующем шаге). Свойства копируются с помощью сериализации с тегами, поэтому, если имена связаны, их следует правильно передавать. Компоненты CDO повторно инициализируются и соответствующим образом настраиваются на этом этапе.
Поскольку класс мог изменить размер, а свойства могли быть добавлены или удалены, компилятору необходимо повторно создать экземпляры всех объектов класса, которые были только что скомпилированы. Этот процесс использует TObjectIterator для поиска всех экземпляров класса, порождает новый, а затем использует функцию CopyPropertiesForUnrelatedObjects() для копирования из старого экземпляра в новый.
Дополнительные сведения можно смотреть в классе FBlueprintCompileReinstancer .
Исходный код компилятора