Programming
Fundamentals
Setting Up Development Environment
Usage Examples
UnigineScript
High-Level Systems
C++
C#
UUSL (Unified UNIGINE Shader Language)
File Formats
Rebuilding the Engine and Tools
GUI
Double Precision Coordinates
API
Containers
Common Functionality
Controls-Related Classes
Engine-Related Classes
Filesystem Functionality
GUI-Related Classes
Math Functionality
Node-Related Classes
Networking Functionality
Pathfinding-Related Classes
Physics-Related Classes
Plugins-Related Classes
Rendering-Related Classes

16. Creating User Interface

<< RETURN TO THE PREVIOUS SECTION

In UNIGINE a Graphical User Interface (GUI) is composed of different types of widgets added to it. Basically, there are two ways of creating GUI:

  • By adding widgets to the system GUI (Unigine user interface) that is rendered on top of application window.
  • By adding widgets to a GUI object positioned in the world. In this case, any postprocessing filter can be applied.
To add elements to the system GUI you should use the Gui class.
Notice
In order to use GUI widgets we must include the UnigineUserInterface.h library.

There are 2 ways to create the GUI layout:

The following code demonstrates how to add a label and a slider to the system GUI:
Source code (C++)
#include <UnigineUserInterface.h>
using namespace Unigine;
GuiPtr gui;

int AppWorldLogic::init() 
{
	// getting a GUI pointer
	gui = Gui::get();

	// creating a label widget and setting up its parameters
	WidgetLabelPtr widget_label = WidgetLabel::create(gui, "Label text:");
	widget_label->setToolTip("This is my label!");
	widget_label->arrange();
	widget_label->setPosition(10, 10);

	// creating a slider widget and setting up its parameters
	WidgetSliderPtr widget_slider = WidgetSlider::create(gui, 0, 360, 90);
	widget_slider->setToolTip("This is my slider!");
	widget_slider->arrange();
	widget_slider->setPosition(100, 10);
}

In order to use GUI elements we must specify handlers for various events (click, change, etc.). The following code demonstrates how to set event handlers

Source code (C++)
#include <UnigineUserInterface.h>
using namespace Unigine;

/// function to be called when button1 is clicked
int onButton1Clicked()
{
	/* .. */
}

/// method to be called when button2 is clicked
int AppWorldLogic::onButton2Clicked()
{
	/* .. */
}

/// method to be called when slider position is changed
int AppWorldLogic::onSliderChanged()
{
	/* .. */
}
		
int AppWorldLogic::init()
{

	/* .. */
	
	// getting a GUI pointer
	gui = Gui::get();

	// setting onButton1Clicked function as a clicked event handler for a buttonwidget2 using a CallbackBase variable
	Unigine::CallbackBase *button1_clicked_callback_function = MakeCallback(onButton1Clicked);
	buttonwidget1->setCallback0(Gui::CLICKED, button1_clicked_callback_function);

	// setting AppWorldLogic::onButton2Clicked method as a clicked event handler for a buttonwidget2 using a CallbackBase variable
	Unigine::CallbackBase *button2_clicked_callback_method = MakeCallback(this, &AppWorldLogic::onButton2Clicked);
	buttonwidget2->setCallback0(Gui::CLICKED, button2_clicked_callback_method);
	
	buttonwidget1->setCallback0(Gui::CLICKED, MakeCallback(this, &AppWorldLogic::onButtonDelClicked));

	// setting AppWorldLogic::onSliderChanged method as a changed event handler for a widget_slider
	widget_slider->setCallback0(Gui::CHANGED, MakeCallback(this, &AppWorldLogic::onSliderChanged));
	/* .. */
	
}

Additional information:

Project Progress

In our project we are going to add the following widgets to the system GUI:

  • Label (WidgetLabel) to display the name of the parameter controlled by the slider.
  • Slider (WidgetSlider) to control the position of the sun manually.
  • Checkbox (WidgetCheckbox) to enable/disable automatic change of the sun position.
  • 4 Buttons (WidgetButton): Close Application, Load World, Load Mesh, and Delete.
  • ComboBox (WidgetComboBox) to display the list of scene objects.
  • File Dialog (WidgetDialogFile) to load a mesh from a file.
  • Window (Widget) to set mesh parameters.

We are going to create all widgets directly from the code, but the last one (Mesh parameters window) we are going to load from a UI file. For this purpose let us create a file with the following contents and save it to the data folder of our application as "user_interface.ui"

Source code (XML)
<?xml version="1.0" encoding="utf-8"?>
<ui version="1.0">
<window name="window" export="1" space="4" sizeable="1" width="512">
	<text>UserInterface</text>
	<gridbox columns="2" space="8" align="expand">
		<label name="label_n" align="expand">
			<text>Name:</text>
		</label>
		<editline name="object_name" align="expand">
			<text></text>
		</editline>
		<label name="label_x" align="expand">
			<text>X coordinate:</text>
		</label>
		<editline name="coord_x" align="expand">
			<text></text>
		</editline>
		<label name="label_y" align="expand">
			<text>Y coordinate:</text>
		</label>
		<editline name="coord_y" align="expand">
			<text></text>
		</editline>
		<label name="label_z" align="expand">
			<text>Z coordinate:</text>
		</label>
		<editline name="coord_z" align="expand">
			<text></text>
		</editline>
	</gridbox>				
	<button name="button_OK" align="bottom">
		<text>OK</text>
	</button>
</window>
</ui>

To create our GUI let us write event handler methods for the elements of our UI and a method called initGUI to initialize our UI.

In the AppWorldLogic.h file, we:

  • include the UnigineUserInterface.h library,
  • define smart pointers for the widgets we are going to use,
  • declare event handlers for each widget and our initGUI method.
Source code (C++)
// AppWorldLogic.h

/* .. */

#include <UnigineUserInterface.h>

/* .. */

class AppWorldLogic : public Unigine::WorldLogic {
	
public:

/* .. */

	// GUI event handlers
	int onButtonDelClicked();
	int onButtonCloseClicked();
	int onButtonFopenClicked();
	int onButtonWloadClicked();
	int onDlgCancelClicked();
	int onDlgOKClicked();
	int onMeshParamsOKClicked();
	int onSliderChanged();
	
/* .. */

private:

/* .. */

	// creating GUI
	int initGUI();
	
/* .. */

	// pointers to the UI and its elements
	Unigine::GuiPtr gui;
	Unigine::UserInterfacePtr ui;
	Unigine::WidgetLabelPtr widget_label;
	Unigine::WidgetSliderPtr widget_slider;
	Unigine::WidgetCheckBoxPtr widget_checkbox;
	Unigine::WidgetButtonPtr widget_button_del;
	Unigine::WidgetButtonPtr widget_button_close;
	Unigine::WidgetButtonPtr widget_button_fopen;
	Unigine::WidgetButtonPtr widget_button_wload;
	Unigine::WidgetComboBoxPtr widget_combo;
	Unigine::WidgetDialogFilePtr widget_file_dialog;
	Unigine::WidgetPtr mesh_parameters_window;
	
/* .. */


};

