This page has been translated automatically.
Video Tutorials
Interface
Essentials
Advanced
How To
Professional (SIM)
UnigineEditor
Interface Overview
Assets Workflow
Version Control
Settings and Preferences
Working With Projects
Adjusting Node Parameters
Setting Up Materials
Setting Up Properties
Lighting
Sandworm
Using Editor Tools for Specific Tasks
Extending Editor Functionality
Built-in Node Types
Nodes
Objects
Effects
Decals
Light Sources
Geodetics
World Nodes
Sound Objects
Pathfinding Objects
Players
Programming
Setting Up Development Environment
Usage Examples
C++
C#
UnigineScript
UUSL (Unified UNIGINE Shader Language)
Plugins
File Formats
Materials and Shaders
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 Creation
Content Optimization
Materials
Material Nodes Library
Miscellaneous
Input
Math
Matrix
Textures
Art Samples
Tutorials
Warning! This version of documentation is OUTDATED, as it describes an older SDK version! Please switch to the documentation for the latest SDK version.
Warning! This version of documentation describes an old SDK version which is no longer supported! Please upgrade to the latest SDK version.

Event Handling Callbacks

Callback is a function wrapper representing a pointer to static and member functions which are expected to be executed with specified parameters at a certain moment. A callback can be passed as an argument to a function.

Notice
Callbacks are guaranteed to be reentrant and provide safe multi-threaded execution.

In Unigine C++ API, the CallbackBase is the base class to represent callbacks with variable number of arguments from 0 to 5. To create a callback, the MakeCallback() function is used:

Source code (C++)
void callback_function() {
	/* .. */
}
CallbackBase *callback = MakeCallback(callback_function);

A callback to a member function of either the current class or another is created as follows:

Source code (C++)
void ThisClass::callback_function() {
	/* .. */
}
/* .. */
// the first argument is an instance of a class, the second one is the pointer to a member function
CallbackBase *callback = MakeCallback(this, &ThisClass::callback_function);

The CallbackBase classes are used to create a callback with fixed number of arguments. Depending on the number of arguments the corresponding class should be used. In this case you should provide template arguments:

Source code (C++)
void ThisClass::callback_function(NodePtr, int) {
	/* .. */
}
// create a callback with no predefined parameters
CallbackBase2<NodePtr, int> *callback = MakeCallback(this, &ThisClass::callback_function);

// create a callback with predefined parameters
CallbackBase2<NodePtr, int> *callback2 = MakeCallback(this, &ThisClass::callback_function, NodeDummy::create()->getNode(), 1);

// create a callback with parameters from lambda
CallbackBase2<NodePtr, int> *callback = MakeCallback([](NodePtr node, int value) { /* .. */ });

// create a callback with parameters from generic lambda
CallbackBase2<NodePtr, int> *callback = MakeCallback([](auto node, auto value) { /* .. */ });

To use overloaded functions and methods as callbacks, provide the template parameters:
MakeCallback< Class, ReturnType, Callback Parameters Types >

Source code (C++)
void ThisClass::callback_method()
{
	/* .. */
}

void ThisClass::callback_method(WidgetPtr w, WidgetPtr w2, int i)
{
	/* .. */
}

CallbackBase *callback = MakeCallback<ThisClass, void, WidgetPtr, WidgetPtr, int>(this, &ThisClass::callback_method);

To raise a custom callback, the run() function of one of the CallbackBase classes is used.

Source code (C++)
// run the callback with no parameters or with default predefined parameters
callback->run();

// run the callback with the specified parameters
callback->run(node, 2);

You can also use lambda expressions for callbacks:

Source code (C++)
// create a callback from lambda
int value = 5;
CallbackBase* callback = MakeCallback([value](){ /* .. */ });

// or std function
std::function<void()> callable_obj = [value]() { /* .. */ };
CallbackBase* callback = MakeCallback(callable_obj);

// or any other type of callable
struct Callable
{
void operator()() const { /* .. */ }
int value;
} callable_obj = { /* .. */ };

CallbackBase* callback = MakeCallback(callable_obj);

Usage Example#

The following section contains the complete source code of a simple callback usage example.

AppWorldLogic.h
#ifndef __APP_WORLD_LOGIC_H__
#define __APP_WORLD_LOGIC_H__

#include <UnigineLogic.h>
#include <UnigineStreams.h>
#include <UnigineCallback.h>

using namespace Unigine;
using namespace Math;

class AppWorldLogic: public Unigine::WorldLogic
{

public:

	int init() override;

	int update() override;
	int postUpdate() override;
	int updatePhysics() override;

	int shutdown() override;

	int save(const Unigine::StreamPtr &stream) override;
	int restore(const Unigine::StreamPtr &stream) override;
};

#endif // __APP_WORLD_LOGIC_H__
AppWorldLogic.cpp
#include "AppWorldLogic.h"

class SomeClass
{
public:
	// a member function to be called on the action
	void callback_method(int a, int b)
	{
		Log::message("\tcallback_method has been called %d %d\n", a, b);
	}

