This page has been translated automatically.
Видеоуроки
Интерфейс
Основы
Продвинутый уровень
Подсказки и советы
Основы
Программирование на C#
Рендеринг
Профессиональный уровень (SIM)
Принципы работы
Свойства (properties)
Компонентная Система
Рендер
Физика
Редактор UnigineEditor
Обзор интерфейса
Работа с ассетами
Контроль версий
Настройки и предпочтения
Работа с проектами
Настройка параметров ноды
Setting Up Materials
Настройка свойств
Освещение
Sandworm
Использование инструментов редактора для конкретных задач
Расширение функционала редактора
Встроенные объекты
Ноды (Nodes)
Объекты (Objects)
Эффекты
Декали
Источники света
Geodetics
World-ноды
Звуковые объекты
Объекты поиска пути
Player-ноды
Программирование
Основы
Настройка среды разработки
Примеры использования
C++
C#
UnigineScript
UUSL (Unified UNIGINE Shader Language)
Форматы файлов
Материалы и шейдеры
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
Работа с контентом
Оптимизация контента
Материалы
Визуальный редактор материалов
Сэмплы материалов
Material Nodes Library
Miscellaneous
Input
Math
Matrix
Textures
Art Samples
Учебные материалы
Внимание! Эта версия документация УСТАРЕЛА, поскольку относится к более ранней версии SDK! Пожалуйста, переключитесь на самую актуальную документацию для последней версии SDK.
Внимание! Эта версия документации описывает устаревшую версию SDK, которая больше не поддерживается! Пожалуйста, обновитесь до последней версии SDK.

Реализация логики Syncker для пользовательского проекта

Внимание
Функционал, описанный в этой статье, недоступен в Community редакции SDK.
Чтобы использовать этот функционал вам необходимо получить лицензию на Sim SDK.

Initializing SynckerИнициализация Syncker#

First of all, you should initialize Syncker. To do so you can simply add the following code to the AppSystemLogic class for both Master and Slave applications.Прежде всего, вы должны инициализировать Syncker. Для этого вы можете просто добавить следующий код в класс AppSystemLogic для приложений Master и Slave.

Insert the following Syncker initialization code to the AppSystemLogic::init() method:Вставьте следующий код инициализации Syncker в метод AppSystemLogic::init():

Исходный код (C++)
#include "AppSystemLogic.h"
#include <plugins/Unigine/Syncker/UnigineSyncker.h>

 /* .. */

using namespace Unigine;
using namespace Plugins;

/* .. */

int AppSystemLogic::init()
{
	// update application even if focus was lost
	Engine::get()->setBackgroundUpdate(Engine::BACKGROUND_UPDATE_RENDER_NON_MINIMIZED);

	// get the Syncker manager interface
	Syncker::Manager* syncker_manager = Syncker::Manager::get();

	// initialize the Syncker using command line arguments
	// or you can initialize Syncker as Master or Slave directly
	//	via initMaster() or initSlave() methods without using command-line arguments
	syncker_manager->initSyncker();

	// enable debug information in right bottom corner
	syncker_manager->getSyncker()->setDebug(true);

	return 1;
}

In the AppWorldLogic.cpp file add the code to get the pointer to the manager and Master interface for further use in the AppWorldLogic::init() method:В файле AppWorldLogic.cpp добавьте код для получения указателя на менеджер и интерфейс Master для дальнейшего использования в методе AppWorldLogic::init():

Исходный код (C++)
#include "AppWorldLogic.h"
#include <plugins/Unigine/Syncker/UnigineSyncker.h>
#include <UnigineWorld.h>
#include <UnigineGame.h>

using namespace Unigine;
using namespace Plugins;
using namespace Math;

int AppWorldLogic::init()
{

	Syncker::Manager *syncker_manager = Syncker::Manager::get();
	if (syncker_manager && syncker_manager->isMasterInitialized())
		syncker_master = syncker_manager->getMaster();

	return 1;
}

Synchronizing Nodes/MaterialsСинхронизация узлов / материалов#

Objects of the following types are synchronized automatically: ObjectWaterGlobal, ObjectCloudLayer, ObjectParticles, WorldLight if they present in the *.world file on all computers.Автоматически синхронизируются объекты следующих типов: ObjectWaterGlobal, ObjectCloudLayer, ObjectParticles, WorldLight, если они присутствуют в файле *.world на всех компьютерах.

