Video Tutorials
Interface
Essentials
Advanced
Полезные советы
UnigineEditor
Interface Overview
Assets Workflow
Settings and Preferences
Working With Projects
Adjusting Node Parameters
Setting Up Materials
Setting Up Properties
Lighting
Landscape Tool
Using Editor Tools for Specific Tasks
Extending Editor Functionality
Программирование
Fundamentals
Setting Up Development Environment
Usage Examples
UnigineScript
C++
C#
UUSL (Unified UNIGINE Shader Language)
File Formats
Rebuilding the Engine 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
Objects-Related Classes
Networking Functionality
Pathfinding-Related Classes
Physics-Related Classes
Plugins-Related Classes
IG Plugin
CIGIConnector Plugin
Rendering-Related Classes
Работа с контентом
Content Optimization
Materials
Art Samples
Tutorials

1. Basic Concepts

Development of a project starts from creating it using SDK Browser. Follow the Creating a C++ Application article or the video tutorial below to create a new C++ project.

After configuring the development environment you can proceed to learning the basic concepts of the engine.

See Also

For more details refer to the following topics:

Basic Scene Objects and Coordinate System#

Basic Scene Objects

In terms of UNIGINE, node is a basic type from which all types of scene objects are inherited. Some of them appear visually: Objects, Decals, and Effects they all have surfaces to represent their geometry (mesh), while others (Light Sources, Players, etc.) are invisible.

Each node has a transformation matrix, which encodes position, rotation, and scale of the node in the world.

All scene objects added to the scene regardless of their type are called nodes.

Coordinate System

The 3D space in Unigine is represented by the right-handed Cartesian coordinate system: X and Y axes form a horizontal plane, Z axis points up. When exporting an animation from 3D editors, Y is considered a forward direction.

Coordinate System

Positive rotation angle sets the rotation counterclockwise. It corresponds to the right-hand rule: if you set right hand thumb along the axis, other fingers wrapped will show rotation direction.

Rotation Directions

Additional information:

Logging and Printing Messages to Console#

Printing messages to the log file and console helps to monitor overall progress of execution of your application and report errors which can be used in debugging. Log class makes it possible to print formatted string messages to the log file and the console. The code below demonstrates how to print various types of messages:

Notice
To enable displaying system messages in the Console use the following command: show_messages 1
Source code (C++)
using namespace Unigine;

// auxiliary variables for messages
char *file_name = "file.txt";
int ID = 10;
	
// reporting an error message
Log::error("Loading mesh: can't open \"%s\" file\n", file_name);

// reporting a message
Log::message("-> Added %d UI elements.\n", 10);
	
// reporting a warning message
Log::warning("ID of the \"%s\" file: %d.\n", file_name, ID);
	
// reporting a fatal error message to the log file and closing the application
Log::fatal("FATAL ERROR reading \"%s\" file!\n", file_name);

Additional information:

  • For more information on the console, see Console page.
  • For more information on the Log class, see Log class page.

Saving and Loading a World#

Some applications manage a single world, while other require several worlds to be managed. In any case, it is very useful to know how to save our current world and load some other. In order to solve this task, we should use the World class, which is designed as a singleton.

Notice
In order to use the World class, include the UnigineWorld.h library.
Source code (C++)
#include <UnigineWorld.h>

using namespace Unigine;
/* .. */

// loading world from the my_world.world file
World::loadWorld("my_world");

We can also do the same via the console by using the Console class, which is also designed as a singleton.

Notice
In order to use the Console class, include the UnigineConsole.h library.
Source code (C++)
#include <UnigineConsole.h>

using namespace Unigine;
/* .. */

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

// loading world from the my_world.world file
Console::run("world_load my_world");

Additional information:

  • For more information on managing worlds via API, see World class page.
  • For more information on the console and available commands, see Console page.
  • For more information on managing the console via API, see Console class page.
  • For more information on managing world nodes that are to be saved via API, see the methods of the Node class.

