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:
- Project Workflow article to learn key information on the workflow stages for developing a project with UNIGINE.
- Programming Overview article to learn about the execution sequence and the ways of creating projects in Unigine.
- Creating C# Application article to learn about creating a C# project in Unigine.
- Creating UnigineScript Application article to learn about creating a UnigineScript project in Unigine.
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.
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.
Additional information:
- For more information on UNIGINE node types, see Built-in Node Types page.
- For more information on managing nodes via API, see Nodes classes page.
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:
// auxiliary variables for messages
string file_name = "file.txt";
int ID = 10;
// reporting an error message
Log.Error("Loading mesh: can't open \"{0}\" file\n", file_name);
// reporting a message
Log.Message("-> Added {0} UI elements.\n", ID);
// reporting a warning message
Log.Warning("ID of the \"{0}\" file: {1}.\n", file_name, ID);
// reporting a fatal error message to the log file and closing the application
Log.Fatal("FATAL ERROR reading \"{0}\" file!\n", file_name);
Additional information:
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.
// 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.
// saving current world to the my_world.world file
Unigine.Console.Run("world_save my_world");
// loading world from the my_world.world file
Unigine.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.
To close the application the following code is to be used:
// 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.
// 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).
// 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();
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()).
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).
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);
Additional information:
- For more information on managing smart pointers, see Working with Smart Pointers page.
- For more information on ownership management, see Memory Management page.