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
Animations-Related Classes
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
VR-Related Classes
Работа с контентом
Оптимизация контента
Материалы
Визуальный редактор материалов
Material Nodes Library
Miscellaneous
Input
Math
Matrix
Textures
Art Samples
Учебные материалы

Доступ к нодам и файлам через свойства (Property)

Примечание
Подход, описанный в этой статье, не предназначен для проектов, использующих компонентную систему C#, где управление ассетами осуществляется с помощью компонентов.

Каждый ассет ( asset), используемый в вашем проекте, будь то нода, меш, материал, текстура или любой другой, имеет уникальный идентификатор (GUID). Идентификатор GUID определяет путь к ассету (т.е. местоположение ассета в проекте). Идентификаторы GUID используются для сохранения всех связей и зависимостей между ассетами, независимо от их имени и местоположения в проекте (при изменении имени или местоположения ассета в проекте его идентификатор GUID не изменяется).

Использование идентификаторов GUID для связывания ваших ассетов безопаснее, чем использование имен файлов, поскольку вам не нужно беспокоиться о том, что ваш материал потеряет текстуру при изменении его имени. Однако прямое управление идентификаторами GUID довольно запутанно.

Свойство позволяет привязывать определенные ассеты к ноде с помощью GUID, даже не задумываясь о них, предоставляя вам легкий доступ к этим ассетам. Существует ряд типов параметров свойств, делающих это возможным:

  • material - для материалов
  • property - для свойств
  • file - для всех остальных файлов (текстур, сеток, звуков и т.д.)

Тип параметра свойства node позволяет связать одну ноду с другой аналогичным образом, используя идентификатор.

Художники и программисты, разрабатывающие проект, должны иметь возможность работать независимо: художники готовят контент (текстуры, материалы, модели и т.д.), в то время как программисты пишут код, реализующий логику, которая выполняет определенные операции с контентом.

Использование свойств упрощает весь процесс:

  • Художники могут безопасно перемещать или переименовывать файлы и ноды. Программисты всегда работают со свойствами: создают их, устанавливают и считывают значения параметров (которые могут представлять ссылки на различные ассеты). Художники тоже могут устанавливать параметры свойств, они делают это через редактор.
  • Ни художники, ни программисты не должны работать с идентификаторами нод или идентификаторами GUID и запоминать их. Программист всегда имеет под рукой переменную (параметр свойства), обеспечивающую доступ к любой необходимой ноде или файлу.

Общий подход, основанный на свойствах, понятен и прост. Существует два основных случая, в зависимости от логики вашего проекта:

Общий подход#

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

  1. Сначала мы создаем свойство для хранения ссылок на все ноды и ассеты, которые нам нужны, и сохраняем его в папке data нашего проекта. Например, свойство может быть таким:

    Исходный код (XML)
    <?xml version="1.0" encoding="utf-8"?>
    <property version="2.16.0.2" name="my_property" parent_name="node_base" manual="1" editable="1">
    	<parameter name="some_float" type="float">30.5</parameter>
    	<parameter name="some_string" type="string">Hello from my_property!</parameter>
    	<parameter name="some_node" type="node">0</parameter>
    	<parameter name="some_material" type="material"></parameter>
    	<parameter name="some_mesh" type="file"></parameter>
    	<parameter name="some_file" type="file"></parameter>
    </property>
  2. Затем откройте UnigineEditor, выберите нужную ноду, нажмите Add new property и перетащите .prop файл в новое поле свойств, затем перетащите все необходимые ассеты и ноды в соответствующие поля свойства (см. видео ниже).

    Привязка нод и ассетов к свойству
  3. Поскольку мы не используем компоненты, нам придется привязываться к имени ноды, которой назначено свойство со ссылками на ассеты. Итак, в методе init() класса WorldLogic мы получаем ноду по ее имени:

    Исходный код (C++)
    int AppWorldLogic::init() 
    {
    	/* ... */
    
    	NodePtr node = World::getNodeByName("node_name");
    	
    	/* ... */
    
    	return 1;
    }
  4. Затем мы получаем свойство, присвоенное ему:

    Исходный код (C++)
    PropertyPtr property = node->getProperty();
  5. Теперь мы можем использовать это свойство для получения доступа к нодам и файлам:

    • чтобы получить материал, мы можем просто использовать соответствующий параметр ноды:

      Исходный код (C++)
      property->getParameterPtr("node_param_name")->getValueMaterial();
    • чтобы получить путь к файлу, мы можем просто использовать:

      Исходный код (C++)
      const char *path = property->getParameterPtr("file_param_name")->getValueFile();
      As we have a path to our file, we can use it, for example:
      Исходный код (C++)
      // to create a node reference
      NodeReferencePtr node_ref = NodeReference::create(path_to_node_file);
      
      // to load a sound source
      SoundSourcePtr sound = SoundSource::create(path_to_sound_file);

