Видеоуроки
Interface
Essentials
Advanced
Полезные советы
Принципы работы
Свойства (properties)
Компонентная Система
Рендер
Физика
Редактор 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
Программирование
Основы
Настройка среды разработки
Примеры использования
UnigineScript
C#
Унифицированный язык шейдеров UUSL
File Formats
Rebuilding the Engine Tools
GUI
Двойная точность координат
API
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
Работа с контентом
Оптимизация контента
Материалы
Art Samples
Tutorials

Создание C++ плагина

Вы можете загрузить специальный библиотечный модуль (файл *.dll, *.so или *.dylib) и получить доступ к его службам и библиотечным функциям во время выполнения Unigine через интерфейс класса Plugin. Использование системы плагинов позволяет выбирать, какие библиотеки загружать "на лету" без перекомпиляции исполняемого файла Unigine.

Чтобы создать библиотеку плагинов, вам необходимо выполнить инструкции, приведенные ниже.

Примечание
Образец библиотеки плагинов доступен в разделе Samples браузера UNIGINE SDK: Samples -> C++ API -> Systems -> Plugins. Чтобы получить доступ к проекту и исходному коду плагина, нажмите кнопку Open Folder.

Реализация интерфейса плагина#

Примечание
Перед реализацией интерфейса подключаемого модуля C ++ подготовьте среду разработки .