In the AppWorldLogic.cpp file let us:

  • implement all our event handlers,
  • implement the initGUI method and insert it into the AppWorldLogic::init() method,
  • make some changes to the following methods in order to integrate our UI:
    • updateLights (check if automatic mode is enabled to change sun position),
    • addMeshToScene (add the name of the new created object to the combo box an enable Delete button if it was disabled),
    • removeMeshFromScene (update the combo box and disable Delete button if the last object was deleted).
Source code (C++)
// AppWorldLogic.cpp

/* .. */

//-----------------------------------------------------------------------------------------------------------------------------
//------------------------------------------------------- GUI EVENT HANDLERS --------------------------------------------------
//-----------------------------------------------------------------------------------------------------------------------------
/// method to be called when the Close button is clicked in the main window
int AppWorldLogic::onButtonCloseClicked()
{
	// removing generated materials and properties
	clearMaterials();
	clearProperties();

	// saving current world to my_world.world file
	Console::get()->run("world_save my_world");

	// executing all pending console commands before exiting
	Console::get()->flush();

	// closing the application
	App::get()->exit();

	return 1;
}
//-----------------------------------------------------------------------------------------------------------------------------
/// method to be called when the Load world button is clicked in the main window
int AppWorldLogic::onButtonWloadClicked()
{
	// reporting to console
	Log::warning("\n\n --- RELOADING A DEFAULT WORLD --- \n\n");

	// loading a world from default.world file
	Console::get()->run("world_load default");

	return 1;
}
//-----------------------------------------------------------------------------------------------------------------------------
/// method to be called when the Cancel button is clicked in the main window
int AppWorldLogic::onButtonFopenClicked()
{
	widget_file_dialog->setHidden(0);

	return 1;
}
//-----------------------------------------------------------------------------------------------------------------------------
/// method to be called when the slider position is changed in the main window
int AppWorldLogic::onSliderChanged()
{
	// updating the position of the Sun
	sun_angle = widget_slider->getValue();

	return 1;
}
//-----------------------------------------------------------------------------------------------------------------------------
/// method to be called when the Delete button is clicked in the main window
int AppWorldLogic::onButtonDelClicked()
{
	String node_name;

	if (widget_combo->getCurrentItem() == 0)									// deleting all objects 
	{
		removeObjects();
		
		// disabling Delete button in the main window
		widget_button_del->setEnabled(0);
	}
	else																		// deleting an object selected in the combo box
	{
		// retrieving currently selected node name in the combo box
		node_name = widget_combo->getCurrentItemText();

		// checking if a node with a given name exists and removing it from the scene
		if (editor->findNode(node_name.get()) != -1)
		{
			removeMeshFromScene(node_name.get());
		}
	}

	return 1;
}
//-----------------------------------------------------------------------------------------------------------------------------
/// method to be called when the Cancel button is clicked in the File Open dialog
int AppWorldLogic::onDlgCancelClicked()
{
	// hiding the file selecton dialog
	widget_file_dialog->setHidden(1);

	return 1;
}
//-----------------------------------------------------------------------------------------------------------------------------
/// method to be called when the OK button is clicked in the File Open dialog
int AppWorldLogic::onDlgOKClicked()
{
	// checking if the file exists
	if (FILE *file = fopen(widget_file_dialog->getFile(), "r")) {
		fclose(file);

		//setting initial values of the fields in the Mesh Parameters window
		String object_name = String::format("Object_%d", widget_combo->getNumItems() - 1);
		WidgetEditLine::cast(ui->getWidget(ui->findWidget("object_name")))->setText(object_name.get());
		WidgetEditLine::cast(ui->getWidget(ui->findWidget("coord_x")))->setText("0.0");
		WidgetEditLine::cast(ui->getWidget(ui->findWidget("coord_y")))->setText("0.0");
		WidgetEditLine::cast(ui->getWidget(ui->findWidget("coord_z")))->setText("0.0");

		// hiding the File Open dialog and showing Mesh Parameters window using different ways
		widget_file_dialog->setHidden(1);
		mesh_parameters_window->setHidden(0);

		return 1;
	}

	// reporting an error to the console
	Log::error("Loading mesh: can't open \"%s\" file\n", widget_file_dialog->getFile());
	return 0;
}
//-----------------------------------------------------------------------------------------------------------------------------
/// method to be called when the OK button is clicked in the Mesh Parameters window
int AppWorldLogic::onMeshParamsOKClicked()
{
	//retrieving pointers to UI elements
	WidgetEditLinePtr name = WidgetEditLine::cast(ui->getWidget(ui->findWidget("object_name")));
	WidgetEditLinePtr x = WidgetEditLine::cast(ui->getWidget(ui->findWidget("coord_x")));
	WidgetEditLinePtr y = WidgetEditLine::cast(ui->getWidget(ui->findWidget("coord_y")));
	WidgetEditLinePtr z = WidgetEditLine::cast(ui->getWidget(ui->findWidget("coord_z")));

	if ((x->getText() != "") && (y->getText() != "") && (z->getText() != "") && (name->getText() != ""))
	{

		Math::Vec3 pos = Math::Vec3(atof(x->getText()), atof(y->getText()), atof(z->getText()));
		String result = String((addMeshToScene(widget_file_dialog->getFile(), name->getText(), "my_mesh_base3", pos)) ? "Ok!" : "Error!");
		
		// reporting progress to the console
		Log::message("(%s) mesh load operation status: %s\n", name->getText(), result.get());

		// hiding the Mesh Parameters window
		mesh_parameters_window->setHidden(1);

		return 1;
	}
	Log::error("Wrong mesh parameters!\n");
	return 0;
}

/* .. */

