创建c++插件
You can load a custom library module (a *.dll or *.so file) and access its services and library functions in Unigine run-time via the Plugin class interface. Using a plugin system enables to choose which libraries to load on-the-fly without recompiling a Unigine executable.您可以加载自定义库模块(一个*.dll或*.so文件),并通过Plugin类接口在Unigine运行时访问其服务和库函数。 使用插件系统可以选择加载哪些库,而无需重新编译Unigine可执行文件。
To create the plugin library, you need to perform the instructions given below.要创建插件库,您需要执行下面给出的说明。
Implementing a Plugin Interface实现一个插件接口#
To implement the plugin interface, perform the following steps:实现插件接口的步骤如下:
- Create a plugin project via C++ Visual Studio or C++ GNU Make (depending on the operating system).
- For Visual Studio, ensure to specify all required dependencies in the project properties, including the paths to the include and lib directories of the UNIGINE SDK.For Visual Studio, ensure to specify all required dependencies in the project properties, including the paths to the include and lib directories of the UNIGINE SDK.
- In GNU Make, define the include and library directories of the UNIGINE SDK using flags in the Makefile. To specify the include directories, use the -I flag, and for the libraries, use the -L flag.In GNU Make, define the include and library directories of the UNIGINE SDK using flags in the Makefile. To specify the include directories, use the -I flag, and for the libraries, use the -L flag.
- For Visual Studio, ensure to specify all required dependencies in the project properties, including the paths to the include and lib directories of the UNIGINE SDK.对于 Visual Studio, 请确保在项目属性中指定所有必需的依赖项,包括 UNIGINE SDK 的 include 和 lib 目录的路径。
- In GNU Make, define the include and library directories of the UNIGINE SDK using flags in the Makefile. To specify the include directories, use the -I flag, and for the libraries, use the -L flag.在 GNU Make, 使用 Makefile 中的变量和标志定义 UNIGINE SDK 的包含目录和库目录。 要指定包含目录,请使用 -I 标志,对于库,请使用 -L 标志。
-
Include all required headers and specify a Unigine namespace to be used.包括所有必需的头文件,并指定要使用的Unigine名称空间。
#include <UniginePlugin.h> using namespace Unigine;
- Implement methods and classes of the dynamic-link library that will be accessible from the script or C++ side of your Unigine application.
实现动态链接库的方法和类,这些方法和类可以从Unigine应用程序的脚本或c++端访问。
my_plugin_function() {...} class MyExternClass { public: MyExternClass() { } ~MyExternClass() { } void my_member_function() { ... } };
my_plugin_function() {...} class MyExternClass { public: MyExternClass() { } ~MyExternClass() { } void my_member_function() { ... } };
-
Create a plugin class - an implementation of the plugin interface which provides access to the dynamic-link library by the Unigine executable. This class must contain the following: a constructor, a destructor, an initialization function (init()), a shut down function (shutdown()), a destroying function (destroy()).创建plugin类——插件接口的实现,通过Unigine可执行文件提供对动态链接库的访问。该类必须包含以下内容:构造函数、析构函数、初始化函数(init())、关闭函数(shutdown())、销毁函数(destroy())。
Don't forget to declare two functions:Don't forget to declare two functions:
- get_name() returning the plugin's name.get_name()返回插件的名称。
- get_order() returning the plugin's execution order.get_order()返回插件的执行顺序。
Each plugin has its execution order, which determines the sequence in which plugin's functions (update() / postUpdate() / render() / shutdown()) will be executed. The only exception is the init() function as it is called just after loading the plugin. Remember, when writing a plugin, that requires interaction with other ones, specifying correct order value is required to avoid issues and ensure proper execution sequence. If in your case the order doesn't matter, set the default 0 value.每个插件都有它的执行顺序,它决定了插件功能的顺序(update() / postUpdate() / render() / shutdown())将被执行。 唯一的例外是 init() 函数,因为它是在加载插件后立即调用的。 请记住,在编写需要与其他插件交互的插件时,需要指定正确的顺序值以避免出现问题并确保正确的执行顺序。 如果在您的情况下顺序无关紧要,请设置默认的 0 值。If necessary, you can also implement an update(), render(), swap() and other functions available in the Plugin class. These methods will be automatically called on the corresponding stage of the engine execution sequence.如果有必要,还可以实现一个update(), render(), swap()和Plugin类中可用的其他函数。这些方法将在引擎执行序列的相应阶段自动调用。
// 1. Declare the class class MyPlugin : public Plugin { public: // constructor MyPlugin() {...} // destructor virtual ~MyPlugin() {...} // plugin data virtual void *get_data() { return this; } // plugin name virtual const char * get_name() override { return "MyPluginName"; } // plugin execution order virtual int get_order() override { return 10; } // initialize the plugin virtual int init(); // shutdown the plugin virtual int shutdown(); // destroy the plugin virtual void destroy(); }; // 2. Define the plugin's initialization function. // The engine will automatically call it on its start-up int MyPlugin::init() { ... return 1; } // 3. Define the plugin shut down function. // The engine will automatically call it on its shut down int MyPlugin::shutdown() { ... return 1; } // 4. Define the function which is called on the video mode restart void MyPlugin::destroy() { ... return 1; }
Export the plugin: expose the created implementation of the plugin interface. The engine will search for these functions to add and release the plugin library.导出插件:公开创建的插件接口实现。引擎将搜索这些功能连接和断开插件库。
// 1. Define the function required for adding the plugin library. // It returns a new instance of the custom plugin extern "C" UNIGINE_EXPORT void *CreatePlugin() { return new MyPlugin(); } // 2. Define the function required for releasing the plugin library. // For that, a void pointer should be casted to the custom plugin type and then deleted extern "C" UNIGINE_EXPORT void ReleasePlugin(void *plugin) { delete static_cast<MyPlugin*>(plugin); }
CreatePlugin() and ReleasePlugin() functions are declared as extern "C" to be compiled as regular C functions. This is required to prevent mangling of the function names and thus ensure that the naming decoration scheme matches between the Unigine executable and the created dynamic-link library. Otherwise, binary code generated by two different C++ compilers may be incompatible, because there is no recognized C++ application binary interface.CreatePlugin()和ReleasePlugin()函数被声明为extern "C",以便编译为普通的C函数。这需要防止函数名的混淆,从而确保命名装饰方案在Unigine可执行文件和创建的动态链接库之间匹配。否则,由两个不同的c++编译器生成的二进制代码可能不兼容,因为没有公认的c++应用程序二进制接口。
The following implementations are not necessary, but they allow extending the plugin interface:下面的实现是没有必要的,但是他们允许扩展插件接口:
If you are going to use plugin's classes and functions on the script side of your application, you should implement exporting and removing of these classes and functions in the plugin's init() and shutdown() functions correspondingly. 如果你打算在应用程序的脚本端使用插件的类和函数,你应该在插件的init()和shutdown()函数中相应地实现导出和删除这些类和函数。
It is required to include UngineInterface.h for exporting custom classes and functions to the script.应包括UngineInterface.h出口定制类和函数的脚本。For example:例如:
// 1. Implement exporting of the custom functions and classes into the script // on plugin initialization int MyPlugin::init() { ... // create a pointer to the function and register its name to be used from the script Interpreter::addExternFunction("my_plugin_function",MakeExternFunction(&my_plugin_function)); // export the custom class into the script: // create a pointer to the object of the external class ExternClass<MyExternClass> *my_class = MakeExternClass<MyExternClass>(); // and register the class constructor. // if it was not declared explicitly, a default one will be registered my_class->addConstructor(); // register the class member function my_class->addFunction("my_member_function",&MyExternClass::my_member_function); // register the class Interpreter::addExternClass("MyExternClass",my_class); return 1; } // 2. Implement removing of the custom functions and classes on plugin shut down int MyPlugin::shutdown() { ... // remove the function Interpreter::removeExternFunction("my_plugin_function"); // remove the external class Interpreter::removeExternClass("MyExternClass"); return 1; }
If you are going to interfere the engine initialization, main loop or shutdown, implement classes inherited from the World/System/EditorLogic classes. You can implement your own logic (namely, callback functions) in these classes and then add it to the engine to be executed in its init(), update(), shutdown(), etc. via the plugin interface.如果你要干预引擎初始化,主循环或关闭,实现类继承自World/System/EditorLogic类。你可以实现自己的逻辑(即回调函数)在这些类,然后将其添加到引擎执行init(), update(), shutdown(),等通过插件接口。
It is required to include UnigineLogic.h for accessing the World/System/EditorLogic classes' methods.需要包含UnigineLogic.h来访问World/System/EditorLogic类的方法。For example:例如:
// 1. Declare the class which has the same logic as the system script: // all the implemented MySystemLogic class functions will be called by the engine // after corresponding system script's methods class MySystemLogic : public SystemLogic { public: virtual int init(); virtual int shutdown(); }; /* */ // 2. Implement the init() function that will be called // after system script initialization int MySystemLogic::init() { Log::message("MySystemLogic::init(): called\n"); return 1; } // 3. Implement the shutdown() function that will be called // after the system script shut down int MySystemLogic::shutdown() { Log::message("MySystemLogic::shutdown(): called\n"); return 1; } // 4. Declare the class which has the same logic as the world script: // all the implemented MyWorldLogic class functions will be called by the engine // after corresponding world script's methods class MyWorldLogic : public WorldLogic { public: virtual int init(); virtual int shutdown(); }; // 5. Implement the init() function that will be called // after the world script initialization int MyWorldLogic::init() { Log::message("MyWorldLogic::init(): called\n"); return 1; } // 6. Implement the shutdown() function that will be called // after the world script shut down int MyWorldLogic::shutdown() { Log::message("MyWorldLogic::shutdown(): called\n"); return 1; } // 7. Declare the class which has the same logic as the editor script: // all the implemented MyEditorLogic class functions will be called by the engine // after corresponding editor script's methods class MyEditorLogic : public EditorLogic { public: virtual int init(); virtual int shutdown(); }; /* */ // 8. Implement the init() function that will be called // after the editor script initialization int MyEditorLogic::init() { Log::message("MyEditorLogic::init(): called\n"); return 1; } // 9. Implement the shutdown() function that will be called // after the editor script shut down int MyEditorLogic::shutdown() { Log::message("MyEditorLogic::shutdown(): called\n"); return 1; } // 10. Declare instances of the classes inherited from System/World/EditorLogic classes in the plugin class class MyPlugin : public Plugin { public: MyPlugin() {...} virtual ~MyPlugin() {...} virtual void *get_data() { return this; } // plugin name virtual const char * get_name() override { return "MyPluginName"; } // plugin execution order virtual int get_order() override { return 0; } virtual int init(); virtual int shutdown(); virtual void destroy(); private: MySystemLogic system_logic; MyWorldLogic world_logic; MyEditorLogic editor_logic; }; // 11. Add C++ callbacks that are called on the engine initialization int MyPlugin::init() { ... Engine *engine = Engine::get(); // init() functions of the MySystem/MyWorld/MyEditorLogic classes will be called // after init() functions of the corresponding scripts engine->addSystemLogic(&system_logic); engine->addWorldLogic(&world_logic); engine->addEditorLogic(&editor_logic); return 1; } // 12. Remove C++ callbacks that are called on the engine shut down int MyPlugin::shutdown() { ... Engine *engine = Engine::get(); // shutdown() functions of the MySystem/MyWorld/MyEditorLogic classes will be called // after shutdown() functions of the corresponding scripts engine->removeSystemLogic(&system_logic); engine->removeWorldLogic(&world_logic); engine->removeEditorLogic(&editor_logic); return 1; }
Compiling the Library编译库#
To compile your library, you can use one of the ways described in the Creating C++ Application article. Note that the following requirements should be met:编译库,您可以使用创建c++应用程序文章中描述的方法之一。注意应满足以下要求:
- The library should be compiled as .dll or .so (according to the operating system used).该库应该被编译为.dll或.so(根据所使用的操作系统)。
- The UNIGINE naming convention should be followed, and the files should be stored in the corresponding folders.遵循UNIGINE命名约定,文件存放在相应文件夹。
- To use with a Unigine debug build, a debug version of the library should be compiled (libname_x64d or libname_double_x64d for double builds): specify the debug=1 command-line option. 要与Unigine调试构建一起使用,应该编译库的调试版本(libname_x64d或libname_double_x64d对于双坐标精度):指定debug=1命令行选项。
- To compile the library with double precision support, specify the double=1 command-line option. 在双精度的支持下,编译这个库指定double=1命令行选项。
Naming Convention命名约定#
Libraries compiled by you should follow the UNIGINE naming convention:你编译的库应该遵循UNIGINE命名约定:
- <VendorName><PluginName>_plugin_<precision>_x64<debug_version>.* for Windows binaries<VendorName><PluginName>_plugin_<precision>_x64<debug_version>.* Windows二进制文件
- lib<VendorName><PluginName>_plugin_<precision>_x64<debug_version>.so for Linux binariesLinux二进制文件的lib<VendorName><PluginName>_plugin_<precision>_x64<debug_version>.so
- <VendorName><PluginName>.h for headers<VendorName><PluginName>.h头
- Editor plugins require the _editorplugin postfix instead of _plugin编辑器插件需要_editorplugin后缀而不是1_ight
Clarifications:澄清:
<precision> implies single or double precision of coordinates: in case of single precision you write nothing, in case of double precision — the word double.
Examples: MyPluginName_plugin_double_x64.dll — for double precision file, MyPluginName_plugin_x64.dll — for single precision file.<precision>意味着单引号或双坐标精度:单精度情况下什么都不用写,双精度情况下用double。
例子:MyPluginName_plugin_double_x64.dll -双精度文件,MyPluginName_plugin_x64.dll -单精度文件。
<debug_version> identifies that your library is a debug or release version. If debug, then d is added; if release — nothing is added.
Examples: MyPluginName_plugin_double_x64d.dll — debug version, MyPluginName_plugin_double_x64.dll — release version.<debug_version>标识您的库是调试或发布版本。如果是debug,则添加d;如果是发布,什么都不用写。
示例:MyPluginName_plugin_double_x64d.dll -调试版本,MyPluginName_plugin_double_x64.dll -发布版本。
An example name for the editor plugin: MyEditorPluginName_editorplugin_double_x64d.dll.一个例子的名称编辑插件:MyEditorPluginName_editorplugin_double_x64d.dll。
Path to Plugin Files插件文件路径#
UNIGINE plugins are located in the corresponding folders:UNIGINE插件位于相应的文件夹:
- binaries — bin\plugins\Unigine\<PluginName> (e.g. bin\plugins\Unigine\ARTTracker\UnigineARTTracker_plugin_double_x64d.dll)二进制文件: bin\plugins\Unigine\<PluginName>(例如bin\plugins\Unigine\ARTTracker\UnigineARTTracker_plugin_double_x64d.dll)
- headers — include\plugins\Unigine\<PluginName>\Unigine<PluginName>.h (e.g. include\plugins\Unigine\ARTTracker\UnigineARTTracker.h)头文件: include\plugins\Unigine\<PluginName>\Unigine<PluginName>.h (e.g. include\plugins\Unigine\ARTTracker\UnigineARTTracker.h)
User plugins similarly should be located in the corresponding folders:用户插件同样应该位于相应的文件夹中:
- binaries — bin\plugins\<VendorName>\<PluginName> (e.g. bin\plugins\Vendor\Plugin\VendorPlugin_plugin_double_x64.dll)二进制文件: bin\plugins\<VendorName>\<PluginName>(例如bin\plugins\Vendor\Plugin\VendorPlugin_plugin_double_x64.dll)
- headers — include\plugins\<VendorName>\<PluginName>\<VendorName><PluginName>.h (e.g. include\plugins\Vendor\Plugin\VendorPlugin.h)头文件: include\plugins\<VendorName>\<PluginName>\<VendorName><PluginName>.h (e.g. include\plugins\Vendor\Plugin\VendorPlugin.h)
Calling Library Functions from Unigine Application从Unigine调用库函数的应用程序#
You can access classes and functions exported from the created dynamic-link library from both the script and C++ level.您可以访问的类和函数导出创建动态链接库的脚本和c++的水平。
Calling Library Functions from Script Side从脚本中调用库函数#
Classes and functions of the library can be directly accessed on the script side if they were properly exported. For example:如果库中的类和函数正确地导出,则可以在脚本端直接访问它们。例如:
int init() {
log.message("\n");
// call the function exported from the library
my_print("Hello world");
log.message("\n");
// create new instances of the class exported from the library
MyExternClass my_class = new MyExternClass();
// call the exported member functions of the class
my_class.print("Hello world");
delete my_class;
return 1;
}
Calling Library Functions from C++ Side从c++端调用库函数#
To access classes and functions of the library from the C++ side of the application, you should do the following:要从应用程序的c++端访问库的类和函数,您应该执行以下操作:
Include a plugin's header file in your C++ code:一个插件的头文件包含在您的c++代码:
#include "plugin.h"
If you implemented the plugin in the .cpp file only, include it instead of creating a separate header file.如果您只在.cpp文件中实现了插件,请包含它,而不是创建一个单独的头文件。Get the plugin instance in the project's world logic:获得插件实例项目的世界逻辑:
class AppWorldLogic : public WorldLogic { public: AppWorldLogic() {} virtual ~AppWorldLogic() {} virtual int init() { // 1. Assume that the plugin has been loaded EnginePtr engine; // specify a path to the plugin relative to the binary executable int id = engine->findPlugin("plugin/plugin"); if (id == -1) { Log::message("Cannot find the plugin\n"); return 0; } // 2. Get the plugin interface Plugin *plugin = Engine::get()->getPluginInterface(id); if (!plugin) { Log::message("The plugin is null\n"); return 0; } // 3. Cast the plugin interface to your plugin class MyPlugin *plugin = static_cast<MyPlugin*>(plugin); if (!plugin) { Log::message("Cannot cast the plugin\n"); return 0; } // call the plugin function my_plugin_function(); // create a new instance of the class declared in the plugin MyExternClass *extern_class = new MyExternClass(); // and call its member functions extern_class->my_member_function(); return 1; } }; // start the main loop with the world logic int main(int argc, char **argv) { EnginePtr engine(argc, argv); AppWorldLogic world_logic; engine->main(NULL, &world_logic, NULL); return 0; }
- Compile and run the C++ application. 编译并运行c++应用程序。
Loading the Plugin Library加载插件库#
The plugin library can be loaded at any moment of Unigine runtime by using one of the following ways: 插件库可以在Unigine运行时的任何时刻加载,使用以下方式之一:
- Pass the plugin as a command-line argument extern_plugin. Once passed, it is written into the configuration file. 通过插件作为 extern_plugin命令行参数。一旦通过,就写进配置文件。
- Specify the plugin directly in the configuration file (data/configs/default.boot, extern_plugin string).在配置文件中直接指定插件(data/configs/default.boot, extern_plugin 字符串)。
- Add and use the plugin in the world script via engine.addPlugin(). The same can be done in the project's world logic via Engine::addPlugin() on the C++ side or via Engine.addPlugin() on the C# side of the project.通过 engine.addPlugin() 在世界脚本中添加和使用插件。 在项目的世界逻辑中,同样可以通过c++端的Engine::addPlugin() 或c#端的 Engine.addPlugin() 来完成。
-
Add and use the plugin in the system script (unigine.cpp):在系统脚本 (unigine.cpp)中添加并使用插件:
- Add the plugin via engine.addPlugin() and use it in the world script. You cannot initialize the plugin in the system script and call plugin functions from it at the same time.通过 engine.addPlugin() 添加插件,并在世界脚本 中使用它。 您不能在系统脚本中初始化插件并同时从中调用插件函数。
- Use the plugin in the system script after initializing it via the command-line argument extern_plugin. 通过命令行参数 extern_plugin初始化插件后,在系统脚本中使用该插件。
The same can be done in the project's system logic via Engine::addPlugin() on the C++ side or via Engine.addPlugin() on the C# side of your project.在项目的系统逻辑中,同样可以通过c++端的Engine::addPlugin() 或c#端的 Engine.addPlugin() 来完成。
The plugin library should be loaded at the engine start-up. The following example shows how to dynamically link the library via the -extern_plugin command:插件库应该在引擎启动时加载。下面的例子展示了如何通过-extern_plugin命令动态链接库:
- The library version that corresponds to the name of the Unigine executable will be used.对应的链接库的版本将使用Unigine可执行文件的名称。
-
The name of the library should be specified without any prefixes or postfixes. For example, to load the VendorNamePluginName_plugin_x64d.dll library on the application start-up, the binary executable of the Unigine project should be run as follows:库的名称应该指定为不带任何前缀或后缀的。例如,要在应用程序启动时加载VendorNamePluginName_plugin_x64d.dll库,应该运行Unigine项目的二进制可执行文件如下:
main.exe -extern_plugin VendorNamePluginName
The main.exe binary executable was created during the application building.main.exe二进制可执行文件中创建应用程序构建。
If the path to the library is relative, it should be relative to the binary executable. It can also be absolute.如果库的路径是相对的,它应该相对于二进制可执行文件。它也可以是绝对的。