Обработка событий
When writing your application logic, one of the biggest challenges you're likely to face is connecting the various changing elements in a way that works. For example, making a character move, jump, or adding up the score can be relatively easy to do on its own. But connecting all things that happen in your game or application without making it confusing to work with can be very challenging.При написании логики приложения одна из самых больших проблем, с которой вы, вероятно, столкнетесь, — это взаимосвязь различных изменяющихся элементов таким образом, чтобы это работало. Например, заставить персонажа двигаться, прыгать или получать очки может быть относительно легко само по себе. Но связать все, что происходит в вашей игре или приложении, не создавая путаницы в работе, может быть очень сложно.
The Event System enables you to create application logic that is executed when an event is triggered during the application execution. It allows objects to subscribe one or more of their own functions to a subject's event. Then, when the subject triggers the event, the objects' functions are called in response. Such functions are also known as event handlers. Система событий позволяет создавать логику приложения, которая выполняется при запуске события во время выполнения приложения. Она позволяет объектам подписывать одну или несколько своих собственных функций на событие субъекта. Затем, когда субъект запускает событие, в ответ вызываются функции объектов. Такие функции также известны как обработчики событий.
The Event System features the following:Система событий включает в себя следующее:
- Strict type checking for signatures: you can see how many and which exactly arguments an event handler function requires. Строгая проверка типов для подписей: вы можете увидеть, сколько и какие именно аргументы требуются функции-обработчику событий.
- Compile-time checking: it ensures that argument types match event types, preventing runtime errors. Проверка во время компиляции: она гарантирует, что типы аргументов соответствуют типам событий, предотвращая ошибки во время выполнения.
- Simple subscription/unsubscription to events with lambda functions with no need to perform internal type conversions. Простая подписка/отмена подписки на события с помощью лямбда-функций без необходимости выполнять внутренние преобразования типов.
- Automatic event unsubscription. Автоматическая отмена подписки на событие.
- Temporary event deactivation: particular events can be temporarily disabled to perform specific actions without triggering them. Временная деактивация событий: определенные события можно временно отключить для выполнения определенных действий без их запуска.
- Batch management: you can unsubscribe from several subscriptions in a single function call. Пакетное управление: вы можете отказаться от нескольких подписок одним вызовом функции.
EventsСобытия#
An event is represented by the abstract Event class. It serves as an interface for interaction with the event. Typically, you get this interface via a reference as Event<args...>, where args represents a list of arguments the event will pass to a handler function.Событие представлено абстрактным классом Event. Он служит интерфейсом для взаимодействия с событием. Как правило, вы получаете этот интерфейс через ссылку в виде Event<args...>, где args представляет список аргументов, которые событие передаст функции-обработчику.
For example, Body::getEventPosition() returns the event with the following signature:Например, Body::getEventPosition() возвращает событие со следующей сигнатурой:
Event<const Ptr<Body>&>
It means the handler function must receive an argument of the same type when connected with the event.Это означает, что функция-обработчик должна получать аргумент того же типа при подключении к событию.
Emulating EventsИмитация событий#
Sometimes, it is necessary to emulate events. For custom events, you can use the EventInvoker::run() function that receives the same arguments as the event and invokes its handler functions.Иногда необходимо имитировать события. Для пользовательских событий вы можете использовать функцию EventInvoker::run(), которая получает те же аргументы, что и событие, и вызывает его функции-обработчики.
The following example shows how to create your event and then run it when necessary:В следующем примере показано, как создать собственное событие, а затем запустить его при необходимости:
class MyEventClass
{
public:
Event<int> &getEvent() { return event; }
void runEvent()
{
num_runs++;
event.run(num_runs);
}
private:
int num_runs = 0;
EventInvoker<int> event;
};
int main()
{
MyEventClass my_class;
my_class.getEvent().connect(
[](int n)
{
printf("n = %d\n", n);
}
);
my_class.runEvent();
my_class.runEvent();
return 0;
}
The existing events that are implemented for built-in objects and available through API can be emulated using the corresponding runEvent*() methods (without having to use EventInvoker::run()). For example, to emulate the Show event for a widget, call Widget::runEventShow().Существующие события, которые реализованы для встроенных объектов и доступны через API, могут быть имитированы с использованием соответствующих методов runEvent*() (без необходимости использовать EventInvoker::run()). Например, чтобы имитировать событие Show для виджета, вызовите Widget::runEventShow().
widget->runEventShow();
Event HandlersОбработчики событий#
The event handler functions can receive no more than 5 arguments.Функции-обработчики событий могут принимать не более 5 аргументов.
In addition, the Event System performs strict type checking for handler function signatures: you can subscribe to the event only if the types of the function arguments match the event types. For example, in the case of the event with a single int argument, you are only able to link it with a handler that also accepts a single integer argument. Even if the types can be implicitly converted (as in the example), subscribing is not permitted.Кроме того, система событий выполняет строгую проверку типов сигнатур функций-обработчиков: вы можете подписаться на событие только в том случае, если типы аргументов функции соответствуют типам событий. Например, в случае события с одним аргументом int вы можете связать его только с обработчиком, который также принимает один целочисленный аргумент. Даже если типы могут быть преобразованы неявно (как в примере), подписка не допускается.
Event<int> event; // event sugnature
void on_event(int a); // types match, subscription is allowed
void on_event(long a); // type mismatch, no subscription
This restriction also applies to the &, const, and const& modifiers. For instance, when the event type is a user class with no modifiers:Это ограничение также применяется к модификаторам &, const и const&. Например, когда типом события является пользовательский класс без модификаторов:
Event<MyClass> event;
void on_event(MyClass a); // types match, subscription is allowed
void on_event(MyClass a&); // type mismatch
void on_event(const MyClass a&);// type mismatch
Discarding ArgumentsОтбрасывание аргументов#
In most cases, not all arguments passed to the handler function by the event are necessary. So, events allow for discarding unnecessary arguments when functions subscribe to them. You can only discard one argument at a time, starting with the last one. For example, the following handler functions can subscribe to the event:В большинстве случаев не все аргументы, передаваемые функции-обработчику событием, необходимы. Таким образом, события позволяют отбрасывать ненужные аргументы, когда функции подписываются на них. Вы можете отбрасывать только один аргумент за раз, начиная с последнего. Например, следующие функции-обработчики могут подписаться на событие:
// the event
Event<int, float, const char *, vec3, const MyClass &> event;
// the event handlers with discarded arguments
on_event(int a, float b, const char *s, vec3 v, const MyClass &c);
on_event(int a, float b, const char *s, vec3 v);
on_event(int a, float b, const char *s);
on_event(int a, float b);
on_event(int a);
on_event();
Receiving Additional ArgumentsПолучение дополнительных аргументов#
To receive an additional user argument in the handler function, you need to add the required argument to the end of the handler arguments list and pass its value to the connect() function.Чтобы получить дополнительный пользовательский аргумент в функции-обработчике, вам нужно добавить требуемый аргумент в конец списка аргументов обработчика и передать его значение функции connect().
class UserClass
{
{ /* ... */ }
};
Event<int, float> event;
void on_event_0(int a, float b, int my_var) { /* ... */ }
void on_event_1(int a, float b, UserClass c) { /* ... */ }
void on_event_2(int a, float b, UserClass *c_ptr) { /* ... */ }
void on_event(float f, const char *str) { /* ... */ }
UserClass user_class;
int main()
{
// pass the value of the additional "my_var" argument to the handler function
event.connect(on_event_0, 33);
// pass the value of the additional "c" argument to the handler function
event.connect(on_event_1, user_class);
// pass the value of the additional "c_ptr" argument to the handler function
event.connect(on_event_2, &user_class);
// discard the int and float handler arguments, add the custom float and const char* and pass them to connect()
event.connect(on_event, 33.3f, "test");
return 0;
}
Subscribing to EventsПодписка на события#
For convenience, the Event System provides the EventConnection and EventConnections classes that allow simple event subscription/unsubscription. Let's go through them in detail.Для удобства система событий предоставляет классы EventConnection и EventConnections, которые позволяют просто подписаться на событие или отписаться от него. Давайте рассмотрим их подробнее.
Single Subscription with EventConnectionЕдиная подписка с EventConnection#
The EventConnection class keeps a connection between an event and its handler. For example, the connection between the event and the free handler function can be set as follows:Класс EventConnection поддерживает связь между событием и его обработчиком. Например, связь между событием и свободной функцией-обработчиком можно установить следующим образом:
EventConnection connection;
// a handler function
void on_event()
{
Log::message("\Handling the event\n");
}
void init()
{
//connect the handler function with the event by using EventConnection
getSomeEvent().connect(connection, this, on_event);
}
You can temporarily turn the event off to perform specific actions without triggering it.Вы можете временно отключить событие, чтобы выполнить определенные действия, не запуская его.
// disable the event
getSomeEvent().setEnabled(false);
/* perform some actions */
// and enable it again
getSomeEvent().setEnaled(true);
Moreover, you can toggle individual connections on and off (EventConnection instances), providing flexibility when working with events.Более того, вы можете включать и выключать отдельные подключения (экземпляры EventConnection), обеспечивая гибкость при работе с событиями.
EventConnection connection;
/* ... */
// disable the connection
connection.setEnabled(false);
/* perform some actions */
// and enable it back when necessary
connection.setEnabled(true);
Later, you can unsubscribe from the event via EventConnection as follows:Позже вы можете отписаться от события с помощью EventConnection следующим образом:
void shutdown()
{
// break the connection by using EventConnection
connection.disconnect();
}
If a class handles the event, you can declare the EventConnection instance as a class member and use it for events subscription. In this case, all linked subscriptions will be automatically removed when the class destructor is called. For example:Если класс обрабатывает событие, вы можете объявить экземпляр EventConnection как член класса и использовать его для подписки на события. В этом случае все связанные подписки будут автоматически удалены при вызове деструктора класса. Например:
// a class handling the event
class SomeClass
{
public:
// instance of the EventConnection class as a class member
EventConnection connection;
// an event handler implemented as a class member
void on_event()
{
Log::message("\Handling the event\n");
}
};
// create a class instance
SomeClass *obj = new SomeClass();
// connect the handler function with the event by using EventConnection;
// specify the class instance as the event handler belongs to the class
getSomeEvent().connect(obj->connection, obj, &SomeClass::on_event);
/* ... */
// the instance of the handler class is deleted with all its subscriptions;
// subscriptions are removed automatically in the destructor
delete obj;
Multiple Subscriptions with EventConnectionsНесколько подписок с подключениями к событиям#
The EventConnections class is a container for the EventConnection instances. Multiple subscriptions to a single event or different events can be linked to a single EventConnections instance. Класс EventConnections является контейнером для экземпляров EventConnection. Несколько подписок на одно событие или разные события могут быть связаны с одним экземпляром EventConnections.
For example, you can create multiple subscriptions to a single event as follows:Например, вы можете создать несколько подписок на одно событие следующим образом:
EventConnections connections;
// event handlers
static void on_some_event_0() { Log::message("\Handling the 1st event\n"); }
static void on_some_event_1() { Log::message("\Handling the 2nd event\n"); }
static void Main(string[] args)
{
// add two handlers for the event
// and link it to an EventConnections instance to remove a pack of subscriptions later
getSomeEvent().connect(connections, this, on_some_event_0);
getSomeEvent().connect(connections, this, on_some_event_1);
}
Also, you can create multiple subscriptions to different events:Кроме того, вы можете создать несколько подписок на разные события:
EventConnections connections;
// event handlers
static void on_some_event_0() { Log::message("\Handling the 1st event\n"); }
static void on_some_event_1() { Log::message("\Handling the 2nd event\n"); }
static void on_some_event_2() { Log::message("\Handling the 3rd event\n"); }
static void Main(string[] args)
{
// subscribe for different events with handlers to be executed when the events are triggered;
// here multiple subscriptions are linked to a single EventConnections class instance
getSomeEvent0().connect(connections, this, on_some_event_0);
getSomeEvent1().connect(connections, this, on_some_event_1);
getSomeEvent2().connect(connections, this, on_some_event_2);
}
Later, you can unsubscribe from the events via EventConnections as follows:Позже вы можете отписаться от события через EventConnections следующим образом:
// break the connection by using EventConnections
// all instances of EventConnection will be removed from the EventConnections container
connections.disconnectAll();
If a class handles the event, you can declare the EventConnections instance as a class member and use it for events subscription. In this case, all linked subscriptions will be automatically removed when the class destructor is called. See the example provided above — the same applies to EventConnections.Если класс обрабатывает событие, вы можете объявить экземпляр EventConnections членом класса и использовать его для подписки на события. В этом случае все связанные подписки будут автоматически удалены при вызове деструктора класса. Смотрите пример, приведенный выше — то же самое относится и к EventConnections.
Inheriting from EventConnectionsНаследование от EventConnections#
There is another way to automatically unsubscribe from the event handled by a class: you can inherit it from EventConnections. In this case, the event will interact with the user class in the same way as with EventConnections. All linked subscriptions will be removed automatically in the user class destructor.Есть еще один способ автоматически отписаться от события, обрабатываемого классом: вы можете унаследовать его от EventConnections. В этом случае событие будет взаимодействовать с пользовательским классом таким же образом, как и с EventConnections. Все связанные подписки будут автоматически удалены в деструкторе пользовательского класса.
class UserClass : public EnventConnections
{
public:
void init(Event<int, float> &event)
{
event.connect(this, &UserClass::on_event);
}
void on_event(int a, float b)
{
Log::message("\Handling the event\n");
}
};
int main()
{
UserClass *user_class = new UserClass();
user_class->init(getSomeEvent());
delete user_class; // my_class will automatically unsubscribe from all of the events in the destructor
return 0;
}
Direct SubscriptionПрямая подписка#
You can subscribe to events directly via the connect() function and unsubscribe via disconnect():Вы можете подписаться на события напрямую через функцию connect() и отписаться от них через disconnect():
- The connect() function is used to connect the event and the event handler. The number of function arguments may vary.Функция connect() используется для подключения события к обработчику событий. Количество аргументов функции может варьироваться.
- The disconnect() function is used to break the connection between the event and its handler. It receives a pointer to the handler function as an argument.Функция disconnect() используется для разрыва соединения между событием и его обработчиком. Она получает указатель на функцию-обработчик в качестве аргумента.
// a handler function
void on_event(int a, float b)
{
Log::message("\Handling the event\n");
}
void init()
{
// connect the handler function with the event
getSomeEvent().connect(on_event);
// disconnect the handler function and the event by using the pointer to this function
getSomeEvent().disconnect(on_event);
return 0;
}
If a class handles the event, you should pass the class instance as an argument to the connect() and disconnect() functions. For example:Если класс обрабатывает событие, вы должны передать экземпляр класса в качестве аргумента функциям connect() и disconnect(). Например:
// a class handling the event
class SomeClass
{
public:
// an event handler implemented as a class member
void on_event()
{
Log::message("\Handling the event\n");
}
};
// create a class instance
SomeClass *obj = new SomeClass();
// connect the handler function with the event;
// specify the class instance as the event handler belongs to the class
getSomeEvent().connect(obj, &SomeClass::on_event);
/* ... */
// remove the subscription
getSomeEvent().disconnect(obj, &SomeClass::on_event);
Connection DescriptorsДескрипторы подключений#
When subscribing to the event, a connection descriptor EventConnectionId is returned. You can save it and use it later to unsubscribe.При подписке на событие возвращается дескриптор подключения EventConnectionId. Вы можете сохранить его и использовать позже, чтобы отписаться.
// subscribe for the Contacts event with a lambda handler function and keeping connection ID
EventConnectionId contacts_handler_id = body->getEventContacts().connect([](const Ptr<Body> & body) {
Log::message("\Handling Contacts event (lambda).\n");
}
);
// remove the subscription later using the ID
body->getEventContacts().disconnect(contacts_handler_id);
In some cases using a connection descriptor leads to a failure:В некоторых случаях использование дескриптора подключения приводит к сбою:
- If you are subscribed to the event via EventConnection, it will become invalid when unsubscribing using the connection descriptor.Если вы подписаны на событие через EventConnection, оно станет недействительным при отмене подписки с использованием дескриптора подключения.
- If you have multiple subscriptions linked to EventConnections, unsubscribing from a single event using the connection descriptor will not remove the EventConnection instance from the container.Если у вас есть несколько подписок, связанных с EventConnections, отмена подписки на одно событие с использованием дескриптора подключения не приведет к удалению экземпляра EventConnection из контейнера.
Using Lambda FunctionsИспользование лямбда-функций#
You can pass a lambda function as an argument to the connect() function to handle the event: there is no need to perform internal type conversions. All features available for the handler functions are also applicable to lambda functions, except additional arguments.Вы можете передать лямбда-функцию в качестве аргумента функции connect() для обработки события: нет необходимости выполнять внутренние преобразования типов. Все функции, доступные для функций-обработчиков, также применимы к лямбда-функциям, за исключением дополнительных аргументов.
int main()
{
auto l = [](int a, float b) {};
event.connect(l);
event.connect([](int a, float b) {});
EventConnection connection;
event.connect(connection, [](int a {}));
return 0;
}
Using Predefined EventsИспользование предопределенных событий#
Some Unigine API members have several predefined events that can be handled in specific cases. The following chapters showcase the practical use of the concepts described above.Для некоторых членов Unigine API существует ряд предопределенных событий, которые могут обрабатываться в конкретных случаях. В следующих главах демонстрируется практическое использование концепций, описанных выше.
TriggersТриггеры#
Triggers are used to detect changes in nodes position or state. Unigine offers three types of built-in triggers:Триггеры используются для обнаружения изменений в положении или состоянии нод. Unigine предлагает три типа встроенных триггеров:
- NodeTrigger triggers events when the trigger node is enabled or its position has changed. NodeTrigger запускает события, когда нода триггера включается или ее положение изменяется.
- WorldTrigger triggers events when any node (collider or not) gets inside or outside it. WorldTrigger запускает события, когда любая нода (коллайдер или нет) попадает внутрь триггера или выходит за его пределы.
- PhysicalTrigger triggers events when physical objects get inside or outside it. PhysicalTrigger запускает события, когда физические объекты попадают внутрь триггера или выходят за его пределы.
Here is a simple NodeTrigger usage example. The event handlers are set via pointers specified when subscribing to the following events: EventEnabled and EventPosition.Вот простой пример использования NodeTrigger. Обработчики событий устанавливаются с помощью указателей, заданных при подписке на следующие события: EventEnabled и EventPosition.
#include <UnigineLogic.h>
#include <UnigineGame.h>
using namespace Unigine;
class AppWorldLogic : public Unigine::WorldLogic {
public:
virtual int init();
virtual int update();
/*...*/
private:
ObjectMeshStaticPtr object;
NodeTriggerPtr trigger;
void position_event_handler(const NodeTriggerPtr &trigger)
{
Log::message("Object position has been changed. New position is: (%f %f %f)\n", trigger->getWorldPosition().x, trigger->getWorldPosition().y, trigger->getWorldPosition().z);
}
void enabled_event_handler(const NodeTriggerPtr &trigger)
{
Log::message("The enabled flag is %d\n", trigger->isEnabled());
}
};
#include "AppWorldLogic.h"
using namespace Math;
int AppWorldLogic::init() {
// create a mesh
object = ObjectMeshStatic::create("core/meshes/box.mesh");
// change material albedo color
object->setMaterialParameterFloat4("albedo_color", vec4(1.0f, 0.0f, 0.0f, 1.0f), 0);
// create a trigger node
trigger = NodeTrigger::create();
// add the trigger node to the static mesh as a child node
object->addWorldChild(trigger);
// subscribe for the Enabled and Position events
trigger->getEventEnabled().connect(this, &AppWorldLogic::enabled_event_handler);
trigger->getEventPosition().connect(this, &AppWorldLogic::position_event_handler);
return 1;
}
int AppWorldLogic::update()
{
float time = Game::getTime();
Vec3 pos = Vec3(Math::sin(time) * 2.0f, Math::cos(time) * 2.0f, 0.0f);
object->setEnabled(pos.x > 0.0f || pos.y > 0.0f);
object->setWorldPosition(pos);
return 1;
}
And here is an example of WorldTrigger that demonstrates how to subscribe to the Enter and Leave events with a corresponding handler and keep this connection to unsubscribe later.А вот пример WorldTrigger, который демонстрирует, как подписаться на события Enter и Leave с помощью соответствующего обработчика и сохранить это подключение, чтобы отписаться позже.
WorldTriggerPtr trigger;
EventConnections leave_event_connections;
// implement the Enter event handler
void AppWorldLogic::enter_event_handler(const NodePtr &node)
{
Log::message("\nA node named %s has entered the trigger\n", node->getName());
}
// implement the Leave event handler
void AppWorldLogic::leave_event_handler1(const NodePtr &node)
{
Log::message("\nA node named %s has left the trigger\n", node->getName());
}
// implement an additional Leave event handler
void AppWorldLogic::leave_event_handler2(const NodePtr &node)
{
Log::message("\nAdditional Leave event handler.\n");
}
int AppWorldLogic::init()
{
// create a world trigger node
trigger = WorldTrigger::create(Math::vec3(3.0f));
// add the Enter event handler to be executed when a node enters the world trigger
trigger->getEventEnter().connect(this, &AppWorldLogic::enter_event_handler);
// adding two handlers for the Leave event to be executed when a node leaves the world trigger
// and attaching it to EventConnections instance to remove a pack of subscriptions later
trigger->getEventLeave().connect(leave_event_connections, this, &AppWorldLogic::leave_event_handler1);
trigger->getEventLeave().connect(leave_event_connections, this, &AppWorldLogic::leave_event_handler2);
return 1;
}
To remove subscriptions to the events, use the following code:Чтобы отписаться от события, используйте следующий код:
// remove all subscriptions to the Leave event
leave_event_connections.disconnectAll();
WidgetsВиджеты#
The widgets base class Widget allows subscribing to events.Базовый класс виджетов Widget позволяет подписываться на события.
The example below demonstrates how to subscribe for the widget's Clicked event.В приведенном ниже примере показано, как подписаться на событие Clicked виджета.
AppWorldLogic.cpp
// 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->getEventClicked().connect(this, &AppWorldLogic::onButtonClicked);
// add the created button widget to the system GUI
gui->addChild(widget_button, Gui::ALIGN_OVERLAP | Gui::ALIGN_FIXED);
return 1;
}
PhysicsФизика#
You can track certain events of the physics-related Bodies and Joints:Вы можете отслеживать определенные события, связанные с физическими телами и сочленениями:
- Body::getEventFrozen() to track an event when a body freezes. Body::getEventFrozen() для отслеживания события, когда тело прекращает движение.
- Body::getEventPosition() to track an event when a body changes its position.Body::getEventPosition() для отслеживания события, когда тело меняет свое положение.
- Body::getEventContactEnter() to track an event when a contact emerges (body starts touching another body or collidable surface).Body::getEventContactEnter() для отслеживания события, когда возникает контакт (тело начинает касаться другого тела или коллизионной поверхности).
- Body::getEventContactLeave() to track an event when a contact ends (body stops touching another body or collidable surface).Body::getEventContactLeave() для отслеживания события, когда контакт прекращается (тело перестает касаться другого тела или коллизионной поверхности).
- Body::getEventContacts() to get all contacts of the body including new ones (enter) and the ending ones (leave). Leave contacts are removed after the callback execution stage, so this is the only point where you can still get them.Body::getEventContacts(), чтобы получить все контакты тела, включая возникающие (входящие) и прекращающиеся (выходящие). Прекращающиеся контакты удаляются после этапа выполнения обратного вызова, так что это единственная точка, где вы все еще можете их получить.
- Joint::getEventBroken() to track an event when a joint breaks.Joint::getEventBroken() для отслеживания события при разрыве подключения.
The following code example shows how to subscribe to the Body events.В следующем примере кода показано, как подписаться на события Body.
AppWorldLogic.cpp
// set the node's albedo color to red on the freezing event
int AppWorldLogic::frozen_event_handler(const BodyPtr &body)
{
body->getObject()->setMaterialParameterFloat4("albedo_color", Math::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_event_handler(const BodyPtr &body)
{
body->getObject()->setMaterialParameterFloat4("albedo_color", Math::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_event_handler(const BodyPtr &body, int num)
{
body->getObject()->setMaterialParameterFloat4("albedo_color", Math::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(Math::Vec3(0, 0, 5.0f));
// add a rigid body to the box
BodyRigidPtr body = BodyRigid::create(meshStatic);
// subscribe to body events
body->getEventFrozen().connect(this, &AppWorldLogic::frozen_event_handler);
body->getEventPosition().connect(this, &AppWorldLogic::position_event_handler);
body->getEventContactEnter().connect(this, &AppWorldLogic::contact_enter_event_handler);
// add a shape to the body
ShapeBoxPtr shape = ShapeBox::create(body, Math::vec3(1.0f));
return 1;
}
PropertiesСвойства#
Events 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 events.События могут использоваться для определения действий, которые необходимо выполнить при добавлении или удалении свойств ноды и поверхности, а также при переключении свойств ноды. Вот пример, демонстрирующий, как отслеживать добавление свойства ноды с помощью событий.
AppWorldLogic.cpp
void AppWorldLogic::node_property_added(const NodePtr &node, const PropertyPtr &property)
{
Log::message("Property \"%s\" was added to the node named \"%s\".\n", property->getName(), node->getName());
}
void AppWorldLogic::parameter_changed(const PropertyPtr &property, int num)
{
Log::message("Parameter \"%s\" of the property \"%s\" has changed its value.\n", property->getParameterPtr(num)->getName(), property->getName());
}
void AppWorldLogic::property_removed(const PropertyPtr &property)
{
Log::message("Property \"%s\" was removed.\n", property->getName());
}
int AppWorldLogic::init()
{
NodeDummyPtr node = NodeDummy::create();
// search for a property named "new_property_0"
PropertyPtr property = Properties::findProperty("new_property_0");
// subscribing to the PropertyNodeAdd event (adding a node property)
node->getEventPropertyNodeAdd().connect(this, &AppWorldLogic::node_property_added);
// add the property named "new_property_0" to the node
node->addProperty("new_property_0");
// subscribing to the ParameterChanged event (changing property parameter)
property->getEventParameterChanged().connect(this, &AppWorldLogic::parameter_changed);
// change the value of the "my_int_param" parameter
property->getParameterPtr("my_int_param")->setValueInt(3);
// inherit a new property named "new_property_1" from the base property "surface_base"
Properties::findManualProperty("surface_base")->inherit("new_property_1");
// subscribing to property removal
Properties::getEventRemoved().connect(this, &AppWorldLogic::property_removed);
// remove the property named "new_property_1"
Properties::removeProperty(Properties::findProperty("new_property_1")->getGUID());
return 1;
}