/// method performing initialization of the GUI
int AppWorldLogic::initGUI()
{
	// getting a GUI pointer
	gui = Gui::get();

	// creating a label widget and setting up its parameters
	widget_label = WidgetLabel::create(gui, "Sun position:");
	widget_label->setToolTip("Change sun position");
	widget_label->setPosition(10, 10);

	// creating a slider widget and setting up its parameters
	widget_slider = WidgetSlider::create(gui, 0, 360, 90);
	widget_slider->setToolTip("Change sun position");
	widget_slider->setPosition(100, 10);
	widget_slider->setCallback0(Gui::CHANGED, MakeCallback(this, &AppWorldLogic::onSliderChanged));

	// creating a checkbox widget and setting up its parameters
	widget_checkbox = WidgetCheckBox::create(gui, "Automatic change");
	widget_checkbox->setToolTip("Change sun position automatically");
	widget_checkbox->setPosition(10, 30);
	widget_checkbox->setChecked(1);
	
	// creating a button widget and setting up its parameters
	widget_button_del = WidgetButton::create(gui, "Delete");
	widget_button_del->setToolTip("Delete object(s) selected in the combo box");
	widget_button_del->setPosition(170, 50);
	widget_button_del->setCallback0(Gui::CLICKED, MakeCallback(this, &AppWorldLogic::onButtonDelClicked));

	// creating a button widget and setting up its parameters
	widget_button_fopen = WidgetButton::create(gui, "Load mesh");
	widget_button_fopen->setToolTip("Load a mesh file");
	widget_button_fopen->setPosition(10, 80);
	widget_button_fopen->setCallback0(Gui::CLICKED, MakeCallback(this, &AppWorldLogic::onButtonFopenClicked));

	// creating a button widget and setting up its parameters
	widget_button_close = WidgetButton::create(gui, "Quit");
	widget_button_close->setToolTip("Close application");
	widget_button_close->setPosition(10, 110);
	widget_button_close->setCallback0(Gui::CLICKED, MakeCallback(this, &AppWorldLogic::onButtonCloseClicked));

	// creating a button widget and setting up its parameters
	widget_button_wload = WidgetButton::create(gui, "Load world");
	widget_button_wload->setToolTip("Load a default world");
	widget_button_wload->setPosition(110, 110);
	widget_button_wload->setCallback0(Gui::CLICKED, MakeCallback(this, &AppWorldLogic::onButtonWloadClicked));

	// creating a combo box widget and setting up its parameters
	widget_combo = WidgetComboBox::create(gui);
	widget_combo->addItem("----ALL-----");
	widget_combo->setPosition(10, 50);
	
	// creating a File Selection dialog widget and setting up its parameters
	widget_file_dialog = WidgetDialogFile::create(gui, "Select a mesh file");
	widget_file_dialog->setPath("../");
	widget_file_dialog->setHidden(1);
	widget_file_dialog->setFilter(".mesh");
	widget_file_dialog->getCancelButton()->setCallback0(Gui::CLICKED, MakeCallback(this, &AppWorldLogic::onDlgCancelClicked));
	widget_file_dialog->getOkButton()->setCallback0(Gui::CLICKED, MakeCallback(this, &AppWorldLogic::onDlgOKClicked));

	// creating a Mesh Parameters dialog from an external ui-file
	ui = UserInterface::create(gui, "user_interface.ui");
	ui->setCallback0("button_OK", Gui::CLICKED, MakeCallback(this, &AppWorldLogic::onMeshParamsOKClicked));
	mesh_parameters_window = ui->getWidget(ui->findWidget("window"));
	mesh_parameters_window->setHidden(1);

	// adding created widgets to the UI
	gui->addChild(widget_label->getWidget(), Gui::ALIGN_OVERLAP | Gui::ALIGN_FIXED);							// ID: 0 - label
	gui->addChild(widget_slider->getWidget(), Gui::ALIGN_OVERLAP | Gui::ALIGN_FIXED);							// ID: 1 - slider
	gui->addChild(widget_checkbox->getWidget(), Gui::ALIGN_OVERLAP | Gui::ALIGN_FIXED);							// ID: 2 - checkbox
	gui->addChild(widget_button_del->getWidget(), Gui::ALIGN_OVERLAP | Gui::ALIGN_FIXED);						// ID: 3 - delete button
	gui->addChild(widget_button_close->getWidget(), Gui::ALIGN_OVERLAP | Gui::ALIGN_FIXED);						// ID: 4 - close button
	gui->addChild(widget_combo->getWidget(), Gui::ALIGN_OVERLAP | Gui::ALIGN_FIXED);							// ID: 5 - combo box
	gui->addChild(widget_button_fopen->getWidget(), Gui::ALIGN_OVERLAP | Gui::ALIGN_FIXED);						// ID: 6 - Load Mesh button
	gui->addChild(widget_button_wload->getWidget(), Gui::ALIGN_OVERLAP | Gui::ALIGN_FIXED);						// ID: 7 - Load world button
	gui->addChild(mesh_parameters_window, Gui::ALIGN_OVERLAP | Gui::ALIGN_FIXED | Gui::ALIGN_CENTER);			// ID: 8 - Mesh Parameters window
	gui->addChild(widget_file_dialog->getWidget(), Gui::ALIGN_OVERLAP | Gui::ALIGN_FIXED | Gui::ALIGN_CENTER);	// ID: 9 - File Open dialog

	//reporting progress to the console
	for (int k = 0; k < gui->getNumChildren() ; k++)
	{
		Log::message("-> Adding UI element %d: %s.\n", k, gui->getChild(k)->getTypeName());
	}
	Log::warning("UI initialized! Number of elements: %d.\n", gui->getNumChildren());

	return 1;
}

/* .. */

int AppWorldLogic::init() 
{

/* .. */

	// creating GUI
	initGUI();

/* .. */
	
	return 1;
}

/* .. */	

/// method updating positions of lights
int AppWorldLogic::updateLights()
{
	// updating the Sun position if automatic mode is enabled
	if (widget_checkbox->isChecked())
	{
		sun_angle += SUN_ROTATION_RATE * ifps;
		if (sun_angle > 360.0f) sun_angle = 0.0f;

		// updating slider position
		widget_slider->setValue(sun_angle);
	}

	// updating the Sun position
	LightWorld::cast(editor->getNodeByName("Sun"))->setWorldRotation(Math::quat(Math::vec3(0.0f, 1.0f, 0.0f), 180.0f - sun_angle));

	return 1;
}

/* .. */

//-----------------------------------------------------------------------------------------------------------------------------
/// method adding a Dynamic Mesh Object to the scene. If an empty filename is passed the function creates a default box
int AppWorldLogic::addMeshToScene(const char *filename, const char *mesh_name, const char *material_name, Math::Vec3 position)
{
	MeshPtr mesh = Mesh::create();
	ObjectMeshDynamicPtr omd;

	if (filename){				// loading a mesh from a specified file
		if (!mesh->load(filename))
		{
			Log::error("\nError opening .mesh file!\n");
			mesh.clear();
			
			return 0;
		}
		else omd = ObjectMeshDynamic::create(mesh);
	}
	else								//creating a default box
	{
		mesh->addBoxSurface("box_surface", Math::vec3(0.5f, 0.5f, 0.5f));

		omd = ObjectMeshDynamic::create(mesh);
	}

	// setting object's material, name and transformation
	omd->setMaterial(material_name, "*");
	omd->setName(mesh_name);
	omd->setWorldPosition(position);

	// inheriting a new property from surface_base_1
	String new_property_name = String::format("%s_property", mesh_name);
	properties->inheritProperty("surface_base_1", "my_property_lib.prop", new_property_name.get());

	// assigning the new property to the object and setting values of its parameters
	omd->setProperty(new_property_name.get(), "*");
	omd->getProperty(0)->setParameterString(omd->getProperty(0)->findParameter("material"), material_name);
	omd->getProperty(0)->setParameterInt(omd->getProperty(0)->findParameter("my_parameter"), 0);

	// enabling intersection detection for the first surface (0)
	omd->setIntersection(1, 0);
	
	// passing node ownership to the editor 
	omd->release();
	editor->addNode(omd->getNode());

	// updating combo list and the list of scene objects
	widget_combo->addItem(omd->getName());
	Objects.append(omd);

	// enabling Delete button in the main window
	widget_button_del->setEnabled(1);

	//reporting progress to the console
	Log::message("-> Object %s added to the scene.\n", mesh_name);

	// clearing the mesh
	mesh->clear();

	return 1;
}
//-----------------------------------------------------------------------------------------------------------------------------
/// method deleting a Dynamic Mesh Object with a given name from the scene
int AppWorldLogic::removeMeshFromScene(const char *node_name)
{
	// getting a pointer to the node with a given name and downcasting it to ObjectMeshDynamicPtr
	ObjectMeshDynamicPtr object = ObjectMeshDynamic::cast(editor->getNodeByName(node_name));

	if (object)
	{
		// checking if the node to be removed was previously selected and resetting old_selection pointer
		if (old_selection)
			if (object->getNode() == old_selection->getNode())
				old_selection = ObjectPtr(NULL);

		// reporting node deletion to the console
		Log::message("Removing %s node named %s from the scene.\n", object->getTypeName(), node_name);

		// removing the node with a given name from the list of scene objects and updating the combobox
		for (int i = 0; i < Objects.size();i++)
		{
			if (strcmp(Objects[i]->getName(), node_name) == 0)
			{
				Objects.remove(i);
				widget_combo->removeItem(i + 1);

				break;
			}
		}

		// removing the node from the scene using upcasting
		editor->removeNode(object->getNode());
		return 1;
	}

	return 0;
}
//-----------------------------------------------------------------------------------------------------------------------------

