创建您的第一个编辑器插件
This article describes how to create an Editor plugin and get the first impression of what a plugin consists of and what its general structure is.本文介绍了如何创建一个编辑器插件,并初步了解插件的组成和一般结构。
UnigineEditor is an application written entirely in C++ relying a lot on the Qt5 framework infrastructure. So, in order to extend its functionality not only C++ programming skills are required, but you should also be familiar with the Qt5 framework, and CMake build system (especially if you use Linux).UnigineEditor 是一个完全用 C++ 编写的应用程序,很大程度上依赖于 Qt5 框架基础架构。因此,为了扩展其功能,不仅需要 C++ 编程技能,而且您还应该熟悉 Qt5 框架和 CMake 构建系统(尤其是如果您使用 Linux)。
See Also也可以看看#
- Extending Editor Functionality article for more information on UnigineEditor's Plugin System and extensions.扩展编辑器功能有关 UnigineEditor 的插件系统和扩展的更多信息的文章。
- Editor API Reference for more information on all available classes.编辑器 API 参考以获取有关所有可用类的更多信息。
- Creating C++ Plugin article for information on the naming convention and plugin files location.创建C ++插件 文章中关于命名约定和插件文件位置的信息。
Plugin Templates插件模板#
For your convenience there are three project templates (Engine GUI Window, Assets, and Materials) that you can use as a basis to create your own plugin. Below you'll find a brief description of each template.为了您的方便,您可以使用两个项目模板(Engine GUI Window 和 Materials)作为基础来创建自己的插件。您将在下面找到每个模板的简要说明。
Materials PluginMaterials Plugin#
This plugin template implements the basic functionality of the Editor's Materials Hierarchy window (see the picture below).这个插件模板实现了编辑器 Materials Hierarchy 窗口的基本功能(见下图)。
Basically the plugin's architecture looks as follows:基本上插件的架构如下所示:
The MaterialsPlugin class is an initialization point and complete representation of the plugin. It creates necessary data model and via the base interface associates it with the widget, which displays the hierarchy. The model directly interacts with the Materials Manager and transforms information on materials to the required structure.MaterialsPlugin 类是插件的初始化点和完整表示。 它创建必要的数据模型并通过基本接口将其与显示层次结构的小部件相关联。 模型直接与 Materials Manager 交互,并将材质信息转换为所需的结构。
The plugin interacts with Engine Materials System and Editor's external subsystems:该插件与 Engine Materials System 和 Editor 的外部子系统交互:
- Adds a widget to WindowManager.向 WindowManager 添加一个小部件。
- Adds a new item to the main menu bar.将新项目添加到主菜单栏。
- Updates the state of materials widget when the global selection is changed.更改全局选择时更新材质小部件的状态。
- Updates the global selection when the local selection is changed by sending a SelectionAction to the Undo stack.通过将 SelectionAction 发送到 Undo 堆栈来更改本地选择时更新全局选择。
Engine Gui Window PluginEngine Gui Window Plugin#
This plugin template implements a simple dockable window of the UnigineEditor with a simple GUI example that displays the current selection in the World Nodes hierarchy window, and enables you to assign a C++ component to selected nodes (see the picture below).这个插件模板实现了 UnigineEditor 的一个简单的可停靠窗口,带有一个简单的 GUI 示例,该示例在 World Nodes 层次结构窗口中显示当前选择,并使您能够将 C++ 组件分配给选定的节点(见下图)。
基本上插件的架构如下所示:
The EngineGuiWindowPlugin class is an initialization point and complete representation of the plugin. EngineGuiWindowPlugin 类是插件的初始化点和完整表示。
The plugin interacts with Editor's external subsystems:该插件与编辑器的外部子系统交互:
- Adds a widget to WindowManager.向 WindowManager 添加一个小部件。
- Adds a new item to the main menu bar.将新项目添加到主菜单栏。
- Interacts with Editor's Drag&Drop System and enables dragging and dropping files within the window area, prints their names to the Console.与编辑器的 Drag&Drop 系统交互并启用在窗口区域内拖放文件,将它们的名称打印到控制台。
- Monitors current window focus state.监视当前窗口焦点状态。
- Updates the global selection when the local selection is changed by sending a SelectionAction to the Undo stack, when a node is selected and the Add components to selection! button is clicked.Updates the global selection when the local selection is changed by sending a SelectionAction to the Undo stack, when a node is selected and the Add components to selection! button is clicked.
And the following Engine subsystems:以及以下引擎子系统:
- C++ Component SystemC++ 组件系统
- GUI System图形用户界面系统
- Input System输入系统
Using Plugin Template使用插件模板#
To start developing your plugin for UnigineEditor perform the following actions:要开始为 UnigineEditor 开发插件,请执行以下操作:
-
Check the Editor Plugin Template option when creating a new project in SDK Browser (or choose Other Actions -> Configure Project if you want to use your new Editor plugin in an existing project).检查 Editor Plugin Template 选项时创建一个新项目在 SDK Browser 中(如果您想在现有项目中使用新的编辑器插件,请选择 Other Actions -> Configure Project)。
- Click Create New Project (or Update Configuration if you're adding a template to an existing project).单击 Create New Project(如果要将模板添加到现有项目,则单击 Update Configuration)。
After clicking the Create New Project / Update Configuration button, a set of projects for the Editor plugins will be added to your UNIGINE project's source folder and all necessary files will be added to these folders depending on your operating system and API + IDE selection in project settings:单击 Create New Project / Update Configuration 按钮后,编辑器插件的一组项目将添加到您的 UNIGINE 项目的 source 文件夹中,并且所有必要的文件都将添加到此文件夹中,具体取决于您的操作系统和项目设置中的 API + IDE 选择:
- Windows OS: C++ (Qt-based / CMake) or UnigineScript — CMake will be used to build your plugin.Windows OS: C++ (Qt-based / CMake) or UnigineScript — CMake 将用于构建您的插件。
- Windows OS: C# (.NET) or C++ (Visual Studio) — a .vcxproj project will be created and Visual Studio 2022 will be used to build your plugin.Windows OS: C# (.NET) or C++ (Visual Studio) — 将创建一个 .vcxproj 项目,并使用 Visual Studio 2022 构建您的插件。
- Linux — CMake will be used to build your plugin regardless of project configuration.Linux — CMake 将用于构建您的插件,无论项目配置如何。
You can open this project in your IDE, build it, and give it a test drive right in the Editor as the project is pre-configured to launch with the UnigineEditor.您可以在 IDE 中打开该项目,构建它,然后在编辑器中对其进行测试,因为该项目已预先配置为使用 UnigineEditor 启动。
Plugin's Logic Implementation插件的逻辑实现#
Now let's have a quick look at the implementation of our plugin template's logic. As an example let's choose the Materials plugin.现在让我们快速看一下插件模板逻辑的实现。例如,让我们选择 Materials 插件。
Plugin Class插件类#
The files MaterialsPlugin.h and MaterialsPlugin.cpp define the implementation of our plugin. Let's highlight some important points here.文件 MaterialsPlugin.h 和 MaterialsPlugin.cpp 定义了我们插件的实现。让我们在这里强调一些要点。
Header File头文件#
The header file MaterialsPlugin.h defines the interface of the plugin class.头文件 MaterialsPlugin.h 定义了插件类的接口。
namespace Materials {
The plugin is defined in a Materials namespace, which conforms to the coding rules for namespacing in sources.该插件在 Materials 命名空间中定义,符合源中命名空间的编码规则。
class MaterialsPlugin : public QObject, public ::Editor::Plugin
{
Q_OBJECT
Q_PLUGIN_METADATA(IID UNIGINE_EDITOR_PLUGIN_IID FILE "Materials.json")
Q_INTERFACES(Editor::Plugin)
All Editor plugins must be derived from Editor::Plugin class and are QObjects. The Q_PLUGIN_METADATA macro is necessary to create a valid Editor plugin. The IID given in the macro must be UNIGINE_EDITOR_PLUGIN_IID, to identify it as an Editor plugin, and FILE must point to the plugin's meta data file as described in Plugin Meta Data.所有编辑器插件必须从 Editor::Plugin 类派生并且是 QObjects。 Q_PLUGIN_METADATA 宏是创建有效编辑器插件所必需的。宏中给出的 IID 必须是 UNIGINE_EDITOR_PLUGIN_IID,以将其标识为编辑器插件,并且 FILE 必须指向插件的元数据文件,如中所述插件元数据.
bool init() override;
void shutdown() override;
The base class defines basic functions that are called during the life cycle of a plugin (on initialization and shutdown), which are here implemented for your new plugin.基类定义了在运行期间调用的基本函数生命周期插件(在初始化和关闭时),这是为您的新插件实现的。
Our plugin has three additional custom slots, that are used show the view, set global selection and update the local one when the global is changed.我们的插件有三个额外的自定义槽,用于显示视图、设置全局选择并在全局更改时更新本地选项。
public slots:
void showView();
void setGlobalSelection(const QModelIndexList &indexes);
void globalSelectionChanged();
MaterialsPlugin.h
#pragma once
#include <editor/Plugin.h>
#include <QObject>
#include <QModelIndex>
namespace Materials {
class MaterialsModel;
class MaterialsView;
}
class QAction;
namespace Materials {
class MaterialsPlugin : public QObject, public ::Editor::Plugin
{
Q_OBJECT
Q_PLUGIN_METADATA(IID UNIGINE_EDITOR_PLUGIN_IID FILE "MaterialsPlugin.json")
Q_INTERFACES(Editor::Plugin)
public:
~MaterialsPlugin() override;
bool init() override;
void shutdown() override;
public slots:
void showView();
void setGlobalSelection(const QModelIndexList &indexes);
void globalSelectionChanged();
private:
MaterialsModel *model_{nullptr};
MaterialsView *view_{nullptr};
QAction *action_{nullptr};
};
} // namespace Materials
Implementation File实施文件#
This file contains the actual implementation of our plugin and its logic. For more information about implementing the plugin interface, see the Editor::Plugin API documentation and Plugin Life Cycle.该文件包含我们插件的实际实现及其逻辑。有关实现插件接口的更多信息,请参阅 Editor::Plugin API 文档和插件生命周期.
MaterialsPlugin.cpp
#include "MaterialsPlugin.h"
#include "MaterialsView.h"
#include "MaterialsModel.h"
#include "QtMetaTypes.h"
#include <editor/Actions.h>
#include <editor/Undo.h>
#include <editor/Constants.h>
#include <editor/WindowManager.h>
#include <editor/Selection.h>
#include <editor/Selector.h>
#include <QObject>
#include <QMenu>
#include <algorithm>
#include <iterator>
using Editor::Selection;
using Editor::SelectionAction;
using Editor::WindowManager;
using Editor::SelectorGUIDs;
namespace Materials
{
MaterialsPlugin::~MaterialsPlugin() = default;
bool MaterialsPlugin::init()
{
model_ = new MaterialsModel(this);
view_ = new MaterialsView(model_);
view_->setWindowTitle("Plugin — Material List");
view_->setObjectName("PluginMaterialsView");
WindowManager::add(view_, WindowManager::ROOT_AREA_LEFT);
connect(view_, &MaterialsView::selected, this, &MaterialsPlugin::setGlobalSelection);
QMenu *menu = WindowManager::findMenu(Constants::MM_WINDOWS);
action_ = menu->addAction("Plugin — Material List", this, &MaterialsPlugin::showView);
connect(Selection::instance(), &Selection::changed,
this, &MaterialsPlugin::globalSelectionChanged);
return true;
}
void MaterialsPlugin::shutdown()
{
QMenu *menu = WindowManager::findMenu(Constants::MM_WINDOWS);
menu->removeAction(action_);
action_ = nullptr;
disconnect(
Selection::instance(), &Selection::changed, this, &MaterialsPlugin::globalSelectionChanged);
disconnect(view_);
WindowManager::remove(view_);
delete view_;
view_ = nullptr;
delete model_;
model_ = nullptr;
}
void MaterialsPlugin::showView()
{
WindowManager::show(view_);
}
void MaterialsPlugin::setGlobalSelection(const QModelIndexList &indexes)
{
disconnect(Selection::instance(), &Selection::changed,
this, &MaterialsPlugin::globalSelectionChanged);
Unigine::Vector<Unigine::UGUID> guids;
guids.reserve(indexes.size());
std::transform(std::begin(indexes), std::end(indexes), std::back_inserter(guids),
[](const QModelIndex &idx)
{ return idx.data(MaterialsModel::GUID_ROLE).value<Unigine::UGUID>(); });
SelectionAction::applySelection(SelectorGUIDs::createMaterialsSelector(guids));
connect(Selection::instance(), &Selection::changed,
this, &MaterialsPlugin::globalSelectionChanged);
}
void MaterialsPlugin::globalSelectionChanged()
{
using namespace std;
if (auto selector = Selection::getSelectorMaterials())
{
const Unigine::Vector<Unigine::UGUID> &guids = selector->guids();
QModelIndexList indexes;
indexes.reserve(guids.size());
transform(begin(guids), end(guids), back_inserter(indexes),
[this](const Unigine::UGUID &guid) { return model_->index(guid); });
auto it = remove(begin(indexes), end(indexes), QModelIndex());
indexes.erase(it, end(indexes));
QSignalBlocker blocker(view_);
view_->globalSelectionChanged(indexes);
}
else if(auto selector = Selection::getSelectorRuntimes())
{
// implementation
}
else
{
view_->clearSelection();
}
}
} // namespace Materials
Model ClassModel Class#
Header File头文件#
MaterialsModel.h
#pragma once
#include <QAbstractItemModel>
namespace Unigine { class UGUID; };
namespace Materials
{
class MaterialsModel final : public QAbstractItemModel
{
class Item;
public:
enum Role
{
GUID_ROLE = Qt::UserRole + 1
};
MaterialsModel(QObject *parent = nullptr);
~MaterialsModel() override;
QModelIndex index(int row, int column, const QModelIndex &parent = QModelIndex()) const override;
QModelIndex index(const Unigine::UGUID &guid) const;
QModelIndex parent(const QModelIndex &child) const override;
int rowCount(const QModelIndex &parent = QModelIndex()) const override;
int columnCount(const QModelIndex &parent = QModelIndex()) const override;
QVariant data(const QModelIndex &index, int role = Qt::DisplayRole) const override;
protected:
void fetchMore(const QModelIndex &parent) override;
bool canFetchMore(const QModelIndex &parent) const override;
private:
Item *const root_;
};
} // namespace Materials
Implementation File实施文件#
MaterialsModel.cpp
#include "MaterialsModel.h"
#include "QtMetaTypes.h"
#include <UnigineGUID.h>
#include <UnigineMaterials.h>
#include <UnigineFileSystem.h>
#include <QDebug>
using Unigine::UGUID;
namespace Materials
{
////////////////////////////////////////////////////////////////////////////////
// MaterialsModel::Item.
////////////////////////////////////////////////////////////////////////////////
class MaterialsModel::Item
{
public:
static Item *createRootItem(MaterialsModel *model);
Item(Item *parent, MaterialsModel *model, const UGUID &guid);
~Item();
QModelIndex index() const;
void append(Item *item);
Item *child(int idx);
Item *find(const UGUID &guid) const;
int childCount() const;
Item *parent();
int row() const;
QVariant data(int role) const;
void fetchMore();
bool canFetchMore() const;
private:
Item *parent_ = nullptr; // Not owned.
MaterialsModel *model_ = nullptr; // Not owned.
QVector<Item *> children_; // Owned.
UGUID guid_;
};
MaterialsModel::Item *MaterialsModel::Item::createRootItem(MaterialsModel *model)
{
Item *root = new Item(nullptr, model, UGUID());
const int size = Unigine::Materials::getNumMaterials();
for (int i = 0; i < size; ++i)
{
const Unigine::MaterialPtr mat = Unigine::Materials::getMaterial(i);
if (mat->isHidden())
{
continue;
}
if (const Unigine::MaterialPtr &parent_mat = mat->getParent())
{
if (!parent_mat->isHidden())
{
continue;
}
}
root->children_.push_back(new Item(root, model, mat->getGUID()));
}
return root;
}
MaterialsModel::Item::Item(Item *parent, MaterialsModel *model, const UGUID &guid)
: parent_(parent), model_(model), guid_(guid)
{
}
MaterialsModel::Item::~Item()
{
qDeleteAll(children_);
}
QModelIndex MaterialsModel::Item::index() const
{
if (!parent_ || guid_.isEmpty())
{
return QModelIndex();
}
return model_->createIndex(row(), 0, const_cast<Item *>(this));
}
void MaterialsModel::Item::append(Item *item)
{
const int size = children_.size();
model_->beginInsertRows(index(), size, size);
item->parent_ = this;
children_.push_back(item);
model_->endInsertRows();
}
auto MaterialsModel::Item::child(int idx) -> Item *
{
return ((idx >= 0) && (idx < children_.size()))
? children_[idx]
: nullptr;
}
auto MaterialsModel::Item::find(const UGUID &guid) const -> Item *
{
for (Item *child: children_)
{
if (child->guid_ == guid)
{
return child;
}
if (child->canFetchMore())
{
child->fetchMore();
}
if (Item *grandchild = child->find(guid))
{
return grandchild;
}
}
return nullptr;
}
int MaterialsModel::Item::childCount() const
{
if (!guid_.isValid())
{
return children_.size();
}
const Unigine::MaterialPtr mat = Unigine::Materials::findMaterialByGUID(guid_);
if (!mat)
{
return 0;
}
int count = mat->getNumChildren();
for (int i = count — 1; i >= 0; --i)
{
if (mat->getChild(i)->isHidden())
{
--count;
}
}
return count;
}
MaterialsModel::Item *MaterialsModel::Item::parent()
{
return parent_;
}
int MaterialsModel::Item::row() const
{
if (parent_)
{
return parent_->children_.indexOf(const_cast<Item *>(this));
}
return 0;
}
QVariant MaterialsModel::Item::data(int role) const
{
if (Qt::DisplayRole == role)
{
const Unigine::MaterialPtr &mat = Unigine::Materials::findMaterialByGUID(guid_);
if (!mat)
{
const QString name =
tr("Removed — %1").arg(QLatin1String(guid_.getString()));
return QVariant::fromValue(name);
}
const UGUID &file_mat_guid = Unigine::FileSystem::getGUID(mat->getPath());
const UGUID &asset_guid = Unigine::FileSystemAssets::resolveAsset(file_mat_guid);
const Unigine::String &virtual_path = Unigine::FileSystem::getVirtualPath(asset_guid);
const Unigine::StringStack<> file_name = Unigine::String::filename(virtual_path);
return QVariant::fromValue(QString(file_name.get()));
}
if (MaterialsModel::GUID_ROLE == role)
{
return QVariant::fromValue(guid_);
}
return QVariant();
}
void MaterialsModel::Item::fetchMore()
{
const Unigine::MaterialPtr &mat = Unigine::Materials::findMaterialByGUID(guid_);
if (!mat)
{
// process adding new materials and removing old ones.
return;
}
const int size = mat->getNumChildren();
QVector<Item *> items;
items.reserve(size);
for (int i = 0; i < size; ++i)
{
const Unigine::MaterialPtr child_mat = mat->getChild(i);
if (child_mat->isHidden())
{
continue;
}
items.push_back(new Item(this, model_, child_mat->getGUID()));
}
if (items.empty())
{
return;
}
const int cur_size = children_.size();
model_->beginInsertRows(index(), cur_size, cur_size + items.size() — 1);
std::copy(std::begin(items), std::end(items), std::back_inserter(children_));
model_->endInsertRows();
}
bool MaterialsModel::Item::canFetchMore() const
{
return children_.empty() && childCount();
}
////////////////////////////////////////////////////////////////////////////////
// MaterialsModel.
////////////////////////////////////////////////////////////////////////////////
MaterialsModel::MaterialsModel(QObject *parent)
: QAbstractItemModel(parent)
, root_(Item::createRootItem(this))
{
}
MaterialsModel::~MaterialsModel()
{
delete root_;
}
QModelIndex MaterialsModel::index(int row, int column, const QModelIndex &parent) const
{
if (column > 0)
{
return {};
}
Item *parent_item = nullptr;
if (!parent.isValid())
{
parent_item = root_;
}
else
{
parent_item = static_cast<Item *>(parent.internalPointer());
}
Item *child_item = parent_item->child(row);
return child_item
? createIndex(row, column, child_item)
: QModelIndex();
}
QModelIndex MaterialsModel::index(const UGUID &guid) const
{
if (guid.isEmpty())
{
return QModelIndex();
}
Item *item = root_->find(guid);
return item ? item->index() : QModelIndex();
}
QModelIndex MaterialsModel::parent(const QModelIndex &child) const
{
if (!child.isValid())
{
return QModelIndex();
}
Item *child_item = static_cast<Item *>(child.internalPointer());
Item *parent_item = child_item->parent();
return (parent_item != root_)
? createIndex(parent_item->row(), 0, parent_item)
: QModelIndex();
}
int MaterialsModel::rowCount(const QModelIndex &parent) const
{
if (parent.column() > 0)
{
return 0;
}
Item *parent_item = nullptr;
if (!parent.isValid())
{
parent_item = root_;
}
else
{
parent_item = static_cast<Item *>(parent.internalPointer());
}
return parent_item->childCount();
}
int MaterialsModel::columnCount(const QModelIndex &parent) const
{
return 1;
}
QVariant MaterialsModel::data(const QModelIndex &index, int role) const
{
if (!index.isValid())
{
return QVariant();
}
Item *item = static_cast<Item *>(index.internalPointer());
return item->data(role);
}
void MaterialsModel::fetchMore(const QModelIndex &parent)
{
if (!parent.isValid())
{
return;
}
Item *item = static_cast<Item *>(parent.internalPointer());
item->fetchMore();
}
bool MaterialsModel::canFetchMore(const QModelIndex &parent) const
{
if (!parent.isValid())
{
return false;
}
Item *item = static_cast<Item *>(parent.internalPointer());
return item->canFetchMore();
}
} // namespace Materials
View ClassView Class#
Header File头文件#
MaterialsView.h
#pragma once
#include <QWidget>
#include <QModelIndex>
namespace Unigine { class UGUID; }
class QAbstractItemModel;
class QItemSelection;
class QTreeView;
namespace Materials
{
class MaterialsView : public QWidget
{
Q_OBJECT
public:
MaterialsView(QAbstractItemModel *model, QWidget *parent = nullptr);
~MaterialsView();
signals:
void selected(const QModelIndexList &indexes);
public slots:
void clearSelection();
void globalSelectionChanged(const QModelIndexList &indexes);
private slots:
void selectionChanged(const QItemSelection &selection);
private:
QTreeView *view_ = nullptr;
};
} // namespace Materials
Implementation File实施文件#
MaterialsView.cpp
#include "MaterialsView.h"
#include <QAbstractItemModel>
#include <QVBoxLayout>
#include <QTreeView>
namespace Materials
{
MaterialsView::MaterialsView(QAbstractItemModel *model, QWidget *parent)
: QWidget{parent}
{
view_ = new QTreeView();
view_->setAlternatingRowColors(true);
view_->setSelectionMode(QAbstractItemView::ExtendedSelection);
view_->setSelectionBehavior(QAbstractItemView::SelectRows);
view_->setHeaderHidden(true);
auto vl = new QVBoxLayout(this);
vl->setContentsMargins(2, 2, 2, 2);
vl->addWidget(view_);
view_->setModel(model);
connect(view_->selectionModel(), &QItemSelectionModel::selectionChanged
, this, &MaterialsView::selectionChanged);
}
MaterialsView::~MaterialsView() = default;
void MaterialsView::clearSelection()
{
view_->selectionModel()->clearSelection();
}
void MaterialsView::globalSelectionChanged(const QModelIndexList &indexes)
{
if (indexes.empty())
{
clearSelection();
return;
}
view_->setCurrentIndex(indexes.back());
QItemSelection item_selection;
for (const QModelIndex &index: indexes)
{
item_selection.append(QItemSelectionRange(index));
}
view_->selectionModel()->select(item_selection, QItemSelectionModel::ClearAndSelect);
view_->scrollTo(indexes.back());
}
void MaterialsView::selectionChanged(const QItemSelection &selection)
{
emit selected(view_->selectionModel()->selectedIndexes());
}
} // namespace Materials
Auxiliary File辅助文件#
This file contains registration of additional metatypes to be used in our plugin. To make them known to Qt's meta-object system (to be able to put and get Unigine::UGUID to and out of the QVariant), we need Unigine::UGUID.该文件包含注册要在我们的插件中使用的附加元类型,以使 Qt 的元对象系统知道它们(为了能够将 Unigine::UGUID 放入和取出 QVariant),我们需要 Unigine::UGUID。
QtMetaTypes.h
#pragma once
// including UnigineGUID.h containing definition of the metatype we're going to declare
#include <UnigineGUID.h>
// including QMetaType to be able to declare metatypes
#include <QMetaType>
// registering the Unigine::UGUID type to make it known to Qt's meta-object system
Q_DECLARE_METATYPE(Unigine::UGUID)
Building the Plugin构建插件#
Using MS Visual Studio使用 MS Visual Studio#
In case you're using MS Visual Studio, there is a .vcxproj file in your plugin folder just select the Release configuration and choose Build Solution in the main menu. The project is pre-configured to launch with the UnigineEditor so you can run and test it right from your IDE (e.g. simply hit the default Ctrl + F5 hotkey in Visual Studio).如果您使用 MS Visual Studio,您的插件文件夹中有一个 .vcxproj 文件,只需选择Release配置并在主菜单中选择Build Solution即可。该项目已预先配置为使用 UnigineEditor 启动,因此您可以直接从 IDE 运行和测试它(例如,只需点击 Visual Studio 中的默认 Ctrl + F5 热键)。
Using CMake使用 CMake#
All files required to build our plugin using CMake (CMakeList.txt and Materials.json describing our Materials plugin and its dependencies) are automatically added to the corressponding folder of your plugin inside your UNIGINE project's source folder. So, launch CMake to build the plugin.使用 CMake 构建我们的插件所需的所有文件(CMakeList.txt 和 Materials.json 描述我们的 Materials 插件及其依赖项)会自动添加到您的 UNIGINE 项目的 source 文件夹内的插件的对应文件夹中。因此,启动 CMake 来构建插件。
Running the Plugin运行插件#
As the plugin is built, all resulting files are automatically copied to the %project_name%/bin/plugins/Unigine/ folder, so that UnigineEditor Plugin System could load it automatically.随着插件的构建,所有生成的文件都会自动复制到%project_name%/bin/plugins/Unigine/文件夹,这样UnigineEditor 的插件系统可以自动加载它。
And finally we launch UnigineEditor to check our plugin. If everything was done properly, you will see the MaterialsPlugin plugin in Editor Plugin Manager (Windows -> Editor Plugin Manager).最后我们启动 UnigineEditor 来检查我们的插件。如果一切正常,您应该会在Editor Plugin Manager (Windows -> Editor Plugin Manager) 中看到 MaterialsPlugin 插件。