22 сентября 2021

🎮 Разрабатываем конфигурируемый плагин для Unreal Engine 4 с нуля

UE4 C++ Developer. Currently working with Flying Wild Hog on the Space Punks title. Author of articles on C++, GameDev, Unreal Engine and general programming
Статья проведёт читателя по процессу создания собственного плагина от и до, покажет, как сделать плагин конфигурируемым через редактор движка, а также даст несколько советов по работе с плагинами.
🎮 Разрабатываем конфигурируемый плагин для Unreal Engine 4 с нуля
Прежде чем начать…
Рекомендуем прочитать нашу статью про модули в UE. Это не критически важно для понимания статьи о плагинах, однако в значительной степени упростит её восприятие и сразу ответит на многие вопросы, которые могут возникнуть по мере прочтения текста.

Также предполагается, что читатель базово знаком с C++ и блюпринтами, в частности, с библиотеками BP-функций [1].

Как связаны между собой плагины и модули

Плагины [2] могут состоять из одного или нескольких модулей, а могут не иметь модулей вообще. Плагин более высокоуровневая структура, нежели модуль. В отличие от модулей, плагины могут содержать контент. Плагины можно продавать в Epic Marketplace, модули – нет.

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

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

Создаём основу плагина

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

Предположим, что у вас уже есть проект* на C++ и ваша IDE открыта. Предположим также, что вы решили разработать игровой плагин, а не плагин движка. Заметьте, что в статье мы будем использовать UE 4.27.0 без исходного кода.

Примечание*
Для создания плагина, в котором нет модулей (и кода на C++ соответственно), необязательно, чтобы сам UE-проект был на C++, но мы сосредоточимся именно на плагинах, содержащих C++.
Примечание
Для написания кода мы будем использовать JetBrains Rider for Unreal Engine, однако вы можете установить любую другую подходящую для UE IDE, например, Microsoft Visual Studio.

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

Создадим следующие файлы и поддиректории:

Содержимое директории нашего плагина.
Содержимое директории нашего плагина.

Много всего, правда? Расскажем обо всём по порядку:

SupremePlugin.uplugin [3] дескриптор плагина, который должен иметь имя <PluginName>Plugin.uplugin. В нашем случае его содержимое таково:

<PluginName>Plugin.uplugin
        {
  "FileVersion" : 3,
  "Version" : 1,
  "VersionName" : "1.0",
  "FriendlyName" : "Supreme Plugin",
  "Description" : "A dumb plugin",
  "Category" : "Other",
  "CreatedBy" : "Proglib",
  "CreatedByURL" : "https://proglib.io",
  "DocsURL" : "",
  "MarketplaceURL" : "",
  "SupportURL" : "",
  "EnabledByDefault" : true,
  "CanContainContent" : false,
  "Installed" : false,
  "Modules" :
  [
     {
        "Name" : "SupremePlugin",
        "Type" : "Runtime",
        "LoadingPhase" : "Default"
     }
  ]
}

    

Большинство полей говорят сами за себя, но мы разберём несколько самых важных:

  • EnabledByDefault – должен ли плагин быть включен сразу после установки.
  • CanContainContent – определяет, может ли плагин содержать ассеты.
  • Installed – определяет, был ли плагин установлен поверх или является частью движка/проекта:
Влияние <code class="inline-code">Installed</code> на отображение плагина в редакторе.
Влияние Installed на отображение плагина в редакторе.
  • Modules – одно из самых важных полей – список модулей плагина. Здесь мы указываем единственный модуль нашего плагина.
Примечание
Подробный разбор синтаксиса этой части дескриптора плагина мы приводили в статье про модули.

Файл SupremePlugin.Build.cs описывает единственный модуль плагина – SupremePlugin. Мы не будем подробно останавливаться на его синтаксисе (т.к. разбирали его в статье про модули), а лишь приведём его содержимое:

SupremePlugin.Build.cs
        namespace UnrealBuildTool.Rules
{
  public class SupremePlugin : ModuleRules
  {
     public SupremePlugin(ReadOnlyTargetRules Target) : base(Target)
     {
        PublicDependencyModuleNames.AddRange(
           new string[] //Зависимости нашего модуля
           {
              "Core",
              "CoreUObject",
              "Engine",
		 “DeveloperSettings”
           }
        );
     }
  }
}

    

Хедер SupremePlugin.h ничего не содержит, в то время как SupremePlugin.cpp содержит определение нашего модуля (подробнее об этом мы также говорили в статье про модули):

SupremePlugin.h
        #include "SupremePlugin.h"
#include "Modules/ModuleManager.h"

IMPLEMENT_MODULE(FDefaultModuleImpl, SupremePlugin)

    

Наконец, пара .h/.cpp-файлов SupremeNativeFunctionLibrary задаёт “полезную нагрузку” нашего плагина – библиотеку BP-функций SupremeNativeFunctionLibrary, с единственной функцией DoSomethingUseful(), которая, по легенде, делает что-то полезное.

SupremeNativeFunctionLibrary.h
        #pragma once

#include "CoreMinimal.h"
#include "Kismet/BlueprintFunctionLibrary.h"
#include "SupremeNativeFunctionLibrary.generated.h"

UCLASS()
class SUPREMEPLUGIN_API USupremeNativeFunctionLibrary : public UBlueprintFunctionLibrary
{
  GENERATED_BODY()

public:
  UFUNCTION(BlueprintCallable, meta=(Category="Supreme Functionality"))
  static void DoSomethingUseful();
};
    