Synchronization of transformations is supported for all types of nodes. As for other type-specific parameters synchronization is available only for the following ones:Синхронизация трансформаций поддерживается для всех типов узлов. По остальным параметрам, зависящим от типа, синхронизация доступна только для следующих:

Примечание
Automatic synchronization of run-time changes of the Global Terrain object is not supported, but it can be implemented manually using custom messages.Автоматическая синхронизация runtime-изменений объекта Global Terrain не поддерживается, но может быть реализована вручную. используя пользовательские сообщения .

To start synchronizing other nodes or materials (the logic is the same for both), that exist in the world, you just need to tell Syncker which of them you want. To do so you can add the following code to the AppWorldLogic::init() method of your master application:Чтобы начать синхронизацию других узлов или материалов (логика одинакова для обоих), существующих в мире, вам просто нужно указать Syncker, какой из них вам нужен. Для этого вы можете добавить следующий код в метод AppWorldLogic::init() вашего главного приложения:

Исходный код (C++)
int AppWorldLogic::init()
{

/* ... */

	// checking if we have a Master interface
	if (syncker_master)
	{
		// getting a pointer for the node named "material_ball"
		NodePtr my_node = World::getNodeByName("material_ball");

		// add the existing node to synchronization
		syncker_master->addSyncNode(my_node, Syncker::Master::SYNC_MASK::TRANSFORM);
	}

	return 1;
}

Now, you can implement your logic for synchronized nodes in the AppWorldLogic::update() method for Master:Теперь вы можете реализовать свою логику для синхронизированных узлов в методе AppWorldLogic::update() для Master:

Исходный код (C++)
int AppWorldLogic::update()
{

	/* ... */

	// checking if we have a Master interface
	if (syncker_master)
	{
		// getting a pointer for the node named "material_ball"
		NodePtr my_node = World::getNodeByName("material_ball");

		// changing node's rotation
		my_node->setRotation(my_node->getRotation() * Math::quat(0, 0, Game::getIFps()));
	}

	return 1;
}

Creating/Deleting Nodes at RuntimeСоздание / удаление узлов во время выполнения#

You can create and delete nodes at run time. To synchronize creation of a node on all connected Slave PCs, use the createNode() function.Вы можете создавать и удалять узлы во время выполнения. Чтобы синхронизировать создание узла на всех подключенных ПК Slave, используйте функцию createNode().

Примечание
Please note, that the createNode() method support only a limited number of node types, so it is recomended to load nodes from *.node files instead.Обратите внимание, что метод createNode() поддерживает только ограниченное количество типов узлов, поэтому рекомендуется узлы нагрузки из файлов *.node.
Исходный код (C++)
/* ... */
int cube_created = 0;

int AppWorldLogic::update()
{

	/* ... */

	// checking if we have a Master interface and cube is not yet created
	if (syncker_master && !cube_created)
	{
		// creating a dynamic cube and set up its transformation
		ObjectMeshStaticPtr dynamic_cube = ObjectMeshStatic::create("box.mesh");
		dynamic_cube->setPosition(Math::Vec3(Game::getRandomFloat(-50, 50), Game::getRandomFloat(-50, 50), Game::getRandomFloat(0, 50)));
		dynamic_cube->setRotation(Math::quat(Game::getRandomFloat(0, 360), Game::getRandomFloat(0, 360), Game::getRandomFloat(0, 360)));

		// commanding Slaves to create a dynamic node and adding it to synchronization
		syncker_master->createNode(dynamic_cube);

		cube_created = 1;
	}

	return 1;
}
Примечание
For this code to be compiled, copy the box.mesh file from the <Unigine SDK>/data/samples/common/meshes folder to the <your_unigine_project>/data/<your_unigine_project>/meshes folder.Для компиляции этого кода скопируйте файл box.mesh из папки <Unigine SDK>/data/samples/common/meshes в папку <your_unigine_project>/data/<your_unigine_project>/meshes.

Loading Nodes at RuntimeЗагрузка узлов во время выполнения#

To synchronize node loading from a *.node file on all connected Slave PCs, use the loadNode() or loadNodereference() methods. This approach is recommended as it allows adding nodes of all types, unlike the createNode() method that supports only a limited number of them.Чтобы синхронизировать загрузку узла из файла *.node на всех подключенных ПК Slave, используйте методы loadNode() или loadNodereference(). Этот подход рекомендуется, поскольку он позволяет добавлять узлы всех типов , в отличие от метода createNode(), который поддерживает только ограниченное их количество.

