6. Creating and Deleting Nodes at Runtime
<< RETURN TO THE PREVIOUS SECTION
Nodes can be created and deleted at runtime almost as easy as in the Editor. The basic set of actions is as follows:
- Creation. To create a node we should declare a smart pointer for the type of the node we are going to create and call a constructor of the corresponding class providing construction parameters if necessary.
- Deletion. To delete a node we simply call the deleteLater() method for the node we are going to remove.
// creating a node of the NodeType named nodename
<NodeType>Ptr nodename = <NodeType>::create(<construction_parameters>);
// removing the node
nodename.deleteLater();
Now let us illustrate the process of creating and deleting a node using a simple static mesh (ObjectMeshStatic) by both creating a box surface and loading a mesh from an asset as an example.
#include <UnigineObjects.h>
using namespace Unigine;
/* .. */
int AppWorldLogic::init()
{
// first, we're creating a mesh instance as it is required to call an ObjectMeshStatic constructor
MeshPtr mesh = Mesh::create();
// loading a mesh from an fbx model imported to the data/fbx/ folder using the Editor
mesh->load("fbx/model.fbx/model.mesh");
// creating a box surface with a given size to the mesh
mesh->addBoxSurface("box_surface", Math::vec3(0.5f, 0.5f, 0.5f));
//create an ObjectMeshStatic node
ObjectMeshStaticPtr my_object = ObjectMeshStatic::create(mesh);
// removing the node
my_object.deleteLater();
// clearing the mesh
mesh->clear();
}
Additional information:
- For more information on managing smart pointers, see the Working with Smart Pointers article.
- For more information on ownership management, see the Memory Management article.
Project Progress#
In our project we are going to need some objects, so let us:
- write an auxiliary method to create an ObjectMeshDynamic node (addMeshToScene),
- write an auxiliary method to append some initial set of objects to our scene(initObjects),
- store the list of nodes created at runtime in a Vector.
In the AppWorldLogic.h file, we include the UnigineObjects.h library to work with ObjectMeshDynamic class, define the scene objects vector and declare our methods: addMeshToScene and initObjects.
// AppWorldLogic.h
#include <UnigineObjects.h>
#include <UnigineEditor.h>
using namespace Unigine;
/* ... */
class AppWorldLogic : public WorldLogic {
public:
/* .. */
private:
/* .. */
// auxiliary functions
int addMeshToScene(const char *file_name, const char *mesh_name, const char *material_name, Math::Vec3 position);
int initObjects();
/* .. */
// scene objects vector
Vector<ObjectMeshDynamicPtr> Objects;
/* .. */
};
In the AppWorldLogic.cpp file let us implement our methods and insert initObjects method into the AppWorldLogic::init() method. We are also going to do some cleanup (delete all created nodes) in the AppWorldLogic::shutdown() method.
// AppWorldLogic.cpp
/* .. */
/// 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;
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, 0.5f, 0.5f));
omd = ObjectMeshDynamic::create(mesh);
}
// setting node material, name and transformation
omd->setMaterial(material_name, "*");
omd->setName(mesh_name);
omd->setWorldPosition(position);
// updating the list of scene objects
Objects.append(omd);
//reporting progress to the console
Log::message("-> Object %s added to the scene.\n", mesh_name);
// clearing the mesh
mesh->clear();
return 1;
}
/* .. */
/// method performing initialization of objects
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), "mesh_base", Math::Vec3(x, y, 1.0f));
index++;
}
}
// reporting progress to the console
Log::warning("Objects generation OK!\n\n");
return 1;
}
/* .. */
int AppWorldLogic::init()
{
// creating objects
initObjects();
/* .. */
return 1;
}
/* .. */
int AppWorldLogic::shutdown() {
// deleting all created nodes
for(int i = 0; i < Objects.size(); i++)
{
// removing current node from the scene
Objects[i].deleteLater();
}
// updating the list of scene objects
Objects.clear();
return 1;
}
/* .. */
Source Files
You can copy the code below and paste it to the corresponding source files of your project:
AppWorldLogic.h
#ifndef __APP_WORLD_LOGIC_H__
#define __APP_WORLD_LOGIC_H__
#include <UnigineLogic.h>
#include <UnigineStreams.h>
#include <UnigineObjects.h>
using namespace Unigine;
class AppWorldLogic : public WorldLogic {
public:
AppWorldLogic();
virtual ~AppWorldLogic();
virtual int init();
virtual int update();
virtual int postUpdate();
virtual int updatePhysics();
virtual int shutdown();
virtual int destroyRenderResources();
virtual int save(const StreamPtr &stream);
virtual int restore(const StreamPtr &stream);
private:
//auxiliary functions
int addMeshToScene(const char *file_name, const char *mesh_name, const char *material_name, Math::Vec3 position);
// initialization functions
int initObjects();
// scene objects vector
Vector<ObjectMeshDynamicPtr> Objects;
};
#endif // __APP_WORLD_LOGIC_H__
AppWorldLogic.cpp
#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.
//-----------------------------------------------------------------------------------------------------------------------------
//---------------------------------------------- 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;
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);
// updating the list of scene objects
Objects.append(omd);
// reporting progress to the console
Log::message("-> Object %s added to the scene.\n", mesh_name);
// clearing the mesh
mesh.clear();
return 1;
}
//-----------------------------------------------------------------------------------------------------------------------------
//---------------------------------------------- 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), "mesh_base", Math::Vec3(x, y, 1.0f));
index++;
}
}
// reporting progress to the console
Log::warning("Objects generation OK!\n\n");
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.
// creating objects
initObjects();
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.
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() {
// deleting all created nodes
for (int i = 0; i < Objects.size(); i++)
{
// removing current node from the scene
Objects[i]->deleteLater();
}
// updating the list of scene objects
Objects.clear();
return 1;
}
int AppWorldLogic::destroyRenderResources() {
// 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 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 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;
}