int AppWorldLogic::shutdown() {

	// resetting selected object pointer
	old_selection = ObjectPtr(NULL);

	// removing generated materials and properties
	clearMaterials();
	clearProperties();

	// removing all objects from the scene
	removeObjects();

	// clearing the list of scene objects
	Objects.clear();

	// clearing gui elements
	gui.clear();

	// removing player
	player.clear();

	return 1;
}

/* .. */

Source Files

You can copy the code below and paste it to the corresponding source files of your project:

AppWorldLogic.h

Source code (C++)
#ifndef __APP_WORLD_LOGIC_H__
#define __APP_WORLD_LOGIC_H__

#include <UnigineLogic.h>
#include <UnigineStreams.h>
#include <UnigineObjects.h>
#include <UnigineEditor.h>
#include <UnigineGame.h>
#include <UnigineLights.h>
#include <UnigineMaterials.h>
#include <UnigineApp.h>
#include <UnigineConsole.h>
#include <UnigineUserInterface.h>

// auxiliary constants
const float DELTA_ANGLE = 60.0f;		// delta angle of objects rotation
const float MOVING_SPEED = 3.0f;		// speed of objects movement
const float CHANGE_INTERVAL = 1.0f;		// the interval between changes of objects' parameters, in seconds
const float SUN_ROTATION_RATE = 10.0f;	// rotation rate of the sun

class AppWorldLogic : public Unigine::WorldLogic {
	
public:
	AppWorldLogic();
	virtual ~AppWorldLogic();
	
	virtual int init();
	
	virtual int update();
	virtual int render();
	virtual int flush();
	
	virtual int shutdown();
	virtual int destroy();
	
	virtual int save(const Unigine::StreamPtr &stream);
	virtual int restore(const Unigine::StreamPtr &stream);
	
	// GUI event handlers
	int onButtonDelClicked();
	int onButtonCloseClicked();
	int onButtonFopenClicked();
	int onButtonWloadClicked();
	int onDlgCancelClicked();
	int onDlgOKClicked();
	int onMeshParamsOKClicked();
	int onSliderChanged();

private:
	Unigine::Editor *editor;
	Unigine::App *app;
	Unigine::Materials *materials;
	Unigine::Properties *properties;
	Unigine::PlayerSpectatorPtr player;

	// pointers to the GUI elements
	Unigine::GuiPtr gui;
	Unigine::UserInterfacePtr ui;
	Unigine::WidgetLabelPtr widget_label;
	Unigine::WidgetSliderPtr widget_slider;
	Unigine::WidgetCheckBoxPtr widget_checkbox;
	Unigine::WidgetButtonPtr widget_button_del;
	Unigine::WidgetButtonPtr widget_button_close;
	Unigine::WidgetButtonPtr widget_button_fopen;
	Unigine::WidgetButtonPtr widget_button_wload;
	Unigine::WidgetComboBoxPtr widget_combo;
	Unigine::WidgetDialogFilePtr widget_file_dialog;
	Unigine::WidgetPtr mesh_parameters_window;


	// pointers to light sources
	Unigine::LightWorldPtr thesun;
	Unigine::LightOmniPtr light_omni;
	Unigine::LightProjPtr projector;

	// auxiliary functions
	int addMeshToScene(const char *file_name, const char *mesh_name, const char *material_name, Unigine::Math::Vec3 position);
	int removeMeshFromScene(const char *node_name);
	int transformNode(Unigine::NodePtr node, float ifps);

	// initialization functions
	int initObjects();
	int initPlayer();
	int initLights();
	int initMaterials();
	int initProperties();
	int initGUI();

	// update functions
	int updateLights();
	int updateObjects();

	// shutdown functions
	int clearMaterials();
	int clearProperties();
	int removeObjects();

	// scene objects vector
	Unigine::Vector <Unigine::ObjectMeshDynamicPtr> Objects;
	
	Unigine::ObjectPtr old_selection;												// pointer to previously selected object
	Unigine::Math::vec3 current_objects_scale = Unigine::Math::vec3(1.0f);			// current scaling vector for objects
	Unigine::Math::Vec3 forward_direction = Unigine::Math::Vec3(0.0f, -1.0f, 0.0f);	// current forward direction vector for objects
	float elapsed_time = CHANGE_INTERVAL;											// current time left to change current scale and forward direction of our objects
	float sun_angle = 0.0f;															// current sun position
};

#endif // __APP_WORLD_LOGIC_H__

AppWorldLogic.cpp

Source code (C++)
#include "AppWorldLogic.h"
// World logic, it takes effect only when the world is loaded.
// These methods are called right after corresponding world script's (UnigineScript) methods.
using namespace Unigine;
// auxiliary variable to store the time of rendering the last frame
float ifps;

