Функции обратного вызова для обработки событий
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. Обратный вызов (коллбэк) - это обертка функции, представляющая собой указатель на статические функции и функции-члены, которые, как ожидается, будут выполнены с указанными параметрами в определенный момент. Функция обратного вызова может быть передана как аргумент другой функции.
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: В Unigine C ++ API CallbackBase является базовым классом для представления функций обратного вызова с переменным числом аргументов от 0 до 5. Для создания функции обратного вызова используется функция MakeCallback():
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: Коллбэк для функции-члена текущего или другого класса создается следующим образом:
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: Классы CallbackBase.. используются для создания обратного вызова с фиксированным количеством аргументов. В зависимости от количества аргументов следует использовать соответствующий класс. В этом случае вы должны предоставить аргументы шаблона:
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 >
Для использования перегруженных функций и методов в качестве обратных вызовов необходимо указать параметры шаблона:
MakeCallback< Class, ReturnType, Callback Parameters Types >
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. Для создания пользовательского обратного вызова используется функция run() класса CallbackBase...
// 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:Вы также можете использовать лямбда-выражения для определения функций обратного вызова:
// 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. В следующем разделе содержится полный исходный код простого примера использования обратного вызова.
#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__
#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;
}
Практическое использование#
Обратные вызовы широко используются при обработке событий. У ряда членов Unigine API есть несколько предопределенных событий, которые в определенных случаях можно обрабатывать с помощью обратных вызовов.
Триггеры#
Триггеры используются для обнаружения изменений положения или состояния узлов. Unigine предлагает три типа встроенных триггеров:
- NodeTrigger запускает функцию обратного вызова, когда контролируемый им узел включается/выключается или изменяется его позиция.
- WorldTrigger запускает функцию обратного вызова, когда какой-либо узел (коллайдер или нет) входит внутрь или выходит из него.
- PhysicalTrigger запускает функцию обратного вызова, когда физический объект входит внутрь или выходит из него.
Вот простой пример использования WorldTrigger:
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;
}
Чтобы удалить обратные вызовы, используйте следующий код:
// remove the callback by using its id
trigger->removeEnterCallback(enter_callback_id);
// clear all leave callbacks
trigger->clearLeaveCallbacks();
Смотрите также
- C++ API сэмпл ( <UnigineSDK>/source/samples/Api/Nodes/NodeTrigger ).
- C++ API сэмпл ( <UnigineSDK>/source/samples/Api/Nodes/WorldTrigger ).
- C++ API сэмпл ( <UnigineSDK>/source/samples/Api/Nodes/PhysicalTrigger ).
Виджеты#
Базовый класс виджетов Widget позволяет регистрировать обратные вызовы для событий, определенных в классе GUI. В следующем примере показано, как создать WidgetButton и зарегистрировать функцию обратного вызова для события CLICKED:
// 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;
}
Смотрите также
- Пример C++ API (<UnigineSDK>/source/csharp/samples/Api/Widgets/WidgetCallbacks).
- Пример Создания пользовательского интерфейса .
Физика#
Вы можете отслеживать определенные события связанных с физикой Bodies и Joints :
- Body::addFrozenCallback() для отслеживания события, когда тело замерзает.
- Body::addPositionCallback() для отслеживания события, когда тело меняет свое положение.
- Body::addContactEnterCallback() для отслеживания события, когда возникает контакт (тело начинает касаться другого тела или поверхности, подверженной столкновению).
- Body::addContactLeaveCallback() для отслеживания события, когда контакт заканчивается (тело перестает касаться другого тела или поверхности, подверженной столкновению).
- Body::addContactsCallback(), чтобы получить все контакты тела, включая новые ( enter ) и конечные ( leave ). Оставленные контакты удаляются после этапа выполнения обратного вызова, поэтому это единственная точка, где вы все еще можете их получить.
- Joint::addBrokenCallback() для отслеживания события при разрыве сустава.
В следующем примере показан способ регистрации обратных вызовов для BodyRigid и изменения цвета меша в зависимости от его состояния:
// 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;
}
Смотрите также
Сэмпл C ++ API, расположенный в папке <UnigineSDK>/source/samples/Api/Physics/BodyCallbacks.
Свойства (Properties)#
Функции обратного вызова могут использоваться для определения действий, которые должны выполняться при добавлении или удалении свойств (properties) узла и поверхности, а также при замене свойств узла. Вот пример, демонстрирующий, как отслеживать добавление свойства узла с помощью обратных вызовов:
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;
}
Вы можете добавить обратные вызовы, чтобы отслеживать любые изменения, внесенные в свойство и его параметры, и выполнять определенные действия.
В приведенном ниже примере показано, как добавить обратный вызов для отслеживания изменений параметров свойства и сообщить имя свойства и измененный параметр (предположим, что у нас есть ручное свойство с именем my_prop с целочисленным параметром с именем my_int_param).
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;
}
Вы также можете добавить обратные вызовы диспетчеру Properties для отслеживания любых изменений, внесенных в любое свойство, и выполнения определенных действий:
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;
}