Давайте воспользуемся примером, чтобы проиллюстрировать этот подход.

Пример использования#

В этом примере мы будем работать с нодами и ассетами, связанными с определенной нодой, используя свойство с помощью C++ и C#.

Давайте создадим простой объект MeshStatic с именем my_object, унаследуем материал от mesh_base для назначения поверхностям нашего объекта и добавим какой-нибудь аудиофайл (*.mp3 или *.oga) в наш проект.

Итак, мы связываем файл *.mesh, материал, ноду material_ball из мира по умолчанию и аудиофайл, используя .prop файл описанный выше.

В нашем коде мы будем:

  • Вращать привязанную ноду.
  • Изменять привязанную материал и сохранять изменения.
  • Создавать новый объект, используя привязанный меш.
  • Проигрывать привязанный аудиофайл.

Реализация на C++#

Ниже вы найдете реализацию описанного выше примера на C++. Вы можете скопировать и вставить код в файл AppWorldLogic.cpp вашего проекта.

AppWorldLogic.cpp

Исходный код (C++)
#include "AppWorldLogic.h"
#include <UnigineMaterials.h>
#include <UnigineSounds.h>
#include <UnigineGame.h>
#include <UnigineWorld.h>
#include <UnigineFileSystem.h>

using namespace Unigine;
using namespace Math;
NodePtr my_node;                   // node to which a property with links is assigned
PropertyPtr property;              // property with all necessary links
MaterialPtr material;              // linked material
NodePtr param_node;                // linked node 
SoundSourcePtr sound;	      	   // sound source to be played 
ObjectMeshStaticPtr generated_obj; // object to be generated using the mesh

AppWorldLogic::AppWorldLogic() {

}

AppWorldLogic::~AppWorldLogic() {

}

int AppWorldLogic::init() {
	// getting the node to which a property with links to assets is assigned 
	my_node = World::getNodeByName("my_object");

	// getting the property, that will be used to access all necessary files
	property = my_node->getProperty();
	// using access to property parameters to perform desired actions
	if (property) {
		// getting a material from the corresponding property parameter
		material = property->getParameterPtr("some_material")->getValueMaterial();

		// getting the path to the mesh file from the corresponding property parameter
		const char *mesh_file_name = property->getParameterPtr("some_mesh")->getValueFile();

		// creating the object by using the mesh
		generated_obj = ObjectMeshStatic::create(mesh_file_name);

		// setting the object position relative to another node position
		generated_obj->setWorldPosition(my_node->getWorldPosition());
		generated_obj->translate(vec3(-1.0f, 0.0f, 0.0f));

		// getting the path to the sound file from the corresponding property parameter
		const char *sound_file_name = property->getParameterPtr("some_file")->getValueFile();

		// getting a node from the corresponding property parameter
		param_node = property->getParameterPtr("some_node")->getValueNode();

		// creating and playing a sound from the file
		sound = SoundSource::create(sound_file_name);
		sound->setMaxDistance(100.0f);
		sound->setLoop(1);
		sound->play();

		// reporting results to the console
		Log::message("Path to mesh file: %s\nPath to sound file: %s\nNode ID: %d\n", mesh_file_name, sound_file_name, param_node->getID());
	}
	return 1;
}

// start of the main loop
int AppWorldLogic::update() {
	// Write here code to be called before updating each render frame: specify all graphics-related functions you want to be called every frame while your application executes.

	float ifps = Game::getIFps();

	// changing the material
	material->setParameterFloat4("albedo_color", vec4(Game::getRandomFloat(0.0f, 1.0f), Game::getRandomFloat(0.0f, 1.0f), Game::getRandomFloat(0.0f, 1.0f), 1.0f));

	// rotate linked node
	param_node->setRotation(param_node->getRotation() * quat(0, 0, 30.0f * ifps));

	return 1;
}