//-----------------------------------------------------------------------------------------------------------------------------
//------------------------------------------------------- GUI EVENT HANDLERS --------------------------------------------------
//-----------------------------------------------------------------------------------------------------------------------------
/// method to be called when the Close button is clicked in the main window
int AppWorldLogic::onButtonCloseClicked()
{
	// removing generated materials and properties
	clearMaterials();
	clearProperties();

	// saving current world to my_world.world file
	Console::get()->run("world_save my_world");

	// executing all pending console commands before exiting
	Console::get()->flush();

	// closing the application
	App::get()->exit();

	return 1;
}
//-----------------------------------------------------------------------------------------------------------------------------
/// method to be called when the Load world button is clicked in the main window
int AppWorldLogic::onButtonWloadClicked()
{
	// reporting to console
	Log::warning("\n\n --- RELOADING A DEFAULT WORLD --- \n\n");

	// loading a world from default.world file
	Console::get()->run("world_load default");

	return 1;
}
//-----------------------------------------------------------------------------------------------------------------------------
/// method to be called when the Cancel button is clicked in the main window
int AppWorldLogic::onButtonFopenClicked()
{
	widget_file_dialog->setHidden(0);

	return 1;
}
//-----------------------------------------------------------------------------------------------------------------------------
/// method to be called when the slider position is changed in the main window
int AppWorldLogic::onSliderChanged()
{
	// updating the position of the Sun
	sun_angle = widget_slider->getValue();

	return 1;
}
//-----------------------------------------------------------------------------------------------------------------------------
/// method to be called when the Delete button is clicked in the main window
int AppWorldLogic::onButtonDelClicked()
{
	String node_name;

	if (widget_combo->getCurrentItem() == 0)									// deleting all objects 
	{
		removeObjects();

		// disabling Delete button in the main window
		widget_button_del->setEnabled(0);
	}
	else																		// deleting an object selected in the combo box
	{
		// retrieving currently selected node name in the combo box
		node_name = widget_combo->getCurrentItemText();

		// checking if a node with a given name exists and removing it from the scene
		if (editor->findNode(node_name.get()) != -1)
		{
			removeMeshFromScene(node_name.get());
		}
	}

	return 1;
}
//-----------------------------------------------------------------------------------------------------------------------------
/// method to be called when the Cancel button is clicked in the File Open dialog
int AppWorldLogic::onDlgCancelClicked()
{
	// hiding the file selecton dialog
	widget_file_dialog->setHidden(1);

	return 1;
}
//-----------------------------------------------------------------------------------------------------------------------------
/// method to be called when the OK button is clicked in the File Open dialog
int AppWorldLogic::onDlgOKClicked()
{
	// checking if the file exists
	if (FILE *file = fopen(widget_file_dialog->getFile(), "r")) {
		fclose(file);

		//setting initial values of the fields in the Mesh Parameters window
		String object_name = String::format("Object_%d", widget_combo->getNumItems() - 1);
		WidgetEditLine::cast(ui->getWidget(ui->findWidget("object_name")))->setText(object_name.get());
		WidgetEditLine::cast(ui->getWidget(ui->findWidget("coord_x")))->setText("0.0");
		WidgetEditLine::cast(ui->getWidget(ui->findWidget("coord_y")))->setText("0.0");
		WidgetEditLine::cast(ui->getWidget(ui->findWidget("coord_z")))->setText("0.0");

		// hiding the File Open dialog and showing Mesh Parameters window using different ways
		widget_file_dialog->setHidden(1);
		mesh_parameters_window->setHidden(0);

		return 1;
	}

	// reporting an error to the console
	Log::error("Loading mesh: can't open \"%s\" file\n", widget_file_dialog->getFile());
	return 0;
}
//-----------------------------------------------------------------------------------------------------------------------------
/// method to be called when the OK button is clicked in the Mesh Parameters window
int AppWorldLogic::onMeshParamsOKClicked()
{
	//retrieving pointers to UI elements
	WidgetEditLinePtr name = WidgetEditLine::cast(ui->getWidget(ui->findWidget("object_name")));
	WidgetEditLinePtr x = WidgetEditLine::cast(ui->getWidget(ui->findWidget("coord_x")));
	WidgetEditLinePtr y = WidgetEditLine::cast(ui->getWidget(ui->findWidget("coord_y")));
	WidgetEditLinePtr z = WidgetEditLine::cast(ui->getWidget(ui->findWidget("coord_z")));

	if ((x->getText() != "") && (y->getText() != "") && (z->getText() != "") && (name->getText() != ""))
	{

		Math::Vec3 pos = Math::Vec3(atof(x->getText()), atof(y->getText()), atof(z->getText()));
		String result = String((addMeshToScene(widget_file_dialog->getFile(), name->getText(), "my_mesh_base3", pos)) ? "Ok!" : "Error!");

		// reporting progress to the console
		Log::message("(%s) mesh load operation status: %s\n", name->getText(), result.get());

		// hiding the Mesh Parameters window
		mesh_parameters_window->setHidden(1);

		return 1;
	}
	Log::error("Wrong mesh parameters!\n");
	return 0;
}
//-----------------------------------------------------------------------------------------------------------------------------
//---------------------------------------------- AUXILIARY FUNCTIONS AND METHODS ----------------------------------------------
//-----------------------------------------------------------------------------------------------------------------------------