Чтобы реализовать интерфейс плагина, выполните следующие действия:

  1. Создайте проект плагина через C++ Visual Studio или C++ GNU Make (в зависимости от операционной системы).
  2. Включите все необходимые заголовки и укажите пространство имен Unigine, которое будет использоваться.
    Примечание
    Заголовок UniginePlugin.h должен быть обязательно включен, так как он необходим для доступа к методам класса Plugin.
    Исходный код (C++)
    #include <UniginePlugin.h>
    
    using namespace Unigine;
  3. Реализуйте методы и классы библиотеки динамической компоновки, которые будут доступны со стороны сценария или C ++ вашего приложения Unigine.
    Исходный код (C++)
    my_plugin_function() {...}
    
    class MyExternClass {
    		
    	public:
    		
    		MyExternClass() { }
    		~MyExternClass() { }
    		
    		
    		void my_member_function() {
    			...
    		}
    };
  4. Создайте класс плагина - реализацию интерфейса плагина, которая обеспечивает доступ к динамически подключаемой библиотеке с помощью исполняемого файла Unigine. Этот класс должен содержать следующее: конструктор, деструктор, функцию инициализации (init()), функцию выключения (shutdown()), функцию разрушения (destroy()), функция, возвращающая имя плагина (get_name()) и порядок выполнения (get_order()).
    Примечание
    У каждого плагина есть свой порядок выполнения, который определяет последовательность, в которой будут выполняться функции плагина (update() / postUpdate() / render() / shutdown()). Единственным исключением является функция init(), поскольку она вызывается сразу после загрузки плагина. Помните, что при написании плагина, который требует взаимодействия с другими, необходимо указать правильное значение порядка, чтобы избежать проблем и обеспечить правильную последовательность выполнения. Если в вашем случае порядок не имеет значения, установите значение по умолчанию 0.

    При необходимости вы также можете реализовать 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. Экспорт плагина: покажите созданную реализацию интерфейса плагина. Движок будет искать эти функции для добавления и выпуска библиотеки плагинов.
    Исходный код (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() и ReleasePlugin() объявлены как extern "C" для компиляции как обычные функции C. Это требуется для предотвращения искажения имен функций и, таким образом, обеспечения совпадения схемы оформления имен между исполняемым файлом Unigine и созданной библиотекой динамической компоновки. В противном случае двоичный код, сгенерированный двумя разными компиляторами C ++, может быть несовместим, поскольку не существует распознанного двоичного интерфейса приложения C ++.

Следующие реализации не нужны, но они позволяют расширить интерфейс плагина:

  • 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.
    Примечание
    Требуется включить UngineInterface.h для экспорта пользовательских классов и функций в скрипт.

    Например:

    Исходный код (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;
    }
  • Если вы собираетесь помешать инициализации движка, главному циклу или выключению, реализуйте классы, унаследованные отWorld/System/EditorLogicклассы. Вы можете реализовать свою собственную логику (а именно, функции обратного вызова) в этих классах, а затем добавить ее в движок для выполнения в его init(), update(), shutdown() и т. Д. Через интерфейс плагина.
    Примечание
    Для доступа к методам классов World / System / EditorLogic необходимо включить UnigineLogic.h.

    Например:

    Исходный код (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;
    }

Компиляция библиотеки#

Чтобы скомпилировать свою библиотеку, вы можете использовать один из способов, описанных в статье Создание приложения C ++ . Обратите внимание, что должны быть выполнены следующие требования:

  • Библиотека должна быть скомпилирована как .dll, .so или .dylib (в зависимости от используемой операционной системы).
  • Для Linux библиотека должна быть названа с префиксом lib (например, libplugin_x86.so).
  • В зависимости от разрядности используемой сборки Unigine скомпилируйте библиотеку как 32-разрядную версию (libname_x86) или 64-разрядную (libname_x64).
  • Для использования с отладочной сборкой Unigine необходимо скомпилировать отладочную версию библиотеки (libname_x86d или libname_x64d): укажите параметр командной строки debug=1.
  • Чтобы скомпилировать библиотеку с поддержкой двойной точности, укажите параметр командной строки double=1.

Вызов функций библиотеки из приложения Unigine#

Вы можете получить доступ к классам и функциям, экспортированным из созданной библиотеки динамической компоновки, как на уровне сценария, так и на уровне C ++.

Вызов функций библиотеки со стороны скрипта#

К классам и функциям библиотеки можно получить прямой доступ на стороне сценария, если они были правильно экспортированы . Например:

Исходный код (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;
}

Вызов функций библиотеки со стороны C ++#

Чтобы получить доступ к классам и функциям библиотеки со стороны C ++ приложения, вы должны сделать следующее:

  1. Включите файл заголовка плагина в свой код C ++:
    Исходный код (C++)
    #include "plugin.h"
    Примечание
    Если вы реализовали плагин только в файле .cpp, включите его вместо создания отдельного файла заголовка.
  2. Получите экземпляр плагина в мировой логике проекта:
    Исходный код (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. Скомпилируйте и запустите приложение C ++.

Загрузка библиотеки плагинов#

Библиотеку плагина можно загрузить в любой момент выполнения Unigine одним из следующих способов:

  • Передайте плагин как аргумент командной строки extern_plugin. После прохождения он записывается в файл конфигурации.
  • Укажите плагин прямо в конфигурационном файле (строка data/configs/default.boot, extern_plugin).
  • Добавьте и используйте плагин в скрипте мира через engine.addPlugin(). То же самое можно сделать в мировой логике проекта через Engine::addPlugin() на стороне C ++ или через Engine.addPlugin() на стороне C # проекта.
  • Добавьте и используйте плагин в системном скрипте (unigine.cpp):
    • Добавьте плагин через engine.addPlugin() и используйте его в мировом скрипте . Вы не можете инициализировать плагин в системном скрипте и одновременно вызывать из него функции плагина.
    • Используйте плагин в системном скрипте после его инициализации с помощью аргумента командной строки extern_plugin.
    То же самое можно сделать в системной логике проекта через Engine::addPlugin() на стороне C ++ или через Engine.addPlugin() on the C# side of the project.

Библиотека плагинов должна быть загружена при запуске двигателя. В следующем примере показано, как динамически связать библиотеку с помощью команды -extern_plugin:

  • Будет использована версия библиотеки, соответствующая имени исполняемого файла Unigine.
  • Имя библиотеки следует указывать без префиксов и постфиксов. Например, чтобы загрузить библиотеку plugin_x64d.dll при запуске приложения, исполняемый двоичный файл проекта Unigine должен быть запущен следующим образом:
    Shell-команды
    main.exe -extern_plugin data/plugin

    Исполняемый двоичный файл main.exe был создан во время сборки приложения.

    Примечание
    Если путь к библиотеке относительный, он должен быть относительно двоичного исполняемого файла . Он также может быть абсолютным.
Последнее обновление: 17.11.2020