	void create_callbacks()
	{
		Log::message("Create a callback with no predefined parameters\n");
		CallbackBase * callback = MakeCallback(this, &SomeClass::callback_method);

		// run the callback with two parameters
		callback->run(73, 37);
		// run the callback with no parameters.
		// if the callback function has arguments, this will lead to unsafe behaviour
		callback->run();

		Log::message("Create a callback with predefined parameters\n");
		CallbackBase * callback2 = MakeCallback(this, &SomeClass::callback_method, 1, 2);

		// run the callback with no parameters. In this case, the predefined parameters will be used
		callback2->run();
		// run the callback with parameters. The predefined ones will be ignored
		callback2->run(351, 153);
		// run the callback with only 1 parameter.
		// the second predefined parameter will be used as the second argument
		callback2->run(118);
	}
};

// a callback function to be called on the action
void callback_function(int a, int b)
{
	Log::message("\tcallback_function has been called %d %d\n", a, b);
}

int AppWorldLogic::init()
{
	SomeClass *some = new SomeClass();
	// call the SomeClass member function
	some->create_callbacks();

	Log::message("Create a callback in the other instance\n");
	// use the callback function of the SomeClass to create a callback
	CallbackBase * callback3 = MakeCallback(some, &SomeClass::callback_method, 5, 25);
	callback3->run();

	Log::message("Create callback functions\n");
	CallbackBase * callback4 = MakeCallback(&callback_function);
	callback4->run(20, 70);
	CallbackBase * callback5 = MakeCallback(&callback_function, 50, 25);
	callback5->run();

	return 1;
}

int AppWorldLogic::update()
{

	return 1;
}

int AppWorldLogic::postUpdate()
{
	return 1;
}

int AppWorldLogic::updatePhysics()
{
	return 1;
}

int AppWorldLogic::shutdown()
{
	return 1;
}

int AppWorldLogic::save(const Unigine::StreamPtr &stream)
{
	UNIGINE_UNUSED(stream);
	return 1;
}

int AppWorldLogic::restore(const Unigine::StreamPtr &stream)
{
	UNIGINE_UNUSED(stream);
	return 1;
}

Practical Use#

Callbacks are widely used in event handling. A number of Unigine API members have several predefined events which can be handled by using callbacks in certain cases.

Triggers#

Triggers are used to detect changes in nodes position or state. Unigine offers three types of built-in triggers:

Here is a simple WorldTrigger usage example:

Source code (C++)
WorldTriggerPtr trigger;
void *enter_callback_id;

// implement the enter callback
void AppWorldLogic::enter_callback(NodePtr node)
{
	Log::message("\nA node named %s has entered the trigger\n", node->getName());
}

// implement the leave callback
void AppWorldLogic::leave_callback(NodePtr node)
{
	Log::message("\nA node named %s has left the trigger\n", node->getName());
}

int AppWorldLogic::init()
{

	// create a world trigger node
	trigger = WorldTrigger::create(Math::vec3(3.0f));

	// add the enter callback to be fired when a node enters the world trigger
	// and keep its id to be used to remove the callback when necessary
	enter_callback_id = trigger->addEnterCallback(MakeCallback(this, &AppWorldLogic::enter_callback));
	// add the leave callback to be fired when a node leaves the world trigger
	trigger->addLeaveCallback(MakeCallback(this, &AppWorldLogic::leave_callback));

	return 1;
}

To remove the callbacks use the following code:

Source code (C++)
// remove the callback by using its id
trigger->removeEnterCallback(enter_callback_id);
// clear all leave callbacks
trigger->clearLeaveCallbacks();
See Also
  • C++ API samples on NodeTrigger, WorldTrigger and PhysicalTrigger located in the <UnigineSDK>/source/samples/Api/Nodes folder.

Widgets#

The widgets base class Widget allows registering callbacks for events defined in the GUI class. The following example shows how to create a WidgetButton and register a callback function for the CLICKED event:

Source code (C++)
// event handler function
int AppWorldLogic::onButtonClicked()
{
	Log::message("\nThe widget button has been clicked\n");

	return 1;
}

int AppWorldLogic::init()
{

	// get a pointer to the system GUI
	GuiPtr gui = Gui::getCurrent();
	// create a button widget and set its caption
	WidgetButtonPtr widget_button = WidgetButton::create(gui, "Press me");
	// set a tooltip
	widget_button->setToolTip("Click this button");
	// rearrange a button size
	widget_button->arrange();
	// set a button position
	widget_button->setPosition(10, 10);
	// set the onButtonClicked function to handle the CLICKED event
	widget_button->addCallback(Gui::CLICKED, MakeCallback(this, &AppWorldLogic::onButtonClicked));
	// add the created button widget to the system GUI
	gui->addChild(widget_button, Gui::ALIGN_OVERLAP | Gui::ALIGN_FIXED);

	return 1;
}
See Also
  • A C++ API sample ( <UnigineSDK>/source/csharp/samples/Api/Widgets/WidgetCallbacks ).
  • An example of Creating User Interface.

Physics#

You can track certain events of the physics-related Bodies and Joints:

The following sample shows the way of registering callbacks for a BodyRigid and change the color of a mesh depending on its state:

