Unreal Engine дает вам действительно мощный инструмент для разработки игр: весь исходный код. Это полезно во многих отношениях:

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

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

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

  • Принятые дополнения будут поддерживаться разработчиками Unreal Engine, поэтому вы не будете одиноки в устранении проблем, связанных с вашим кодом.
  • Это будет проверено официальным разработчиком, поэтому они могут обнаружить что-то, чего вы не заметили сами, что сэкономит вам часы или дни головной боли в поисках ошибки.
  • Для следующего проекта, над которым вы будете работать, он уже будет исправлен, вам не нужно снова сталкиваться с одними и теми же проблемами в ваших проектах.
  • Даже если он будет отклонен, вы получите четкую причину. Это может помочь вам понять, почему использование этого кода было бы опасным или неуместным, что вы можете использовать, чтобы подумать о лучшем решении, или, по крайней мере, узнать о рисках, прежде чем добавлять его в свою собственную вилку.
  • Я не могу поверить, что на ваше резюме не повлияло то, что вы исправили 18 критических ошибок в самом исходном коде движка.
  • Вы можете сказать своим коллегам, когда их раздражает проблема: «О, я уже исправил это, это будет в предстоящем выпуске на следующей неделе». Здорово, если вы можете гордиться своей работой.

В этом сообщении блога я опишу метод, который я использую, чтобы внести свой вклад в движок. Естественно, я предполагаю, что вы знаете C++, Git и работали над проектами C++ UE4, так что мне не нужно объяснять все основы, такие как UPROPERTY и сборка мусора. Если вы не справитесь с какой-либо из этих задач, просто используйте возможности Интернета, чтобы сначала получить эти необходимые знания, и вернитесь ко мне через несколько дней, недель или месяцев, в зависимости от объема знаний, которые вам необходимо получить.

Методология

К счастью, мне не нужно объяснять все необходимые шаблонные настройки, так как официальная документация уже описывает это:

Убедитесь, что у вас есть большой SSD, если вы хотите работать с движком, так как он может занимать более 70 ГБ, а поиск и компиляция на жестком диске намного хуже. Для внесения вклада лучше всего использовать ветку master (чтобы у вас были все последние изменения). После завершения установки у вас должен быть исходный проект, который успешно компилируется и запускается.

Следующий шаг — когда вы можете столкнуться с первой проблемой: С чего начать? Там тысячи файлов… И вы имеете полное право задать этот вопрос. Снаружи UE4 действительно зверь. Мало того, что он огромен, он также имеет много частей, которые являются недостроенными, старыми, необслуживаемыми, уродливыми или их комбинацией. Вдобавок к этому, он постоянно меняется, части перерабатываются, полностью удаляются или добавляются совершенно новые вещи, что может со временем сделать ваши знания в определенных областях устаревшими.

Многие люди пытаются изучить движок, буквально просматривая файлы и классы, чтобы иметь общее представление, базовые знания о том, как вещи структурированы и взаимосвязаны. Однако я не думаю, что это работает. Это действительно слишком много. Я почти уверен, что если вы спросите официального разработчика инструментов Epic о классе в разделе рендеринга, он, вероятно, не поймет, о чем вы говорите. По моему опыту, что действительно работает, так это сначала поставить перед собой небольшие цели, например, исправить очень незначительную ошибку или реализовать крошечную функцию. Это дает вам преимущество:

  • Сосредоточьтесь на конкретной части двигателя, таким образом, получая более глубокие знания в той части, которая важна для вас.
  • Вспомните систему гораздо лучше той, с которой вы работали. Что-то делать всегда больше стимулирует мозг, чем просто читать что-то.
  • Увидите реальные результаты своих действий, будет цель, которую нужно достичь.
  • Не скучайте, анализируя миллионы строк кода, а бросайте вызов.

Найти эти мелочи, которые нужно исправить, нетрудно (не)к счастью, поскольку редактор плавает в ошибках и неудобных функциях. Просто поработайте несколько дней интенсивно в редакторе, и вы сможете составить список из множества мелких проблем, которые вас раздражали, замедляли рабочий процесс и т. д.

В следующих частях я рассмотрю решение примера проблемы, с помощью которого я попытаюсь дать вам некоторое представление о том, как вы можете найти свой путь в исходном коде, даже если вы видите его впервые. Конечно, вы можете разработать свою собственную методологию через некоторое время, это просто подход, который я использовал для участия в разработке Polars, нашей маленькой игры-головоломки на основе магнитов.

Выбор проблемы

Проблема, которую я выбрал, заключается в поиске ошибочных чертежей при попытке играть в редакторе. В настоящее время это выглядит так:

Моя проблема с этим экраном заключается в том, что хотя мы и получаем список чертежей, они не кликабельны, поэтому мы не можем быстро перейти к ошибкам, нам приходится искать файлы в Content Browser один за другим. То есть, если вы сможете запомнить их все, иначе придется снова нажимать Play, и проверять, какие записи остались в списке. Особенно раздражает, когда что-то рефакторишь, и естественно получаешь в результате штук 10 ошибочных чертежей. Это проблема, которую я хотел бы решить. Звучит минорно и хорошо инкапсулировано.

Теперь вам не нужно иметь точный план метода исправления, пока вы на самом деле не увидите, с чем имеете дело. Мы не хотим разрабатывать полностью новые экраны и сотни строк кода, мы хотели бы внести минимальные необходимые изменения, чтобы преодолеть первоначальное ограничение.

Моей первой мыслью было просто сделать эти записи ссылками, как «Копировать сообщение».

Выполнение

Теперь, когда мы обнаружили проблему… как нам найти код, с которым нам придется иметь дело? Хотите верьте, хотите нет, но наш ближайший друг в этом — старый добрый текстовый поиск. Когда я пытаюсь найти код, с которым я хотел бы работать, какой бы ни была проблема, я пытаюсь найти ближайший, надеюсь, уникальный текст, который я могу найти в коде. Например, если бы я хотел придать полю «Ключевые слова функции» другой стиль, я бы не стал искать «Ключевые слова», так как будут буквально тысячи результатов. Вместо этого ближайшая уникальная строка может быть всплывающей подсказкой для этого поля.

В нашем случае я бы просто искал «У одного или нескольких чертежей есть». Весьма вероятно, что этот текст используется только этим всплывающим окном, поэтому я могу подобрать код, который мне нужен для этого сообщения. В Windows я рекомендую Total Commander для поиска по содержимому файлов, так как он очень быстрый, особенно на SSD.

Вы найдете исходный код движка в «[UE4]/Engine/Source», если вы выбрали его для установки в Launcher. Чаще всего вы будете использовать здесь папки «Редактор» и «Время выполнения». Первый предназначен для кода только для редактора, а второй — для кода, который также упакован в игру (и также может использоваться редактором). Теперь это всплывающее окно компиляции чертежей, скорее всего, является функцией только для редактора, поскольку вы действительно не ожидаете, что она будет существовать в упакованной игре, поэтому давайте поищем в каталоге «Редактор». По мере того, как вы будете узнавать двигатель все больше и больше, вы сможете находить их намного лучше, но пока давайте просто предположим, что вы понятия не имеете, где что находится. Кроме того, иногда вы не можете найти то, что ищете, в той папке, в которой, по вашему мнению, это есть. Если это произойдет, просто загляните и в другие каталоги (включая «Engine/Plugins», так как источник плагинов находится здесь).

В нашем случае нам крупно повезло, так как наш текст действительно был уникальным, и мы видим, что PlayLevel.cpp — это то, с чем нам приходится иметь дело. Если результатов больше, вам придется проверить текстовые вхождения в каждом файле, чтобы найти тот, который вам действительно нужен.

Если вы откроете файл и поищите нашу подстроку, вы увидите, что мы нашли наш диалог.

Мы видим, что список чертежей строится прямо перед ним.

Теперь это просто длинная строка с символами новой строки (\n) между именами чертежей, которая передается в FMessageDialog::Open, поэтому, к сожалению, мы не можем преобразовать их в ссылки. Итак, как еще мы можем решить основную проблему?

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

В нем есть сообщение со ссылками, милая! С левой стороны есть много категорий журналов, поэтому я начал щелкать по ним, чтобы увидеть, где может поместиться наше сообщение. К моему удивлению, неудачная компиляция уже была напечатана в «Blueprint Log».

Здорово! Так что на самом деле он уже зарегистрирован. Однако есть несколько проблем, которые я видел:

  • Схема здесь тоже не кликабельна.
  • Он показывает это как «информацию», хотя на самом деле это должно быть «предупреждением».
  • По какой-то причине он не показывает все ошибочные чертежи.

Во-первых, давайте перейдем к делу, сделаем его кликабельным, если сможем, а потом мы разберемся с остальным. Еще раз вызовите поиск подстроки для нашей помощи: «не удалось скомпилировать». Если вы поищите его, вы увидите, что это также сделано ResolveDirtyBlueprints в том же файле, PlayLevel.cpp.

ОК, круто. Теперь приступим к кодированию. Всякий раз, когда вы доберетесь до этой части, помните: вы почти всегда можете скопировать нужный вам код или, по крайней мере, черпать вдохновение из примеров, которые вы можете найти в исходниках. Вот, например, мы уже знаем, что ошибки времени выполнения блупринта красиво оформлены ссылками, так что давайте просто украдем оттуда код! Как всегда, поиск строки: «Ошибка выполнения Blueprint: «. И снова один результат: KismetDebugUtilities.cpp.