Исходный код (C++)
/* ... */
int node_loaded = 0;

int AppWorldLogic::update()
{

	/* ... */

	// checking if we have a Master interface and our node is not yet loaded
	if (syncker_master && !node_loaded)
	{

		// commanding Slaves to load a node and adding it to synchronization
		syncker_master->loadNode("my_node.node", Syncker::Master::SYNC_MASK::NODE_FLAGS | Syncker::Master::SYNC_MASK::TRANSFORM);

		node_loaded = 1;
	}

	return 1;
}

Customizing Synchronization via User MessagesНастройка синхронизации с помощью пользовательских сообщений#

Let us consider the following simple example: we can set transformation for a node, by simply sending rotation and position via a user message.Давайте рассмотрим следующий простой пример: мы можем установить трансформацию для узла, просто отправив поворот и положение через сообщение пользователя .

  1. First step is to create an object on Master and on all Slave PCs as a static one (without adding it to synchronization).Первый шаг - создать объект на Master и на всех ПК Slave как статический (без добавления его в синхронизацию).
  2. Next, we implement a callback function:Далее мы реализуем функцию обратного вызова:

    • on_message_received — to be called on receiving a user message. Here we extract the parameters from the received message and use them (in this case to change node's transformation).on_message_received - вызываться при получении пользовательского сообщения. Здесь мы извлекаем параметры из полученного сообщения и используем их (в данном случае для изменения трансформации узла).
  3. And the last step is to set this callback using the setMessageReceivedCallback() method in AppWorldLogic::init()И последний шаг - установить этот обратный вызов с помощью метода setMessageReceivedCallback() в AppWorldLogic::init()

    Исходный код (C++)
    // declaring and initializing pointers for the Master, Slave, and Syncker instances
    Unigine::Plugins::Syncker::Master* master = nullptr;
    Unigine::Plugins::Syncker::Slave* slave = nullptr;
    Unigine::Plugins::Syncker::Syncker* syncker = nullptr;
    
    // declaring a node, that we are going to transform via user messages
    NodePtr node;
    
    // declaring a blob to store our messages
    Unigine::BlobPtr blob = Unigine::Blob::create();
    
    // declaring message types to be used
    enum MESSAGE_TYPE
    {
    	REPORT,
    	TRANSFORM,
    };
    
    /* ... */
    
    /// callback function to be fired on receiving a user message
    void AppWorldLogic::on_message_received(const Unigine::BlobPtr& message)
    {
    	// reading an unsigned char from the blob defining message type (REPORT or TRANSFORM)
    	unsigned char type = message->readUChar();
    	switch (type)
    	{
    	case REPORT:
    	{
    		// printing to the console that we have received a message
    		Log::message("REPORT Message received!");
    	}
    	break;
    
    	case TRANSFORM:
    	{
    		// reading position and rotation from the message and using them to update node's transformation
    		Vec3 pos = Vec3(message->readDVec3());
    		quat rot = message->readQuat();
    		node->setTransform(translate(pos) * Mat4(rotate(rot)));
    	}
    	break;
    	}
    }
    
    int AppWorldLogic::init()
    {
    
       /* ... */
    	
    	// trying to get a Manager interface
    	auto manager = Syncker::Manager::get();
    	if (manager)
    	{
    		// checking if we have a Master interface
    		if (manager->isMasterInitialized())
    			master = manager->getMaster();
    		else if (manager->isSlaveInitialized())
    			slave = manager->getSlave();
    
    		// getting a pointer to Master/Slave base class
    		syncker = manager->getSyncker();
    	}
    
    	// subscribing to network messages
    	if (syncker)
    		syncker->setMessageReceivedCallback("net", MakeCallback(this, &AppWorldLogic::on_message_received));
    
    	/* ... */
    
    	// somewhere in code... sending a REPORT message
    	blob->clear();
    	blob->writeUChar(REPORT);
    	syncker->sendMessage("net", blob);
    
    	return 1;
    }
    
    int AppWorldLogic::update()
    {
    
    	/* ... */
    
    	// sending a message with object's transform to all peers
    	if (master)
    	{
    		Vec3 pos = node->getPosition();
    		quat rot = node->getRotation();
    		blob->clear();
    		blob->writeUChar(TRANSFORM);
    		blob->writeDVec3(dvec3(pos));
    		blob->writeQuat(rot);
    		syncker->sendMessage("net", blob);
    	}
    
    	/* ... */
    
    	return 1;
    }

Synchronizing Terrain ModificationsСинхронизация изменений ландшафта#

Run-time changes of the Global Terrain object are not synchronized automatically, but you can do it manually using custom messages:Изменения во время выполнения объекта Global Terrain не синхронизируются автоматически, но вы можете сделать это вручную, используя пользовательские сообщения:

Исходный код (C++)
Plugins::Syncker::Manager* syncker_manager = nullptr;

ObjectTerrainGlobalPtr terrain;

/// Method updating the Terrain
void AppWorldLogic::update_terrain(int detail, float threshold, float width, float contrast)
{
	// apply parameters to terrain
	terrain->getDetail(detail)->setMaskThreshold(threshold);
	terrain->getDetail(detail)->setMaskWidth(width);
	terrain->getDetail(detail)->setMaskContrast(contrast);

	if (syncker_manager)
	{
		auto master = syncker_manager->getMaster();
		if (master)
		{
			// send the "terrain_update" message with parameters
			BlobPtr blob = Blob::create();
			blob->writeInt(detail);
			blob->writeFloat(threshold);
			blob->writeFloat(width);
			blob->writeFloat(contrast);

			master->sendMessage("terrain_update", blob);
		}
	}
}

void AppWorldLogic::on_terrain_update(const Unigine::BlobPtr& message)
{
	// read parameters from the message in the same order
	int detail = message->readInt();
	float threshold = message->readFloat();
	float width = message->readFloat();
	float contrast = message->readFloat();
	// invoke update_terrain to apply parameters
	update_terrain(detail, threshold, width, contrast);
}

int AppWorldLogic::init()
{

	// getting the terrain object (this operation is performed once, so it should be in the init() method)
	terrain = checked_ptr_cast<ObjectTerrainGlobal>(World::getNodeByType(Node::OBJECT_TERRAIN_GLOBAL));

	// for Slave
	syncker_manager = Plugins::Syncker::Manager::get();
	if (syncker_manager)
	{
		auto slave = syncker_manager->getSlave();
		if (slave)
		{
			// set the on_terrain_update() method to be called on receiving the "terrain_update" message by a Slave.
			slave->setMessageReceivedCallback("terrain_update", MakeCallback(this, &AppWorldLogic::on_terrain_update));
		}
	}

	return 1;
}

int AppWorldLogic::update()
{

	 // for the Master
	if (Input::isKeyPressed(Input::KEY_T))
	{
		update_terrain(1, 2.0, 3.0f, 4.0f);
	}

	return 1;
}

Changing Views and CamerasИзменение видов и камер#

You can change the viewport on the Master or Slave side. For this purpose you can use the following code (example):Вы можете изменить вьюпорт на стороне Master или Slave. Для этого можно использовать следующий код (пример):

Исходный код (C++)
Plugins::Syncker::Manager* syncker_manager = nullptr;

	
	/* ...

	perform checks if the Syncker plugin is loaded
	and get the Syncker manager interface (Manager)

	 ... */

	 // if syncker is initialized, assigning the config mesh surface named "surface_0" to the 2nd monitor
	if (syncker_manager->isSynckerInitialized())
		syncker_manager->getSyncker()->setView(1, "surface_0");

	/* ... */

You can also tell Slave to use another camera. To do so, you can use the following code (example):Вы также можете указать Slave использовать другую камеру. Для этого вы можете использовать следующий код (пример):

Исходный код (C++)
/* ... */

PlayerSpectatorPtr aux_player = nullptr;

/* ... */

int AppWorldLogic::update()
{

	/* ... */

	// checking if we have a Master interface
	if (syncker_master && !aux_player)
	{
		// creating a new auxiliary player (on Master and Slaves)
		aux_player = PlayerSpectator::create();
		aux_player->setPosition(Vec3(0.0f, -40.0f, 200.0f));
		aux_player->setDirection(vec3(0.0f, 1.0f, 0.0f), vec3_up);
		aux_player->setMinVelocity(10);
		aux_player->setMaxVelocity(aux_player->getMinVelocity() * 2);

		// commanding Slaves to create a new player and adding it to synchronization
		syncker_master->createNode(aux_player, ~0);

		// setting a new created player for Slave(s) having sync_view = "view_1"
		syncker_master->setSlavePlayer("view_1", aux_player);
	}

	/* ... */

	return 1;
}
Последнее обновление: 23.06.2023
Build: ()