/// method adding a Dynamic Mesh Object to the scene. If an empty filename is passed the method creates a default box; otherwise, loads a mesh-file with a given name.
int AppWorldLogic::addMeshToScene(const char *file_name, const char *mesh_name, const char *material_name, Math::Vec3 position)
{
	MeshPtr mesh = Mesh::create();
	ObjectMeshDynamicPtr omd;
	String new_property_name = String::format("%s_property", mesh_name);

	if (file_name){				// loading a mesh from a specified file
		if (!mesh->load(file_name)){
			Log::error("\nError opening .mesh file!\n");
			mesh.clear();
			return 0;
		}
		else omd = ObjectMeshDynamic::create(mesh);
	}
	else																// creating a default box
	{
		mesh->addBoxSurface("box_surface", Math::vec3(0.5f));

		omd = ObjectMeshDynamic::create(mesh);
	}

	// setting node material, name and position
	omd->setMaterial(material_name, "*");
	omd->setName(mesh_name);
	omd->setWorldPosition(position);

	//inheriting a new property from "surface_base_1"
	properties->inheritProperty("surface_base_1", "my_property_lib.prop", new_property_name.get());

	// assigning the new property to the node and setting values of its parameters
	omd->setProperty(new_property_name.get(), "*");
	omd->getProperty(0)->setParameterString(omd->getProperty(0)->findParameter("material"), material_name);
	omd->getProperty(0)->setParameterInt(omd->getProperty(0)->findParameter("selected"), 0);

	// enabling intersection detection for the first surface (0)
	omd->setIntersection(1, 0);

	// passing node ownership to the editor as a runtime node
	omd->release();
	editor->addNode(omd->getNode());

	// updating combo list and the list of scene objects
	widget_combo->addItem(omd->getName());
	Objects.append(omd);

	// enabling Delete button in the main window
	widget_button_del->setEnabled(1);

	// reporting progress to the console
	Log::message("-> Object %s added to the scene.\n", mesh_name);

	// clearing the mesh
	mesh->clear();

	return 1;

}
//-----------------------------------------------------------------------------------------------------------------------------
/// method deleting a Dynamic Mesh Object with a given name from the scene
int AppWorldLogic::removeMeshFromScene(const char *node_name)
{
	// getting a pointer to the node with a given name and downcasting it to ObjectMeshDynamicPtr
	ObjectMeshDynamicPtr object = ObjectMeshDynamic::cast(editor->getNodeByName(node_name));

	if (object)
	{
		if (old_selection)
		{
			// checking if the node to be removed was previously selected
			if (object->getNode() == old_selection->getNode())
			{
				// resetting old_selection pointer to NULL
				old_selection.clear();
			}
		}

		// reporting node deletion to the console
		Log::message("Removing %s node named %s from the scene.\n", object->getTypeName(), node_name);

		// removing the node with a given name from the list of scene objects
		for (int i = 0; i < Objects.size(); i++)
		{
			if (strcmp(Objects[i]->getName(), node_name) == 0) {
				Objects.remove(i);
				widget_combo->removeItem(i + 1);

				break;
			}
		}

		// removing the node from the scene using upcasting
		editor->removeNode(object->getNode());

		return 1;
	}

	return 0;
}
//-----------------------------------------------------------------------------------------------------------------------------
/// method performing node transformations 
int AppWorldLogic::transformNode(NodePtr node, float ifps)
{
	// getting current node transformation matrix
	Math::Mat4 transform = node->getTransform();

	// calculating delta rotation around an arbitrary axis
	Math::quat delta_rotation = Math::quat(rand() % 2, rand() % 2, rand() % 2, DELTA_ANGLE * ifps);

	// setting node's scale, rotation and position
	node->setWorldScale(current_objects_scale);
	node->setWorldRotation(node->getWorldRotation() * delta_rotation);
	node->setWorldPosition(node->getWorldPosition() + forward_direction * MOVING_SPEED *ifps);

	return 1;
}
//-----------------------------------------------------------------------------------------------------------------------------
/// function getting an object under the mouse cursor
ObjectPtr getObjectUnderMouseCursor(const PlayerPtr &player, int mouse_x, int mouse_y, float max_dist)
{
	// setting start point (p0) at the center of player's screen
	Math::Vec3 p0 = player->getWorldPosition();

	// setting end point (p1) according to the position of the mouse cursor
	Math::Vec3 p1 = p0 + Math::Vec3(player->getDirectionFromScreen(mouse_x, mouse_y)) * max_dist;

	WorldIntersectionPtr intersection = WorldIntersection::create();

	// returning the pointer to the first intersected object if any or NULL
	return World::get()->getIntersection(p0, p1, 1, intersection);
}
//-----------------------------------------------------------------------------------------------------------------------------
/// function setting new object as selected and updating previous selection
int selectObject(ObjectPtr new_selected_object, ObjectPtr &old_selected_object)
{
	// checking if new selected object is not NULL and it has a property with parameter "selected" assigned
	if (new_selected_object)
	{
		if (new_selected_object->getProperty(0)->findParameter("selected") != -1)
		{
			// checking if we already have a previously selected object and setting its "selected" parameter of the property to 0
			if (old_selected_object)
			{
				old_selected_object->getProperty(0)->setParameterInt(old_selected_object->getProperty(0)->findParameter("selected"), 0);
			}

			// setting the "selected" parameter of the property to 1 for the new selected object 
			new_selected_object->getProperty(0)->setParameterInt(new_selected_object->getProperty(0)->findParameter("selected"), 1);

			// remembering the object as selected
			old_selected_object = new_selected_object;

			return 1;
		}
	}

	return 0;
}

