Интерактивная зона
In games and VR applications, some actions are performed when you enter a specific area in the scene (for example, music starts, sound or visual effects appear). Let's implement this in our project: when you enter or teleport to a certain area, it rains inside it.В играх и VR-приложениях часто бывает нужно при вхождении пользователя в определенную область сцены выполнять определенные действия (например, включать музыку, звуковые или визуальные эффекты). В нашем проекте давайте сделаем так, чтобы при входе или телепортировании пользователя в определенную область, в ее пределах шел дождь.
In UNIGINE, the World Trigger node is used to track when any node (collider or not) gets inside or outside of it (for objects with physical bodies, the Physical Trigger node is used).В UNIGINE для отслеживания вхождения любой ноды (не важно, с физическим телом или без) в определенный объем и покидания этого объема используется World Trigger (для объектов с физическим телом используется Physical Trigger).
-
First of all, create a trigger to define the rain area. On the Menu bar, choose Create -> Logic -> World Trigger and place the trigger near the Material Ball, for example. Set the trigger size to (2, 2, 2).Первым делом создадим триггер для определения области, где будем делать дождь. Выберите в меню Create -> Logic -> World Trigger и расположите триггер, например, рядом с Material Ball. Установите размеры триггера (2, 2, 2).
-
Create a new TriggerZone component that will turn the rain node on and off (in our case, it is a particle system simulating rain), depending on the trigger state. Add the following code to the component and then assign it to the World Trigger node:Создайте новый компонент TriggerZone, который в зависимости от состояния триггера будет включать или выключать заданную ноду (в нашем случае это будет система частиц имитирующая дождь). Добавьте в компонент следующий код и затем назначьте этот компонент на ноду World Trigger:
#pragma once #include <UnigineComponentSystem.h> #include <UnigineWorlds.h> class TriggerZone : public Unigine::ComponentBase { public: COMPONENT(TriggerZone, Unigine::ComponentBase); COMPONENT_INIT(init); // имя свойства (property), связанного с компонентом PROP_NAME("TriggerZone"); // нода, которая будет включаться и выключаться триггером PROP_PARAM(Node, controlledNode); protected: void init(); // обработчики событий для триггера void trigger_enter(const Unigine::NodePtr &player); void trigger_leave(const Unigine::NodePtr &player); private: // нода-триггер Unigine::WorldTriggerPtr trigger; // ссылка на все подписки на события триггера (все подписки будут автоматически аннулированы при уничтожении компонента) Unigine::EventConnections conns; };
#include "TriggerZone.h" using namespace Unigine; REGISTER_COMPONENT(TriggerZone); void TriggerZone::init() { // проверяем, является ли нода, на которую назначен компонент WorldTrigger-ом if (node->getType() != Node::WORLD_TRIGGER) { Log::error("Node %s (TriggerZone) error: this component can only be assigned to a WorldTrigger.\n", node->getName()); ComponentSystem::get()->removeComponent<TriggerZone>(node); return; } // проверяем, назначеня ли управляемая нода (controlledNode) if (controlledNode.nullCheck()) { Log::error("Node %s (TriggerZone) error: 'controlledNode' is not assigned.\n", node->getName()); ComponentSystem::get()->removeComponent<TriggerZone>(node); return; } // добавляем обработчики событий, которые будут выполняться при входе какой-либо ноды в триггер и выходе из него trigger = checked_ptr_cast<WorldTrigger>(node); trigger->getEventEnter().connect(conns, this, &TriggerZone::trigger_enter); trigger->getEventLeave().connect(conns, this, &TriggerZone::trigger_leave); } // обработчик события, который при входе игрока (Player) в триггер включает управляемую ноду (ControlledNode) void TriggerZone::trigger_enter(const NodePtr &player) { if (!player->isPlayer()) return; controlledNode->setEnabled(true); } // обработчик события, который при выходе игрока (Player) из триггера выключает управляемую ноду (ControlledNode) void TriggerZone::trigger_leave(const NodePtr &player) { if (!player->isPlayer()) return; controlledNode->setEnabled(false); }
-
Now, let's add the rain effect. Drag the rain_effect.node asset from the vr/particles folder of the UNIGINE Starter Course Project add-on to the scene, add it as the child to the trigger node and turn it off (the component will turn it on later).Теперь добавим эффект дождя. Найдите ассет rain_effect.node (data/vr/particles в UNIGINE Starter Course Project), перетащите его в сцену и добавьте в качестве дочерней ноды к триггеру и отключите пока (ее будет включать наш компонент).
-
Drag the rain_effect node from the World Hierarchy window to the Controlled Node field of the TriggerZone (in the Parameters window).Затем перетащите ноду rain_effect из World Hierarchy в поле Controlled Node компонента TriggerZone.
Save changes (Ctrl+S) you've made to the world.Сохраните последние изменения в мире (Ctrl+S).
-
We also have to enable the Triggers Interaction option for the VR Player to make the trigger react on contacting it. Simply add the following line to the init_player() method of the VRPlayerSpawner component (Framework\Components\VRPlayerSpawner.cpp):Также нам нужно включить опцию Triggers Interaction для ноды VR Player чтобы триггер на нее реагировал. Просто добавьте следующую строку в метод init_player() компонента VRPlayerSpawner (файл: Framework\Components\VRPlayerSpawner.cpp):
void VRPlayerSpawner::init_player(const PlayerPtr &player) { // copy transform player->setWorldTransform(node->getWorldTransform()); ${#HL}$player->setTriggerInteractionEnabled(true); ${HL#}$ if (node->getType() >= Node::PLAYER_BEGIN && node->getType() <= Node::PLAYER_END) { PlayerPtr player_ref = checked_ptr_cast<Player>(node); // copy basic parameters player->setFov(player_ref->getFov()); player->setZNear(player_ref->getZNear()); player->setZFar(player_ref->getZFar()); for (int i = 0 ; i < player_ref->getNumScriptableMaterials(); i++) player->addScriptableMaterial(player_ref->getScriptableMaterial(i)); } // unparent current node // (cause vr_group can be our parent and we don't want to turn off // components that are children to us) node->setWorldParent(NodePtr()); }
-
Save changes (Ctrl+S), rebuild the project, and run it via SDK Browser, then try to enter the area defined by the trigger — it should raininside.Сохраните изменения (Ctrl+S), пересоберите проект и запустите его через SDK Browser, а затем попробуйте войти в заданную область – в ней будет локальный дождь.