Closing the Application#

Any application needs to be closed at some moment. To close your application as well as to manage its window parameters, controls events, etc. you should use App class.

Notice
In order to work with App class the UnigineApp.h library must be included.

To close the application the following code is to be used:

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

/* .. */
		
// closing the application 
App::exit();

Additional information:

  • For more information on managing the application via API, see App class page.

Working with Smart Pointers#

In UNIGINE, instances of C++ API classes (such as: node, mesh, body, image, etc.) only store pointers to instances of internal C++ classes, they cannot be created and deleted via the standard new/delete operators. So they should be declared as smart pointers (Unigine::Ptr) that allow you to automatically manage their lifetime. UNIGINE has its own optimized memory allocator for faster and more efficient memory management.

To create an instance of an internal class we should declare a smart pointer for the class we are going to instantiate and call the create() method - class constructor - providing construction parameters if necessary.

Source code (C++)
// instantiating an object of an internal class
<Class>Ptr instance = <Class>::create(<construction_parameters>);

All objects are divided into two groups regarding the way their lifetime is managed:

Ownership Objects#

Ownership objects (Image, Texture, Mesh, Tileset, etc.) — these objects are managed in accordance with reference counter, i.e. how many smart pointers are pointing to the managed object; when the last smart pointer is destroyed, the counter goes to 0. In this case it is assumed that the object is no longer needed (the Engine doesn’t know anything about it, and the user has got no pointer to be able to use it) and, therefore, it is deleted. (e.g. such objects declared within a scope will be automatically deleted when leaving the scope).

Source code (C++)
// creating a new image
ImagePtr img = Image::create();

// now two pointers point to our image (reference counter increment)
ImagePtr img2 = img;

// removing the image (as both pointers no longer point to it and reference counter is zero)
img2 = img = nullptr;

// another way to clear pointers
img.clear();
img2.clear();

Notice
You should avoid cyclic references!
If there is a ring, or cycle, of objects that have smart pointers to each other, they keep each other "alive" - they won't get deleted even if no other objects in the universe are pointing to them from "outside" of the ring. This cycle problem is illustrated in the diagram below that shows a container of smart pointers pointing to three objects each of which also point to another object with a smart pointer and form a ring. If we empty the container of smart pointers, the three objects won't get deleted, because each of them still has a smart pointer pointing to them.

Cyclic references

Non-Ownership Objects#

Non-ownership objects (nodes, widgets, materials, properties, etc.) — these objects interact with the Engine and become managed by it since the moment of their creation (they are engaged in the main loop, can be retrieved by names, etc.). The lifetime of these objects is not determined by the reference counter, they provide the mechanism of weak references, so you can check whether an object was deleted or not. To delete such objects you should use deleteLater() or a corresponding manager’s method (e.g.: Materials::removeMaterial()).

Source code (C++)
NodePtr node;
void somefunc1(){
// creating a new dummy node
node = NodeDummy::create();
}

void somefunc2(){
// checking whether the node exists
if (node)
Log::message("The node is alive\n");

// deleting the node
node.deleteLater();
}

Instead of managing references for nodes manually now you can simply choose lifetime management policy for it:

  • World-managed - in this case a node shall be deleted when the world is closed. This policy is used by default for each new node.
  • Engine-managed - in this case the node shall be deleted automatically on Engine shutdown (can be used for nodes that should be kept when changing worlds).
Source code (C++)
NodePtr node = NodeDummy::create();
NodePtr node2 = NodeDummy::create();

// the node shall be deleted on world shutdown
node->setLifetime(Node::LIFETIME_WORLD);
// the node2 shall remain alive even when another world is loaded
node2->setLifetime(Node::LIFETIME_ENGINE);
Notice
Lifetime of each node in the hierarchy is defined by its root (either parent or possessor). Setting lifetime management type for a child node different from the one set for the root has no effect.

Additional information:

PROCEED TO THE NEXT SECTION >>

Last update: 21.04.2020