//-----------------------------------------------------------------------------------------------------------------------------
//---------------------------------------------- INITIALIZATION METHODS -------------------------------------------------------
//-----------------------------------------------------------------------------------------------------------------------------
/// method performing initialization of the set of 4 boxes
int AppWorldLogic::initObjects()
{
	int index = 0;

	for (int x = 0; x < 2; x++)
	{
		for (int y = 0; y < 2; y++)
		{
			addMeshToScene(NULL, String::format("my_meshdynamic_%d", index).get(), String::format("my_mesh_base%d", index).get(), Math::Vec3(x, y, 1.0f));
			index++;
		}
	}

	// reporting progress to the console
	Log::warning("Objects generation OK!\n\n");

	return 1;
}
//-----------------------------------------------------------------------------------------------------------------------------
/// method performing initialization of the player
int AppWorldLogic::initPlayer()
{
	// creating a new PlayerSpectator instance
	player = PlayerSpectator::create();

	// setting player's FOV, ZNear, ZFar
	player->setFov(90.0f);
	player->setZNear(0.1f);
	player->setZFar(10000.0f);

	// setting player's view direction vector and position
	player->setPosition(Math::Vec3(3.0f));
	player->setDirection(Math::vec3(-1.0f), Math::vec3(0.0f, 0.0f, -1.0f));

	// setting the player to the Game singleton instance
	Game::get()->setPlayer(player->getPlayer());
	player->release();

	//reporting progress to the console
	Log::warning("\nPlayer initialization OK!\n\n");

	return 1;
}
//-----------------------------------------------------------------------------------------------------------------------------
/// method performing initialization of lights
int AppWorldLogic::initLights()
{
	// creating an omni light and setting up its parameters
	light_omni = LightOmni::create(Math::vec4(1.0f, 1.0f, 1.0f, 1.0f), 10.0f, "");
	light_omni->setWorldPosition(Math::Vec3(0.0f, 0.0f, 5.0f));
	light_omni->setIntensity(0.1f);

	// passing node ownership to the editor
	light_omni->release();
	editor->addNode(light_omni->getNode());

	// reporting progress to the console
	Log::message("-> Created a %s light source.\n", light_omni->getName());

	// creating a world light and setting up its parameters
	thesun = LightWorld::create(Math::vec4(1.0f, 1.0f, 1.0f, 1.0f));
	thesun->setName("Sun");
	thesun->setDisableAngle(90.0f);
	thesun->setIntensity(1.0f);
	thesun->setScattering(LightWorld::SCATTERING_SUN);
	thesun->setWorldRotation(Math::quat(0.0f, 1.0f, 0.0f, 170.0f));

	// passing node ownership to the editor
	thesun->release();
	editor->addNode(thesun->getNode());

	// reporting progress to the console
	Log::message("-> Created a %s light source.\n", thesun->getName());

	// creating a proj light and setting up its parameters
	projector = LightProj::create(Math::vec4(1.0f, 1.0f, 0.5f, 1.0f), 10.0f, 60.0f, "");
	projector->setWorldPosition(Math::Vec3(2.5f, 2.5f, 3.0f));
	projector->setName("projector");
	projector->setRotation(Math::quat(-45.0f, 45.0f, 0.0f));
	projector->setPenumbra(0.425f);
	projector->setIntensity(1.0f);

	// passing node ownership to the editor
	projector->release();
	editor->addNode(projector->getNode());

	// reporting progress to the console
	Log::message("-> Created a %s light source.\n", projector->getName());
	Log::warning("Lights initialization OK!\n");

	return 1;
}
//-----------------------------------------------------------------------------------------------------------------------------
/// method performing initialization of materials
int AppWorldLogic::initMaterials()
{
	// getting a pointer to materials interface
	materials = Materials::get();

	// creating a new material library
	materials->create("my_material_lib");

	// creating a new child material of the mesh_base and setting its color
	materials->inheritMaterial("mesh_base", "my_material_lib", "my_mesh_base0");
	MaterialPtr  my_mesh_base = materials->findMaterial("my_mesh_base0");
	my_mesh_base->setParameter(Material::PARAMETER_COLOR, Math::vec4(255, 0, 0, 255));

	// reporting progress to the console
	Log::message("\n-> Generated %s material.\n", my_mesh_base->getName());

	// creating a new child material of the mesh_base and setting its color
	materials->inheritMaterial("mesh_base", "my_material_lib", "my_mesh_base1");
	my_mesh_base = materials->findMaterial("my_mesh_base1");
	my_mesh_base->setParameter(Material::PARAMETER_COLOR, Math::vec4(0, 255, 0, 255));

	// reporting progress to the console
	Log::message("-> Generated %s material.\n", my_mesh_base->getName());

	//creating a new child material of the mesh_base and setting its color
	materials->inheritMaterial("mesh_base", "my_material_lib", "my_mesh_base2");
	my_mesh_base = materials->findMaterial("my_mesh_base2");
	my_mesh_base->setParameter(Material::PARAMETER_COLOR, Math::vec4(0, 0, 255, 255));

	// reporting progress to the console
	Log::message("-> Generated %s material.\n", my_mesh_base->getName());

	//creating a new child material of the mesh_base and setting its color
	materials->inheritMaterial("mesh_base", "my_material_lib", "my_mesh_base3");
	my_mesh_base = materials->findMaterial("my_mesh_base3");
	my_mesh_base->setParameter(Material::PARAMETER_COLOR, Math::vec4(255, 255, 0, 255));

	// reporting progress to the console
	Log::message("-> Generated %s material.\n", my_mesh_base->getName());
	Log::warning("Material generation OK!\n\n");

	// clearing material pointer
	my_mesh_base.clear();

	return 1;
}
//-----------------------------------------------------------------------------------------------------------------------------
/// method performing initialization of properties
int AppWorldLogic::initProperties()
{
	// getting a pointer to the property manager
	properties = Properties::get();

	// loading a property library
	properties->addWorldLibrary("my_property_lib.prop");

	return 1;
}
/// method performing initialization of the GUI
int AppWorldLogic::initGUI()
{
	// getting a GUI pointer
	gui = Gui::get();

	// creating a label widget and setting up its parameters
	widget_label = WidgetLabel::create(gui, "Sun position:");
	widget_label->setToolTip("Change sun position");
	widget_label->setPosition(10, 10);

	// creating a slider widget and setting up its parameters
	widget_slider = WidgetSlider::create(gui, 0, 360, 90);
	widget_slider->setToolTip("Change sun position");
	widget_slider->setPosition(100, 10);
	widget_slider->setCallback0(Gui::CHANGED, MakeCallback(this, &AppWorldLogic::onSliderChanged));

	// creating a checkbox widget and setting up its parameters
	widget_checkbox = WidgetCheckBox::create(gui, "Automatic change");
	widget_checkbox->setToolTip("Change sun position automatically");
	widget_checkbox->setPosition(10, 30);
	widget_checkbox->setChecked(1);

	// creating a button widget and setting up its parameters
	widget_button_del = WidgetButton::create(gui, "Delete");
	widget_button_del->setToolTip("Delete object(s) selected in the combo box");
	widget_button_del->setPosition(170, 50);
	widget_button_del->setCallback0(Gui::CLICKED, MakeCallback(this, &AppWorldLogic::onButtonDelClicked));

	// creating a button widget and setting up its parameters
	widget_button_fopen = WidgetButton::create(gui, "Load mesh");
	widget_button_fopen->setToolTip("Load a mesh file");
	widget_button_fopen->setPosition(10, 80);
	widget_button_fopen->setCallback0(Gui::CLICKED, MakeCallback(this, &AppWorldLogic::onButtonFopenClicked));

	// creating a button widget and setting up its parameters
	widget_button_close = WidgetButton::create(gui, "Quit");
	widget_button_close->setToolTip("Close application");
	widget_button_close->setPosition(10, 110);
	widget_button_close->setCallback0(Gui::CLICKED, MakeCallback(this, &AppWorldLogic::onButtonCloseClicked));

	// creating a button widget and setting up its parameters
	widget_button_wload = WidgetButton::create(gui, "Load world");
	widget_button_wload->setToolTip("Load a default world");
	widget_button_wload->setPosition(110, 110);
	widget_button_wload->setCallback0(Gui::CLICKED, MakeCallback(this, &AppWorldLogic::onButtonWloadClicked));

	// creating a combo box widget and setting up its parameters
	widget_combo = WidgetComboBox::create(gui);
	widget_combo->addItem("----ALL-----");
	widget_combo->setPosition(10, 50);

	// creating a File Selection dialog widget and setting up its parameters
	widget_file_dialog = WidgetDialogFile::create(gui, "Select a mesh file");
	widget_file_dialog->setPath("../");
	widget_file_dialog->setHidden(1);
	widget_file_dialog->setFilter(".mesh");
	widget_file_dialog->getCancelButton()->setCallback0(Gui::CLICKED, MakeCallback(this, &AppWorldLogic::onDlgCancelClicked));
	widget_file_dialog->getOkButton()->setCallback0(Gui::CLICKED, MakeCallback(this, &AppWorldLogic::onDlgOKClicked));

	// creating a Mesh Parameters dialog from an external ui-file
	ui = UserInterface::create(gui, "user_interface.ui");
	ui->setCallback0("button_OK", Gui::CLICKED, MakeCallback(this, &AppWorldLogic::onMeshParamsOKClicked));
	mesh_parameters_window = ui->getWidget(ui->findWidget("window"));
	mesh_parameters_window->setHidden(1);

	// adding created widgets to the UI
	gui->addChild(widget_label->getWidget(), Gui::ALIGN_OVERLAP | Gui::ALIGN_FIXED);							// ID: 0 - label
	gui->addChild(widget_slider->getWidget(), Gui::ALIGN_OVERLAP | Gui::ALIGN_FIXED);							// ID: 1 - slider
	gui->addChild(widget_checkbox->getWidget(), Gui::ALIGN_OVERLAP | Gui::ALIGN_FIXED);							// ID: 2 - checkbox
	gui->addChild(widget_button_del->getWidget(), Gui::ALIGN_OVERLAP | Gui::ALIGN_FIXED);						// ID: 3 - delete button
	gui->addChild(widget_button_close->getWidget(), Gui::ALIGN_OVERLAP | Gui::ALIGN_FIXED);						// ID: 4 - close button
	gui->addChild(widget_combo->getWidget(), Gui::ALIGN_OVERLAP | Gui::ALIGN_FIXED);							// ID: 5 - combo box
	gui->addChild(widget_button_fopen->getWidget(), Gui::ALIGN_OVERLAP | Gui::ALIGN_FIXED);						// ID: 6 - Load Mesh button
	gui->addChild(widget_button_wload->getWidget(), Gui::ALIGN_OVERLAP | Gui::ALIGN_FIXED);						// ID: 7 - Load world button
	gui->addChild(mesh_parameters_window, Gui::ALIGN_OVERLAP | Gui::ALIGN_FIXED | Gui::ALIGN_CENTER);			// ID: 8 - Mesh Parameters window
	gui->addChild(widget_file_dialog->getWidget(), Gui::ALIGN_OVERLAP | Gui::ALIGN_FIXED | Gui::ALIGN_CENTER);	// ID: 9 - File Open dialog

	//reporting progress to the console
	for (int k = 0; k < gui->getNumChildren(); k++)
	{
		Log::message("-> Adding UI element %d: %s.\n", k, gui->getChild(k)->getTypeName());
	}
	Log::warning("UI initialized! Number of elements: %d.\n", gui->getNumChildren());

	return 1;
}
//-----------------------------------------------------------------------------------------------------------------------------
//---------------------------------------------------- UPDATE METHODS ---------------------------------------------------------
//-----------------------------------------------------------------------------------------------------------------------------
/// method updating the position of the sun
int AppWorldLogic::updateLights()
{
	// updating the Sun rotation angle
	if (widget_checkbox->isChecked()){
		sun_angle += SUN_ROTATION_RATE * ifps;
		if (sun_angle > 360.0f) sun_angle = 0.0f;
		
		// updating slider position
		widget_slider->setValue(sun_angle);
	}
	// changing the Sun position using the new angle
	LightWorld::cast(editor->getNodeByName("Sun"))->setWorldRotation(Math::quat(Math::vec3(0.0f, 1.0f, 0.0f), 180.0f - sun_angle));

	return 1;
}
//-----------------------------------------------------------------------------------------------------------------------------
/// method updating the initial set of scene objects
int AppWorldLogic::updateObjects()
{
	ObjectMeshDynamicPtr object;

	// changing transformation for all scene objects named "my_meshdynamic_*" (initial set)
	for (auto it = Objects.begin(); it != Objects.end(); it++)
	{
		object = it.get();

		// getting the property assigned to the first surface of the object 
		PropertyPtr p = object->getProperty(0);

		// checking if such property exists and has a "selected" parameter
		if (p)
		{
			int param = p->findParameter("selected");
			if (param != -1)
				if (p->getParameterInt(param) == 1)
				{
					// if "selected" parameter value is 1 assigning "mesh_base" material to the object to highlight it
					object->setMaterial("mesh_base", "*");
				}
				else
				{
					// if "selected" parameter value is 0 assigning the material that is stored in the "material" parameter of its property
					object->setMaterial(p->getParameterString(p->findParameter("material")), "*");
				}
		}

		// if a node is named "my_meshdynamic_*" (it belongs to the initial set) change its transformation
		if (strstr(object->getName(), "my_meshdynamic_"))
		{
			// transform the node
			transformNode(object->getNode(), ifps);
		}
	}

	return 1;
}
//-----------------------------------------------------------------------------------------------------------------------------
//---------------------------------------------------- SHUTDOWN METHODS -------------------------------------------------------
//-----------------------------------------------------------------------------------------------------------------------------
/// method removing all created materials
int AppWorldLogic::clearMaterials()
{
	materials->removeMaterial("my_mesh_base0");
	materials->removeMaterial("my_mesh_base1");
	materials->removeMaterial("my_mesh_base2");
	materials->removeMaterial("my_mesh_base3");

	return 1;
}
//-----------------------------------------------------------------------------------------------------------------------------
/// method removing all created properties
int AppWorldLogic::clearProperties()
{
	// clearing the property library
	properties->clear("my_property_lib.prop");

	// removing the property library
	properties->removeWorldLibrary("my_property_lib.prop");

	return 1;
}
//-----------------------------------------------------------------------------------------------------------------------------
/// method removing all  created objects
int AppWorldLogic::removeObjects()
{
	while (Objects.size() > 0)
	{
		removeMeshFromScene(Objects.begin()->get()->getName());
	}

	return 1;
}
//-----------------------------------------------------------------------------------------------------------------------------
AppWorldLogic::AppWorldLogic() {

}

