UnigineEditor
界面概述
资产工作流程
设置和首选项
项目开发
调整节点参数
Setting Up Materials
Setting Up Properties
照明
Landscape Tool
Sandworm (Experimental)
使用编辑器工具执行特定任务
Extending Editor Functionality
嵌入式节点类型
Nodes
Objects
Effects
Decals
Light Sources
Geodetics
World Objects
Sound Objects
Pathfinding Objects
Players
编程
基本原理
搭建开发环境
Usage Examples
UnigineScript
C#
UUSL (Unified UNIGINE Shader Language)
File Formats
Rebuilding the Engine Tools
GUI
双精度坐标
应用程序接口
Containers
Common Functionality
Controls-Related Classes
Engine-Related Classes
Filesystem Functionality
GUI-Related Classes
Math Functionality
Node-Related Classes
Objects-Related Classes
Networking Functionality
Pathfinding-Related Classes
Physics-Related Classes
Plugins-Related Classes
IG Plugin
CIGIConnector Plugin
Rendering-Related Classes

创建C ++插件

You can load a custom library module (a *.dll, *.so or *.dylib 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*.dylib文件),并通过Plugin类接口在Unigine运行时中访问其服务和库函数。 使用插件系统可以选择即时加载哪些库,而无需重新编译Unigine可执行文件。

To create the plugin library, you need to perform the instructions given below.要创建插件库,您需要执行以下说明。

注意
A sample of a plugin library is available in the Samples section of UNIGINE SDK Browser: Samples -> C++ API -> Systems -> Plugins. To access the plugin's project and the source code, press the Open Folder button.UNIGINE SDK浏览器的Samples部分提供了一个插件库示例:Samples -> C++ API -> Systems -> Plugins。要访问插件的项目和源代码,请按Open Folder按钮。

Implementing a Plugin Interface实施插件接口#

注意
Before implementing a C++ plugin interface, prepare the development environment.在实现C ++插件接口之前,请准备开发环境

To implement the plugin interface, perform the following steps:要实现插件接口,请执行以下步骤:

  1. Create a plugin project via C++ Visual Studio or C++ GNU Make (according to the operating system).通过C++ Visual StudioC++ GNU Make创建插件项目(根据操作系统)。
  2. Include all required headers and specify a Unigine namespace to be used.
    注意
    The UniginePlugin.h header must be included necessarily as it is required for accessing the Plugin class methods.The UniginePlugin.h header must be included necessarily as it is required for accessing the Plugin class methods.
    源代码 (C++)
    #include <UniginePlugin.h>
    
    using namespace Unigine;
    The UniginePlugin.h header must be included necessarily as it is required for accessing the Plugin class methods.
    包括所有必需的标头,并指定要使用的Unigine命名空间。
    注意
    The UniginePlugin.h header must be included necessarily as it is required for accessing the Plugin class methods.必须包含UniginePlugin.h标头,这是访问Plugin类方法所必需的。
    源代码 (C++)
    #include <UniginePlugin.h>
    
    using namespace Unigine;
  3. Implement methods and classes of the dynamic-link library that will be accessible from the script or C++ side of your Unigine application.
    源代码 (C++)
    my_plugin_function() {...}
    
    class MyExternClass {
    		
    	public:
    		
    		MyExternClass() { }
    		~MyExternClass() { }
    		
    		
    		void my_member_function() {
    			...
    		}
    };
    实现可从Unigine应用程序的脚本或C ++端访问的动态链接库的方法和类。
    源代码 (C++)
    my_plugin_function() {...}
    
    class MyExternClass {
    		
    	public:
    		
    		MyExternClass() { }
    		~MyExternClass() { }
    		
    		
    		void my_member_function() {
    			...
    		}
    };
  4. 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()), a function returning plugin's name (get_name()) and execution order (get_order()).创建一个插件类-插件接口的实现,该接口提供Unigine可执行文件对动态链接库的访问。此类必须包含以下内容:构造函数,析构函数,初始化函数(init()),关闭函数(shutdown()),销毁函数(destroy()),返回插件名称的函数(get_name())和执行顺序(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类中可用的其他函数。这些方法将在引擎执行序列的相应阶段自动调用。

    源代码 (C++)
    // 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;
    }
  5. 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.导出插件:公开创建的插件接口实现。引擎将搜索这些功能以添加和发布插件库。

    源代码 (C++)
    // 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. It 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:例如:

    源代码 (C++)
    // 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.访问World / System / EditorLogic类的方法时,必须包含UnigineLogic.h

    For example:例如:

    源代码 (C++)
    // 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, .so or .dylib (according to the operating system used).该库应编译为.dll.so.dylib(根据所使用的操作系统)。
  • For Linux the library should be named with the lib prefix (e.g. libplugin_x86.so).对于Linux,该库应以lib前缀(例如libplugin_x86.so)命名。
  • Depending on the bitness of Unigine build to be used, compile the library as a 32-bit version (libname_x86) or 64-bit one (libname_x64).根据要使用的Unigine构建的位数,将库编译为32位版本(libname_x86)或64位版本(libname_x64)。
  • To use with a Unigine debug build, a debug version of the library should be compiled (libname_x86d or libname_x64d): specify the debug=1 command-line option. 要与Unigine调试版本一起使用,应编译该库的调试版本(libname_x86dlibname_x64d):指定debug=1命令行选项。
  • To compile the library with double precision support, specify the double=1 command-line option. 要以双精度支持方式编译库,请指定double=1命令行选项。

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:如果正确导出,则可以在脚本端直接访问库的类和函数。例如:

源代码 (UnigineScript)
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 ++端访问库的类和函数,您应该执行以下操作:

  1. Include a plugin's header file in your C++ code:在C ++代码中包含插件的头文件:

    源代码 (C++)
    #include "plugin.h"
    注意
    If you implemented the plugin in the .cpp file only, include it instead of creating a separate header file.如果仅在.cpp文件中实现了插件,请包括该插件,而不要创建单独的头文件。
  2. Get the plugin instance in the project's world logic:在项目的世界逻辑中获取插件实例:

    源代码 (C++)
    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(UNIGINE_VERSION, argc, argv);
    
    	AppWorldLogic world_logic;
    	engine->main(NULL, &world_logic, NULL);
    
    	return 0;
    }
  3. 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.bootextern_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):
    • 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.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.
    • Use the plugin in the system script after initializing it via the command-line argument extern_plugin. Use the plugin in the system script after initializing it via the command-line argument 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 the project.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.Use the plugin in the system script after initializing it via the command-line argument extern_plugin.
    系统脚本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初始化插件后,请在系统脚本中使用该插件。
    在项目的系统逻辑中,可以通过C ++端的Engine::addPlugin()或项目C#端的Engine.addPlugin() on the C# side of the project.

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 plugin_x64d.dll library on the application start-up, the binary executable of the Unigine project should be run as follows:库的名称应指定为没有任何前缀或后缀。例如,要在应用程序启动时加载plugin_x64d.dll库,Unigine项目的二进制可执行文件应按以下方式运行:

    命令行
    main.exe -extern_plugin data/plugin

    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.如果库的路径是相对的,则它应相对于二进制可执行文件。它也可以是绝对的。
最新更新: 12pm30Asia/Novosibirsk-f2020Thu, 12 Nov 2020 18:40:22 +070011pm30