int AppWorldLogic::postUpdate() {
	// The engine calls this function before rendering each render frame: correct behavior after the state of the node has been updated.

	return 1;
}

int AppWorldLogic::updatePhysics() {
	// Write here code to be called before updating each physics frame: control physics in your application and put non-rendering calculations.
	// The engine calls updatePhysics() with the fixed rate (60 times per second by default) regardless of the FPS value.
	// WARNING: do not create, delete or change transformations of nodes here, because rendering is already in progress.

	return 1;
}
// end of the main loop

int AppWorldLogic::shutdown() {
	// Write here code to be called on world shutdown: delete resources that were created during world script execution to avoid memory leaks.
	// saving current material color (check it in the UnigineEditor to see that it was modified)
	material->save();
	return 1;
}

int AppWorldLogic::save(const Unigine::StreamPtr &stream) {
	// Write here code to be called when the world is saving its state: save custom user data to a file.

	UNIGINE_UNUSED(stream);
	return 1;
}

int AppWorldLogic::restore(const Unigine::StreamPtr &stream) {
	// Write here code to be called when the world is restoring its state: restore custom user data to a file here.

	UNIGINE_UNUSED(stream);
	return 1;
}

Реализация на C##

Примечание
В проектах на C#, использующих компонентную систему C#, доступ к файлам и нодам обычно реализуется через компоненты.

Ниже вы найдете реализацию описанного выше примера на C#. Вы можете скопировать и вставить код в файл AppWorldLogic.cs вашего проекта.

AppWorldLogic.cs