AppWorldLogic::~AppWorldLogic() {

}

int AppWorldLogic::init() {
	// Write here code to be called on world initialization: initialize resources for your world scene during the world start.

	// getting a pointer to the application
	app = App::get();

	// initializing pseudo-random number generator
	Game::get()->setSeed(time(NULL));

	// getting a pointer to the Editor
	editor = Editor::get();

	// creating materials
	initMaterials();

	// creating properties
	initProperties();

	// creating GUI
	initGUI();
	
	// creating objects
	initObjects();

	// creating a player
	initPlayer();

	// creating lights
	initLights();

	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.

	// getting an inverse FPS value (the time in seconds it took to complete the last frame)
	ifps = Game::get()->getIFps();

	// checking if it's time to change current scale and forward vector direction of our objects
	if (elapsed_time < 0.0f)
	{
		// change current scaling vector for objects
		current_objects_scale = Math::vec3(Game::get()->getRandomFloat(0.8f, 1.2f));

		// change forward direction for objects
		forward_direction = forward_direction * Math::rotateZ(60.0f);

		// resetting elapsed time counter
		elapsed_time = CHANGE_INTERVAL;
	}

	// decreasing the time counter to the next change of current scale and forward vector direction
	elapsed_time -= ifps;

	// if right mouse button is clicked
	if (app->getMouseButtonState(App::BUTTON_RIGHT))
	{
		// get and select object under the mouse cursor
		selectObject(getObjectUnderMouseCursor(Game::get()->getPlayer(), app->getMouseX(), app->getMouseY(), 100.0f), old_selection);
	}

	// closing the application if a 'Q' key is pressed, ignoring the key if the console is opened
	if (app->getKeyState('q') && !Console::get()->getActivity())
	{
		app->exit();
	}

	// updating objects
	updateObjects();

	// updating lights
	updateLights();

	return 1;
}


int AppWorldLogic::render() {
	// 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::flush() {
	// Write here code to be called before updating each physics frame: control physics in your application and put non-rendering calculations.
	// The engine calls flush() 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.

	// deleting all created nodes
	removeObjects();

	// clearing the player pointer
	player.clear();

	// clearing light sources
	thesun.clear();
	light_omni.clear();
	projector.clear();

	// clearing all created materials
	clearMaterials();

	// clearing all created properties
	clearProperties();  
	
	// clearing gui elements
	gui.clear(); 

	// resetting old_selection pointer
	old_selection.clear();

	return 1;
}

int AppWorldLogic::destroy() {
	// Write here code to be called when the video mode is changed or the application is restarted (i.e. video_restart is called). It is used to reinitialize the graphics context.

	return 1;
}

int AppWorldLogic::save(const Unigine::StreamPtr &stream) {
	// Write here code to be called when the world is saving its state (i.e. state_save is called): 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 (i.e. state_restore is called): restore custom user data to a file here.

	UNIGINE_UNUSED(stream);
	return 1;
}

Congratulations!

Graphical user interface of the tutorial project application.

Well, now our project is ready! You have done a great job and learned how to use basic functions of the Unigine Engine. If you need more detailed information on how to use Unigine C++ API please refer to our extended API manual.

Last update: 2017-07-03