SupremeNativeFunctionLibrary.cpp
        #include "SupremeNativeFunctionLibrary.h"

void USupremeNativeFunctionLibrary::DoSomethingUseful()
{
  UE_LOG(LogTemp, Log, TEXT("Kinda useful functionality"));
}
    

Теперь достаточно собрать и запустить проект и в редакторе блюпринтов станет доступной функция DoSomethingUseful().

Использование BP-функциональности из нашего плагина.
Использование BP-функциональности из нашего плагина.
Примечание
Если ваша IDE не видит созданные файлы плагина, в Проводнике Windows кликните правой кнопкой мыши на файле .uproject и выберите Generate Visual Studio Files (даже если работаете не в Visual Studio).

На данный момент в редакторе наш плагин отображается с иконкой по умолчанию:

Отображение плагина в редакторе.
Отображение плагина в редакторе.

Чтобы заменить иконку, необходимо создать папку Resources в корне плагина и добавить в неё изображение с именем Icon128.png, с разрешением 128x128.

Чтобы создать плагин, используя редактор движка, достаточно открыть панель Plugins и нажать New Plugin. Здесь вам будет предложено выбрать один из нескольких типовых шаблонов для будущего плагина, ввести имя и базовую мета-информацию:

Мастер создания плагинов встроенный в редактор.
Мастер создания плагинов встроенный в редактор.

Конфигурация плагина

Предположим, что нас не устраивает, что DoSomethingUseful() выводит сообщение, которое нельзя изменить без изменения кода. В таком случае для плагина стоит добавить конфигурацию, через которую можно будет изменять подобные параметры.

Конфигурация в UE происходит через обычные UObject’ы с UProperty, помеченными с помощью спецификатора Config. Всё, что необходимо сделать – это создать класс для конфигурации, который содержал бы необходимые поля.

В нашем случае класс будет полностью описан в хедере. Создадим хедер SupremePluginSettings.h([4], [5], [6]):

SupremePluginSettings.h
        #pragma once

#include "Engine/DeveloperSettings.h"
#include "SupremePluginSettings.generated.h"

//Здесь Config указывает имя файла, в котором будет храниться конфигурация
//DefaultConfig говорит движку перезаписывать Default*.ini-файлы [4], а не создавать поверх них локальные файлы конфигурации
//Соответственно, конфигурация данного класса будет храниться в DefaultGame.ini
UCLASS(Config=Game, DefaultConfig, meta=(DisplayName="Supreme Plugin Settings"))
//Если бы USupremePluginSettings был унаследован напрямую от UObject [5], а не от UDeveloperSettings [6], то тогда бы он не был доступен для редактирования в Project Settings в редакторе
class SUPREMEPLUGIN_API USupremePluginSettings : public UDeveloperSettings
{
    GENERATED_BODY()
public:

    UPROPERTY(Config, EditAnywhere, Category = "Main")
    FString Message; //Свойство, которое будет содержать необходимое сообщение
};

    

Теперь значение Message можно редактировать через Project Settings в редакторе:

Конфигурирование плагина через настройки проекта
Конфигурирование плагина через настройки проекта

Остаётся лишь каким-либо образом получить значение поля Message в нашей DoSomethingUseful(). Это можно сделать, используя систему CDO (Class Default Object). Её подробное рассмотрение выходит за рамки статьи, но вот ссылка на документацию для интересующихся.

Обновим SupremeNativeFunctionLibrary.cpp:

SupremeNativeFunctionLibrary.cpp
        #include "SupremeNativeFunctionLibrary.h"
#include "SupremePluginSettings.h"

void USupremeNativeFunctionLibrary::DoSomethingUseful()
{
  FString Message = GetDefault<USupremePluginSettings>()->Message; //Получение значения Message через CDO USupremePluginSettings
 
  UE_LOG(LogTemp, Log, TEXT("%s"), *Message);
}

    

Отключение плагина без открытия проекта

В случае, если ваш плагин падает и не даёт открыть редактор, а значит не позволяет отключить самого себя, существует способ отключить плагин без использования GUI. Для этого откройте .uproject и в разделе Plugins добавьте запись с необходимым плагином и полем Enabled, установленным в false:

Фрагмент .uproject-файла.
        ...
"Plugins" : [
  {
     "Name": "SupremePlugin",
     "Enabled": false
  }
],
...

    

Вывод

Плагины в UE могут содержать несколько модулей, а могут не содержать модулей вообще. В отличие от модулей, плагины могут нести с собой ассеты и распространяться на Epic Marketplace. Плагин более высокоуровневая единица абстракции, нежели модуль.

***

На Unreal Engine сделаны многие современные игры, поэтому движок однозначно рекомендован к изучению всем интересующимся геймдевом. На самостоятельное освоение всех его возможностей потребуются не один год, но есть и более короткий путь. Обратите внимание на курс факультета разработки игр на Unreal Engine 4 образовательной онлайн-платформы GeekBrains. Вы освоите сам движок, научитесь программировать на Blueprints и C++ и сможете самостоятельно создавать игры с нуля. Занятия ведут эксперты-разработчики российских технологических компаний, а успешно окончившие курс студенты получат диплом о профессиональной переподготовке, несколько проектов в портфолио и помощь в трудоустройстве.

Источники

Комментарии

ВАКАНСИИ

Добавить вакансию
Разработчик C++
Москва, по итогам собеседования

ЛУЧШИЕ СТАТЬИ ПО ТЕМЕ