Source code (C++)
// set the node's albedo color to red on the freezing event
int AppWorldLogic::frozen_callback(BodyPtr body)
{
	body->getObject()->setMaterialParameterFloat4("albedo_color", vec4(1.0f, 0.0f, 0.0f, 1.0f), 0);

	return 1;
}

// set the node's albedo color to blue on the position change event
int AppWorldLogic::position_callback(BodyPtr body)
{
	body->getObject()->setMaterialParameterFloat4("albedo_color", vec4(0.0f, 0.0f, 1.0f, 1.0f), 0);

	return 1;
}

// set the node's albedo color to yellow on each contact
int AppWorldLogic::contact_enter_callback(BodyPtr body, int num)
{
	body->getObject()->setMaterialParameterFloat4("albedo_color", vec4(1.0f, 1.0f, 0.0f, 1.0f), 0);

	return 1;
}

int AppWorldLogic::init()
{

	// create a box
	ObjectMeshStaticPtr meshStatic = ObjectMeshStatic::create("core/meshes/box.mesh");
	meshStatic->setPosition(Vec3(0, 0, 5.0f));
	// add a rigid body to the box
	BodyRigidPtr body = BodyRigid::create(meshStatic);
	// register callbacks for events
	body->addFrozenCallback(MakeCallback(this, &AppWorldLogic::frozen_callback));
	body->addPositionCallback(MakeCallback(this, &AppWorldLogic::position_callback));
	body->addContactEnterCallback(MakeCallback(this, &AppWorldLogic::contact_enter_callback));
	// add a shape to the body
	ShapeBoxPtr shape = ShapeBox::create(body, vec3(1.0f));

	return 1;
}
Notice
Physics-based callbacks are executed in the main tread, as they are mainly used for creation, destruction or modification of other objects.
See Also

A C++ API sample located in the <UnigineSDK>/source/samples/Api/Physics/BodyCallbacks folder.

Properties#

Callback functions can be used to determine actions to be performed when adding or removing node and surface properties as well as when swapping node properties. Here is an example demonstrating how to track adding a node property via callbacks:

Source code (C++)
void AppWorldLogic::node_property_added(NodePtr node, PropertyPtr property)
{
	Log::message("Property \"%s\" was added to the node named \"%s\".\n", property->getName(), node->getName());
}

int AppWorldLogic::init()
{

	NodeDummyPtr node = NodeDummy::create();

	// search for a property named "new_property_0"
	PropertyPtr property = Properties::findProperty("new_property_0");

	// set the callback function on adding a node property
	node->addCallback(Node::CALLBACK_PROPERTY_NODE_ADD, MakeCallback(this, &AppWorldLogic::node_property_added));

	// add the property named "new_property_0" to the node
	node->addProperty("new_property_0");

	return 1;
}

You can add callbacks to track any changes made to a property and its parameters and perform certain actions.

The example below shows how to add a callback to track changes of property parameters and report the name of the property and the changed parameter.

Source code (C++)
void AppWorldLogic::parameter_changed(PropertyPtr property, int num)
{
	Log::message("Parameter \"%s\" of the property \"%s\" has changed its value.\n", property->getParameterPtr(num)->getName(), property->getName());	
}

int AppWorldLogic::init()
{

	// set the callback function on parameter change
	property->addCallback(Property::CALLBACK_PARAMETER_CHANGED, MakeCallback(this, &AppWorldLogic::parameter_changed));

	// change the value of the "my_int_param" parameter
	property->getParameterPtr("my_int_param")->setValueInt(3);

	return 1;
}

You can also add callbacks to the Properties manager to track any changes made to any property and perform certain actions:

Source code (C++)
void AppWorldLogic::property_removed(PropertyPtr property)
{
	Log::message("Property \"%s\" was removed.\n", property->getName());
}

int AppWorldLogic::init()
{

	// inherit a new property named "new_property_1" from the base property "surface_base"
	Properties::findManualProperty("surface_base")->inherit("new_property_1");

	// set the callback function on property removal
	Properties::addCallback(Properties::CALLBACK_REMOVED, MakeCallback(this, &AppWorldLogic::property_removed));

	// remove the property named "new_property_1"
	Properties::removeProperty(Properties::findProperty("new_property_1")->getGUID());

	return 1;
}

See Also#

These are not all usage examples of event handling callbacks. The following list contains more API members supporting event handling:

  • UserInterface - for handling events from widgets created by loading a UI file.
  • Render - callback functions can be used to get access to buffers and matrices at intermediate stages of the rendering sequence.
  • Console supports adding a callback function that will be executed when a text is output to the console.
  • EditorLogic - a set of editor callback functions can be overridden for certain purposes.
  • ComponentBase - there may be performed certain actions on destroying a component.
  • AsyncQueue - сallback functions can be used to determine actions to be performed when certain resources are loaded.
  • WorldSplineGraph provides a set of callbacks for handling actions on editing points and segments.
  • Viewport - callback functions can be used to get access to buffers and matrices at intermediate stages of the rendering sequence.
Last update: 2023-06-23
Build: ()