Исходный код (C#)
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;

using Unigine;

namespace UnigineApp
{
	class AppWorldLogic : WorldLogic
	{
		// World logic, it takes effect only when the world is loaded.
		// These methods are called right after corresponding world script's (UnigineScript) methods.
		Node my_node;						// node to which a property with links is assigned
		Property property;					// property with all necessary links
		Material material;					// linked material
		Node param_node;					// linked node
		SoundSource sound;					// sound source to be played 
		ObjectMeshStatic generated_obj;		// object to be generated using the mesh

		public AppWorldLogic()
		{
		}

		public override bool Init()
		{
			// getting the node to which a property with links to assets is assigned 
			my_node = World.GetNodeByName("my_object");

			// getting the property, that will be used to access all necessary files
			property = my_node.GetProperty();
			// using access to property parameters to perform desired actions
			if (property) {
				// getting a material from the corresponding property parameter
				material = property.GetParameterPtr("some_material").ValueMaterial;

				// getting the path to the mesh file from the corresponding property parameter
				String mesh_file_name = property.GetParameterPtr("some_mesh").ValueFile;

				// creating the object by using the mesh
				generated_obj = new ObjectMeshStatic(mesh_file_name);

				// setting the object position relative to another node position
				generated_obj.WorldPosition = my_node.WorldPosition;
				generated_obj.Translate(-1.0f, 0.0f, 0.0f);
				
				// getting the path to the sound file from the corresponding property parameter
				String sound_file_name = property.GetParameterPtr("some_file").ValueFile;

				// getting a node from the corresponding property parameter
				param_node = property.GetParameterPtr("some_node").ValueNode;

				// creating and playing a sound from the file
				sound = new SoundSource(sound_file_name);
				sound.MaxDistance = 100.0f;
				sound.Loop = 1;
				sound.Play();

				// reporting results to the console
				Log.Message("Path to mesh file: {0}\nPath to sound file: {1}\nNode ID: {2}\n", mesh_file_name, sound_file_name, param_node.ID);
			}
			return true;
		}

		// start of the main loop
		public override bool Update()
		{
			// Write here code to be called before updating each render frame: specify all graphics-related functions you want to be called every frame while your application executes.
			float ifps = Game.IFps;

			// changing the material
			 material.SetParameterFloat4("albedo_color", new vec4(Game.GetRandomFloat(0.0f, 1.0f), Game.GetRandomFloat(0.0f, 1.0f), Game.GetRandomFloat(0.0f, 1.0f), 1.0f));
			
			// rotate linked node
			param_node.SetRotation(param_node.GetRotation() * new quat(0, 0, 30.0f * ifps));

			return true;
		}

		public override bool PostUpdate()
		{
			// The engine calls this function before rendering each render frame: correct behavior after the state of the node has been updated.
			return true;
		}

		// end of the main loop

		public override bool Shutdown()
		{
			// Write here code to be called on world shutdown: delete resources that were created during world script execution to avoid memory leaks.
			// saving current material color (check it in the UnigineEditor to see that it was modified)
			material.Save();

			return true;
		}

		public override bool Save(Stream stream)
		{
			// Write here code to be called when the world is saving its state: save custom user data to a file.
			return true;
		}

		public override bool Restore(Stream stream)
		{
			// Write here code to be called when the world is restoring its state: restore custom user data to a file here.
			return true;
		}
	}
}

Подход с использованием системы компонентов C++#

Если вы используете Компонентную систему C++ в своем проекте, рекомендуется следующая последовательность действий:

  1. Создайте компонент, унаследовав класс от ComponentBase. Шаблон этого класса доступен в заголовке UnigineComponentSystem.h.
  2. Добавьте поля для хранения ссылок на все необходимые ноды и файлы, материалы, меши и т.д. (с помощью макросов PROP_PARAM).
  3. Создайте файл *.prop для этого класса (путем компиляции и запуска приложения).
  4. Откройте свой мир в UnigineEditor и назначьте свойство сгенерированного компонента нужным нодам.
  5. Укажите все ноды, материалы, текстуры, меши, другие файлы, которые будут использоваться, перетащив их из Asset Browser непосредственно в соответствующее поле свойства в окне Parameters.

    Привязка нод и ассетов к свойству.
  6. Экземпляр компонента создается при запуске приложения. Этот экземпляр имеет переменные, обеспечивающие доступ ко всем используемым ассетам.

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

Глобальные свойства для нескольких миров#

Иногда вам может понадобиться иметь свойство со ссылками на ассеты (аналогичное описанному выше), которое вы хотите использовать в нескольких мирах, своего рода глобальное самодостаточное свойство, не назначенное ни одной ноде. Такое свойство может быть использовано, например, для хранения настроек для определенного типа оружия (FBX-модель, звуки стрельбы, ноды с системами частиц для визуальных эффектов и т.д.), которые будут использоваться глобально на различных уровнях игры.

Процедура здесь выглядит следующим образом:

  1. Сначала мы создаем свойство для хранения ссылок на все ноды и ассеты, которые нам нужны, и сохраняем его в папке data нашего проекта. Например, свойство может быть таким:

    Исходный код (XML)
    <?xml version="1.0" encoding="utf-8"?>
    <property version="2.16.0.2" name="my_property" parent_name="node_base" manual="1" editable="1">
    	<parameter name="some_float" type="float">30.5</parameter>
    	<parameter name="some_string" type="string">Hello from my_property!</parameter>
    	<parameter name="some_node" type="node">0</parameter>
    	<parameter name="some_material" type="material"></parameter>
    	<parameter name="some_mesh" type="file"></parameter>
    	<parameter name="some_file" type="file"></parameter>
    </property>
  2. Затем откройте UnigineEditor, найдите созданное свойство в окне Properties, щелкните по нему правой кнопкой мыши и выберите Create Child. Должно быть создано свойство с именем my_property_0 (вы можете переименовать его, если хотите).

    Создайте дочернее свойство
  3. Дизайнеры уровней подготавливают нужные настройки, перетаскивая необходимые ассеты и ноды в соответствующие поля дочернего свойства my_property_0, а также устанавливают другие параметры (если таковые имеются).

    Привязка нод и ассетов к свойству
  4. Программисты могут получить доступ к любому из этих ассетов через это глобальное свойство из любого мира. Поскольку мы не используем компоненты, нам нужно будет найти свойство со ссылками на ассеты по его имени (my_property_0). Итак, в методе init() класса WorldLogic мы делаем следующее:

    Исходный код (C++)
    int AppWorldLogic::init() 
    {
    	/* ... */
    
    	PropertyPtr property = Properties::findManualProperty("my_property_0");
    	
    	/* ... */
    
    	return 1;
    }
  5. Теперь мы можем использовать это свойство для получения доступа к нодам и файлам:

    • чтобы получить материал, мы можем просто использовать соответствующий параметр ноды:

      Исходный код (C++)
      property->getParameterPtr("node_param_name")->getValueMaterial();
    • чтобы получить путь к файлу, мы можем просто использовать:

      Исходный код (C++)
      const char *path = property->getParameterPtr("file_param_name")->getValueFile();

      Поскольку у нас есть путь до нашего файла мы можем, например, использовать его чтобы:

      Исходный код (C++)
      // to create a node reference
      NodeReferencePtr node_ref = NodeReference::create(path_to_node_file);
      
      // to load a sound source
      SoundSourcePtr sound = SoundSource::create(path_to_sound_file);

Реализация на C++#

Итак, реализация примера на C++ (описанная выше) для глобального свойства будет переписана, как показано ниже. Вы можете скопировать и вставить код в файл AppWorldLogic.cpp вашего проекта.

AppWorldLogic.cpp

Исходный код (C++)
#include "AppWorldLogic.h"
#include <UnigineMaterials.h>
#include <UnigineSounds.h>
#include <UnigineGame.h>
#include <UnigineWorld.h>
#include <UnigineFileSystem.h>

using namespace Unigine;
using namespace Math;
PropertyPtr property;              // property with all necessary links
MaterialPtr material;              // linked material
NodePtr param_node;                // linked node 
SoundSourcePtr sound;	      	   // sound source to be played 
ObjectMeshStaticPtr generated_obj; // object to be generated using the mesh

AppWorldLogic::AppWorldLogic() {

}

AppWorldLogic::~AppWorldLogic() {

}

int AppWorldLogic::init() {
	// getting the property that will be used to access all necessary files using its name
	property = Properties::findManualProperty("my_property_0");
	// using access to property parameters to perform desired actions
	if (property) {
		// getting a material from the corresponding property parameter
		material = property->getParameterPtr("some_material")->getValueMaterial();

		// getting the path to the mesh file from the corresponding property parameter
		const char *mesh_file_name = property->getParameterPtr("some_mesh")->getValueFile();

		// creating the object by using the mesh
		generated_obj = ObjectMeshStatic::create(mesh_file_name);

		// setting the object position relative to another node position
		generated_obj->setWorldPosition(my_node->getWorldPosition());
		generated_obj->translate(vec3(-1.0f, 0.0f, 0.0f));

		// getting the path to the sound file from the corresponding property parameter
		const char *sound_file_name = property->getParameterPtr("some_file")->getValueFile();

		// getting a node from the corresponding property parameter
		param_node = property->getParameterPtr("some_node")->getValueNode();

		// creating and playing a sound from the file
		sound = SoundSource::create(sound_file_name);
		sound->setMaxDistance(100.0f);
		sound->setLoop(1);
		sound->play();

		// reporting results to the console
		Log::message("Path to mesh file: %s\nPath to sound file: %s\nNode ID: %d\n", mesh_file_name, sound_file_name, param_node->getID());
	}
	return 1;
}

// start of the main loop
int AppWorldLogic::update() {
	// Write here code to be called before updating each render frame: specify all graphics-related functions you want to be called every frame while your application executes.

	float ifps = Game::getIFps();

	// changing the material
	material->setParameterFloat4("albedo_color", vec4(Game::getRandomFloat(0.0f, 1.0f), Game::getRandomFloat(0.0f, 1.0f), Game::getRandomFloat(0.0f, 1.0f), 1.0f));

	// rotate linked node
	param_node->setRotation(param_node->getRotation() * quat(0, 0, 30.0f * ifps));

	return 1;
}

int AppWorldLogic::postUpdate() {
	// The engine calls this function before rendering each render frame: correct behavior after the state of the node has been updated.

	return 1;
}

int AppWorldLogic::updatePhysics() {
	// Write here code to be called before updating each physics frame: control physics in your application and put non-rendering calculations.
	// The engine calls updatePhysics() with the fixed rate (60 times per second by default) regardless of the FPS value.
	// WARNING: do not create, delete or change transformations of nodes here, because rendering is already in progress.

	return 1;
}
// end of the main loop

int AppWorldLogic::shutdown() {
	// Write here code to be called on world shutdown: delete resources that were created during world script execution to avoid memory leaks.
	// saving current material color (check it in the UnigineEditor to see that it was modified)
	material->save();
	return 1;
}

int AppWorldLogic::save(const Unigine::StreamPtr &stream) {
	// Write here code to be called when the world is saving its state: save custom user data to a file.

	UNIGINE_UNUSED(stream);
	return 1;
}

int AppWorldLogic::restore(const Unigine::StreamPtr &stream) {
	// Write here code to be called when the world is restoring its state: restore custom user data to a file here.

	UNIGINE_UNUSED(stream);
	return 1;
}

Реализация на C##

Реализация примера на C# (описанная выше) для глобального свойства будет переписана, как показано ниже. Вы можете скопировать и вставить код в файл AppWorldLogic.cs вашего проекта.

AppWorldLogic.cs

Исходный код (C#)
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;

using Unigine;

namespace UnigineApp
{
	class AppWorldLogic : WorldLogic
	{
		// World logic, it takes effect only when the world is loaded.
		// These methods are called right after corresponding world script's (UnigineScript) methods.
		Property property;					// property with all necessary links
		Material material;					// linked material
		Node param_node;					// linked node
		SoundSource sound;					// sound source to be played 
		ObjectMeshStatic generated_obj;		// object to be generated using the mesh

		public AppWorldLogic()
		{
		}

		public override bool Init()
		{
			// getting the property that will be used to access all necessary files using its name
			property = Properties.FindManualProperty("my_property_0");
			// using access to property parameters to perform desired actions
			if (property) {
				// getting a material from the corresponding property parameter
				material = property.GetParameterPtr("some_material").ValueMaterial;

				// getting the path to the mesh file from the corresponding property parameter
				String mesh_file_name = property.GetParameterPtr("some_mesh").ValueFile;

				// creating the object by using the mesh
				generated_obj = new ObjectMeshStatic(mesh_file_name);

				// setting the object position relative to another node position
				generated_obj.WorldPosition = my_node.WorldPosition;
				generated_obj.Translate(-1.0f, 0.0f, 0.0f);
				
				// getting the path to the sound file from the corresponding property parameter
				String sound_file_name = property.GetParameterPtr("some_file").ValueFile;

				// getting a node from the corresponding property parameter
				param_node = property.GetParameterPtr("some_node").ValueNode;

				// creating and playing a sound from the file
				sound = new SoundSource(sound_file_name);
				sound.MaxDistance = 100.0f;
				sound.Loop = 1;
				sound.Play();

				// reporting results to the console
				Log.Message("Path to mesh file: {0}\nPath to sound file: {1}\nNode ID: {2}\n", mesh_file_name, sound_file_name, param_node.ID);
			}
			return true;
		}

		// start of the main loop
		public override bool Update()
		{
			// Write here code to be called before updating each render frame: specify all graphics-related functions you want to be called every frame while your application executes.
			float ifps = Game.IFps;

			// changing the material
			 material.SetParameterFloat4("albedo_color", new vec4(Game.GetRandomFloat(0.0f, 1.0f), Game.GetRandomFloat(0.0f, 1.0f), Game.GetRandomFloat(0.0f, 1.0f), 1.0f));
			
			// rotate linked node
			param_node.SetRotation(param_node.GetRotation() * new quat(0, 0, 30.0f * ifps));

			return true;
		}

		public override bool PostUpdate()
		{
			// The engine calls this function before rendering each render frame: correct behavior after the state of the node has been updated.
			return true;
		}

		// end of the main loop

		public override bool Shutdown()
		{
			// Write here code to be called on world shutdown: delete resources that were created during world script execution to avoid memory leaks.
			// saving current material color (check it in the UnigineEditor to see that it was modified)
			material.Save();

			return true;
		}

		public override bool Save(Stream stream)
		{
			// Write here code to be called when the world is saving its state: save custom user data to a file.
			return true;
		}

		public override bool Restore(Stream stream)
		{
			// Write here code to be called when the world is restoring its state: restore custom user data to a file here.
			return true;
		}
	}
}
Последнее обновление: 02.03.2023
Build: ()