Расширение функционала редактора
UNIGINE позволяет вам расширить базовый функционал UnigineEditor, например, добавить новые меню, окна, команды панели инструментов, подрежимы, определить способ отображения свойств и даже создать собственный редактор. Это стало возможным благодаря Системе плагинов UnigineEditor через добавление пользовательских плагинов .
UnigineEditor — это приложение, полностью написанное на C++, в значительной степени полагающееся на инфраструктуру фреймворка Qt5. Итак, для расширения его функционала требуются не только навыки программирования на C++, но и знакомство с фреймворком Qt5, системой сборки CMake.
Смотрите также#
- Статью Создание вашего первого плагина для редактора, чтобы узнать, как создать собственный плагин для редактора.
- Справочник API редактора для получения дополнительной информации обо всех доступных классах.
Система плагинов редактора#
Плагины — это наборы кода и данных, которые разработчики могут легко включать или отключать в UnigineEditor для каждого проекта. Каждый плагин скомпилирован в динамическую библиотеку, которая поставляется отдельно, обнаруживается и загружается UnigineEditor во время выполнения.
В основном структура системы плагинов выглядит следующим образом:
Вот краткое описание основных компонентов:
- QPluginLoader отвечает за загрузку плагинов и предоставление метаданных.
- Editor::PluginInfo — парсинг и хранение метаданных плагина, взаимодействие с интерфейсом плагина, а также содержит текущее состояние плагина и информацию о возможной ошибке инициализации плагина.
- Editor::PluginManager — класс-менеджер, отвечающий за поиск плагинов, построение очереди загрузки плагинов, а также загрузку и удаление плагинов.
-
Editor::Plugin — базовый класс, от которого наследуются все плагины Editor, объявляется следующим образом:
Editor::Plugin Объявление класса
namespace Editor { class EDITOR_API Plugin { public: Plugin(); virtual ~Plugin(); // Plugin's life cycle. virtual bool init() = 0; virtual void shutdown() = 0; }; } // namespace Editor // Associates the given Identifier (a string literal) to the interface class called Editor::Plugin. Q_DECLARE_INTERFACE(Editor::Plugin, "com.unigine.EditorPlugin")
Он имеет два абстрактных метода, определяющих жизненный цикл , вы должны переопределить их для своего пользовательского плагина:
- init() — инициализация плагина (возвращает результат инициализации)
- shutdown() — отключение плагина
Макрос Q_DECLARE_INTERFACE добавляет интерфейс в метасистему Qt.
Поиск плагинов#
Плагины, библиотеки которых расположены в каталоге, указанном ниже, автоматически загружаются и добавляются в список доступных плагинов UnigineEditor, никакого специального кода не требуется:
- %project%/bin/editor — для сборки Release и любой сборки SDK.
- %project%/bin/editor_debug — для сборок Debug на ПК разработчика.
Чтобы увидеть список всех загруженных на данный момент плагинов в UnigineEditor, выберите Help -> Plugins.
Вы можете просмотреть описание любого плагина, выбрав его в списке и нажав Details.
Если при загрузке плагина произошла ошибка, в окне описания будет отображено ее подробное описание.
Метаданные плагина#
Каждый плагин должен иметь дополнительную информацию, необходимую менеджеру плагинов, чтобы найти ваш плагин и проверить его зависимости перед фактической загрузкой файла библиотеки вашего плагина. Эта информация хранится в метафайле в формате JSON (файл myplugin.json автоматически генерируется при добавлении шаблона плагина Editor в ваш проект) и может быть получена во время выполнения из экземпляра класса Editor::PluginInfo. Этот метафайл может выглядеть следующим образом:
{
"Name" : "MyPlugin",
"Vendor" : "Unigine",
"Description" : "The plugin's description text." ,
"Version" : "@PLUGIN_VERSION@",
"CompatVersion" : "@PLUGIN_COMPAT_VERSION@"
"Dependencies" : [
{
"Name": "RequiredPlugin",
"Type": "required",
"Version" : "2.9.0.0"
},
{
"Name": "OptionalPlugin",
"Type": "optional",
"Version" : "2.8.0"
}
]
}
Вот краткий обзор основных элементов:
- Name — имя плагина, отображаемое в списке плагинов UnigineEditor и используемое в качестве ссылки при описании зависимостей других плагинов.
- Vendor, Description — дополнительная информация (необязательно).
- Version — текущая версия плагина.
- CompatVersion — последняя бинарно-совместимая версия плагина, определяет, с какой версией этого плагина текущая версия обратно совместима в бинарном режиме, и используется для разрешения зависимостей от этого плагина.
- Dependencies — список объектов, описывающих зависимости от других плагинов.
- Name — название плагина, на который опирается этот плагин.
- Type — тип зависимости, может быть как required, так и optional.
- Version — версия, с которой должен быть совместим плагин для заполнения зависимости, в виде x.y.z. Может быть пустым, если версия не имеет значения.
На самом деле автор создает файл .json.in, который затем используется CMake для создания фактического файла метаданных плагина .json, заменяя такие переменные, как EDITOR_VERSION, их фактическими значениями.
Зависимости плагинов#
Плагин может использовать другие плагины. Такие зависимости указываются в метаданных плагина, чтобы другие плагины загружались перед ним.
Зависимости объявляются с ключом Dependency, который содержит массив объектов JSON с обязательными ключами Name и Version и необязательным ключом Type.
Следующие формулы иллюстрируют, как сопоставляется информация о зависимости. В формулах имя требуемого подключаемого модуля (как определено в Name объекта зависимости) обозначается как DependencyName, а требуемая версия подключаемого модуля обозначается как DependencyVersion. Плагин с указанными Name, Version и CompatVersion, как определено в метаданных плагина, соответствует зависимости, если выполняются следующие условия:
- Его Name соответствует DependencyName.
- CompatVersion <= DependencyVersion <= Version
Например, зависимость
{
"Name" : "SomeOtherPlugin",
"Version" : "2.4.1"
}
будет соответствовать плагину, который содержит
{
"Name" : "SomeOtherPlugin",
"Version" : "3.1.0",
"CompatVersion" : "2.2.0",
...
}
поскольку имя совпадает, а версия 2.4.1, указанная в теге зависимости, находится между 2.2.0 и 3.1.0.
Жизненный цикл плагина#
Чтобы писать плагины для редактора, вы должны понимать шаги, которые выполняет менеджер плагинов при запуске или завершении работы UnigineEditor.
Когда вы запускаете UnigineEditor , менеджер плагинов делает следующее:
- Ищет в своих путях поиска все динамические библиотеки и считывает их метаданные. Все библиотеки без метаданных и без IID com.unigine.EditorPlugin игнорируются. Начальное состояние всех плагинов — INVALID, так как это первая точка, в которой загрузка плагина при искаженных метаданных в худшем случае может завершиться ошибкой.
- Создает экземпляр класса Editor::PluginInfo для каждого плагина. Будучи контейнером для метаданных плагина, этот класс также отслеживает текущее состояние плагина. Вы можете получить экземпляры Editor::PluginInfo с помощью функции Editor::PluginManager::plugins().
- Устанавливает плагины в состояние READ.
- Проверяет, что зависимости каждого плагина существуют и совместимы.
- Устанавливает плагины в состояние RESOLVED.
- Сортирует все плагины в список, называемый «очередью загрузки», где зависимости плагина располагаются после плагина (но не обязательно сразу после него). Это гарантирует, что плагины загружаются и инициализируются в правильном порядке.
- Загружает библиотеки плагинов и создает их экземпляры Editor::Plugin в порядке очереди загрузки. В этот момент вызываются конструкторы плагинов. Плагины, от которых зависят другие плагины, создаются первыми.
- Устанавливает плагины в состояние LOADED.
-
Вызывает функции init() всех плагинов в соответствии с очередью загрузки. В функции init() плагин должен убедиться, что все экспортируемые интерфейсы настроены и доступны для других плагинов. Поскольку каждый плагин предполагает, что плагины, от которых он зависит, настроили свои экспортированные интерфейсы.
Функция init() плагина — хорошее место для:- создания новых объектов;
- загрузки настроек;
- добавления новых меню и новых действий в них;
- подключения к сигналам других плагинов.
- Устанавливает плагины в состояние RUNNING.
При выключении UnigineEditor менеджер плагинов запускает последовательность завершения работы:
- Вызывает функции shutdown() всех плагинов в порядке очереди загрузки. Плагины должны принимать меры для ускорения фактического завершения работы здесь, например, отключать сигналы, которые в противном случае вызывались бы без необходимости.
- Уничтожает все плагины, удаляя их экземпляры Editor::Plugin в порядке, обратном очереди загрузки. В этот момент вызываются деструкторы плагинов. Плагины должны убирать за собой, освобождая память и другие ресурсы.
Горячая перезагрузка#
Плагины редактора поддерживают горячую перезагрузку, что может быть полезно в случаях, когда вам нужно выполнить такие действия, как:
- выгрузить плагин;
- сделать что-нибудь;
- перезагрузить плагин.
Вы можете выгрузить и снова загрузить свой плагин, когда это необходимо, с помощью соответствующей кнопки в столбце HotReload (доступно через окно Editor Plugins: Help -> Plugins).
Вы также можете сделать то же самое с помощью кода:
Unigine::Vector<Editor::PluginInfo *> plugin_infos = Editor::PluginManager::plugins();
// choose your `PluginInfo*`:
Editor::PluginInfo *required_plugin_info = ..;
// Unload a plugin:
bool unloaded = Editor::PluginManager::unloadPlugin(required_plugin_info);
// Do something
// ...
// Reload the plugin
bool loaded = Editor::PluginManager::loadPlugin(required_plugin_info);