事件处理回调
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.要引发自定义回调,请使用CallbackBase..类的run()函数。
// 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:您还可以将lambda表达式用于回调:
// 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在触发节点启用或触发节点 position 更改时触发回调。
- WorldTrigger在任何节点(是否有对撞机)获取其 inside 或 outside 时触发回调。
- PhysicalTrigger在物理对象获取 inside 或 outside 时触发回调。
这是一个简单的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 ).
- 创建用户界面教程。
物理#
- Body::addFrozenCallback()跟踪物体冻结时的事件。
- Body::addPositionCallback()用于在主体更改其位置时跟踪事件。
- Body::addContactEnterCallback()跟踪出现接触时的事件(身体开始接触另一个身体或可碰撞的表面)。
- Body::addContactLeaveCallback()用来跟踪接触结束时的事件(身体停止接触另一个身体或可碰撞的表面)。
- Body::addContactsCallback()获取身体的所有接触,包括新接触(输入)和结束接触(离开)。离开联系人在回调执行阶段之后被删除,因此这是仍然可以获取它们的唯一点。
- 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;
}
也可以看看
位于<UnigineSDK>/source/samples/Api/Physics/BodyCallbacks文件夹中的C ++ API示例。
物产#
回调函数可用于确定在添加或删除节点和曲面属性以及交换节点属性时要执行的操作。这是一个演示如何通过回调跟踪添加节点属性的示例:
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;
}