Нам повезло, так как в этом сообщении уже есть ссылка на чертеж, так что нам не придется искать, как это сделать. Единственное, о чем нам нужно позаботиться, это OnMessageTokenActivated (который, по-видимому, определяет, что делает гиперссылка), так как он использует локальную функцию обратного вызова. Давайте просто скопируем и сообщение, и структуру Local в PlayLevel, поскольку мы хотим сделать то же самое, а затем немного отформатируем их, чтобы они лучше соответствовали нашему сообщению.

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

Потрясающий! Я обнаружил, что в журнале появляются только те чертежи, которые еще не сохранены, однако они появляются во всплывающем окне. Строковый список всплывающего окна состоит из ErroredBlueprints, который заполняется ResolveDirtyBlueprints. Если вы посмотрите на имя переменной, то увидите, что есть два места, где мы добавляем новый элемент в этот массив, и пишем в лог только из одного из них. Так что я просто переместил регистрацию сообщений в новую функцию в структуре Local и вызвал ее из обоих мест. Скомпилировав и снова запустив проект, мы видим, что теперь мы получаем сообщение от всех сломанных чертежей. Вау, мы приближаемся!

Теперь, когда журнал работает должным образом, во-первых, я хотел создать новую кнопку во всплывающем окне, что-то вроде «Показать в журнале». Если вы перейдете к объявлению FMessageDialog (Ctrl + правый клик в Visual Studio), вы увидите, что, к сожалению, кнопки предварительно распределены (ОК, Отмена и т. д.), нет возможности передать наши собственные пользовательские кнопки. В качестве запасного варианта давайте просто откроем журнал на тот случай, если разработчик решит не продолжать работу в режиме PIE, поскольку вполне вероятно, что он так и решил бы, если бы захотел позаботиться об ошибках. Кроме того, окончание PIE уже делает это с сообщениями времени выполнения.

Вполне вероятно, что это также сделал PlayLevel, но поскольку мы не знаем, что искать, давайте найдем название категории журнала в исходном коде редактора с учетом регистра и кавычками, так как мы знаем, что это должна быть целая строка: «Играть в редакторе». Если бы вы просто искали его без кавычек, вы, вероятно, нашли бы тонны комментариев, а поиск с учетом регистра обычно помогает сузить результаты. Один результат в UnrealEdMisc.

Вас понял! Здесь мы видим, что название категории журнала — «PIE» (какой сюрприз), поэтому давайте вернемся к PlayLevel и найдем его с кавычками. Снова один результат: NAME_CategoryPIE. Если мы просто просмотрим несколько результатов, мы увидим, как он открывается.

Давайте просто сделаем то же самое после того, как получим результат из всплывающего окна, в случае !bContinuePIE. Если мы проверим объявление Open, то увидим, что первый параметр определяет минимальную серьезность, при которой меню журнала не открывается. «Предупреждение» как раз подходит для нас, так как мы использовали одинаковую серьезность для ведения журнала.

И все, конечный результат вы можете увидеть здесь. Давайте просто сделаем последнюю сборку и проверим, действительно ли она работает так, как мы ожидаем. Если все в порядке, создайте PR для проверки Epic с помощью их документации. Я сделал это только что, когда писал этот пост, вы можете посмотреть здесь.

Конечно, это может быть не самое оптимальное решение, но оно работает и вроде бы ничего не ломает. Посмотрим, примут или нет.

Вывод

Оглядываясь назад на весь пост, он действительно кажется большим и сложным, также возможно, что вы заблудились в нескольких моментах. Тем не менее, конкретные шаги сейчас не имеют значения, единственное, что имеет значение, это то, что вы понимаете методологию: используйте строковый поиск, чтобы обнаружить только ту часть движка, с которой вы сейчас работаете, и делайте минимальные локальные модификации, только если ты сможешь. Чем меньше вы меняете, тем легче их проверять и тем больше шансов, что они будут приняты Epic.

Как видите, мы заставили что-то работать, не вникая по шею в классы, с которыми мы работали. Мы можем ничего не знать о UBlueprint, FMessageLog и многих других, но нам и не нужно знать. Если бы мы захотели, то анализ всех соответствующих классов занял бы часы или дни, и мы, вероятно, просто получили бы тот же код, который мы сделали за полчаса, но с несколькими потерянными днями на изучение чего-то, чего мы не знали. даже нужно прямо сейчас, и, вероятно, все равно забудете через несколько недель (или, если ваш мозг работает, как мой, дни).

Другой важный урок заключается в том, что вы не сможете обойтись без прочного фундамента C++ (и стороны C++ UE4). Хотя большая часть кода может быть легко основана на существующем коде движка, вам, безусловно, придется понять и преобразовать его, чтобы он соответствовал задаче, которую вы решаете в данный момент.