Создание и удаление объектов из кода
That's great when everything is available in the scene from the outset, but more often than not some objects should be created and deleted at runtime.Хорошо, когда в сцене изначально уже все есть, но чаще бывает так, что какие-то объекты должны создаваться и удаляться во время выполнения приложения, или как говорят, в “рантайме”.
Creating and deleting nodes at runtime as almost as easy as in the Editor. The basic set of actions is as follows:Ноды можно создавать и удалять во время выполнения почти так же просто, как в редакторе. Основной набор действий заключается в следующем:
- Creation. To create a node we should call a constructor of the corresponding class by using the new keyword and providing construction parameters, if necessary.Создание. Чтобы создать ноду, мы должны вызвать конструктор соответствующего класса, используя ключевое слово new и при необходимости указав параметры построения.
- Deletion. To delete a node we simply call the DeleteLater() method for the node we are going to remove.Удаление. Чтобы удалить ноду, мы просто вызываем метод DeleteLater() для ноды, которую мы собираемся удалить.
In general, the code will look as follows:В общем случае это выглядит так:
// создаем ноду типа NodeType с именем nodename
<NodeType>Ptr nodename = <NodeType>::create(<construction_parameters>);
// удаление ноды
nodename.deleteLater();
Now let us illustrate the process of loading a node from a *.node file from disk using a NodeReference class.Теперь давайте проиллюстрируем процесс загрузки ноды из файла *.node с диска с использованием класса NodeReference.
// путь к *.node-ассету на диске
String placeablePath = "mynodefile.node"
// точка размещения (задает положение, поворот и масштаб)
NodePtr spawnPoint;
// загружаем ноду из указанного *.node-файла
NodeReferencePtr placeable = NodeReference::create(FileSystem::guidToPath(FileSystem::getGUID(placeablePath.get())));
// позиционируем созданную ноду в соответствии с трансформациями spawnPoint
placeable->setWorldTransform(spawnPoint->getWorldTransform());
You can also create new nodes of various built-in classes, for example create Static Mesh. Just provide the asset file path to a class constructor.Вы также можете создавать новые ноды различных базовых типов, например, Static Mesh, просто указав конструктору класса путь к файлу ассета.
// путь до файла ассета на диске
String meshAssetPath = "mynodefile.node"
// точка размещения (задает положение, поворот и масштаб)
public Node spawnPoint;
// проверяем, существует ли указанный ассет-файл на диске
if (FileSystem::isFileExist(FileSystem::guidToPath(FileSystem::getGUID(meshAssetPath.get())))
{
// создаем новый объект типа Static Mesh из указанного mesh-ассета
ObjectMeshStaticPtr mesh = ObjectMeshStatic::create(FileSystem::guidToPath(FileSystem::getGUID(meshAssetPath.get())));
// позиционируем созданную ноду в соответствии с трансформациями spawnPoint
placeable->setWorldTransform(spawnPoint->getWorldTransform());
}
PracticeПрактика#
For our project, let's create the ObjectPlacer component that will place the specified objects in the scene at the specified points (NodeDummy) and the Removable component that will allow us to remove objects by right-clicking. Destroying is always easier that creating, so let's start simple — create the Removable component by inheriting it from Interactable. As a click action, it will simply remove the node to which it is assigned.Для нашего проекта создадим компонент ObjectPlacer, который будет расставлять по сцене указанные предметы в заданных точках (NodeDummy) и компонент Removable, который позволит нам удалять объекты по правому щелчку мыши. Ломать не строить, поэтому давайте начнем с простого – создадим компонент Removable, унаследовав его от Interactable. В качестве действия по щелчку мыши он просто будет удалять ноду, на которую назначен.
#pragma once
#include <UnigineComponentSystem.h>
#include "Interactable.h"
class Removable :
public Interactable
{
public:
// Объявляем конструктор и деструктор для нашего класса, а также задаем имя связанного с компонентом свойства (property).
// Файл Removable.prop со всеми описанными ниже параметрами будет сгенерирован Компонентной системой в папке 'data' вашего проекта при первом запуске приложения
COMPONENT_DEFINE(Removable, Interactable);
// регистрация методов, которые будут вызываться на соответствующих этапах жизненного цикла приложения (сами методы объявляются в protected-блоке ниже)
COMPONENT_INIT(init);
// перегрузка метода Action реализующая удаление управляемой ноды
void action(int num = 0);
protected:
// объявление методов, которые будут вызываться на соответствующих этапах жизненного цикла приложения
void init();
};
#include "Removable.h"
// Регистрация компонента Removable
REGISTER_COMPONENT(Removable);
using namespace Unigine;
using namespace Math;
void Removable::init()
{
// задаем текст подсказки, которая будет отображаться при наведении курсора на объект
tooltip = Unigine::String::format("Объект можно удалить по правому щелчку мыши.");
}
// перегрузка метода Action реализующая удаление управляемой ноды
void Removable::action(int num)
{
// индексы действий, отличные от нуля, для данного компонента невалидны, поэтому игнорируются
if (num != 0)
return;
// удаляем ноду
node.deleteLater();
}
Now let's deal with the placer component.А теперь займемся компонентом-расстановщиком.
-
Create a new component, name it ObjectPlacer, and add the following code into it:Создаем новый компонент, называем его ObjectPlacer и пишем в нем следующий код:
#pragma once #include <UnigineComponentSystem.h> class ObjectPlacer : public Unigine::ComponentBase { public: // Объявляем конструктор и деструктор для нашего класса, а также задаем имя связанного с компонентом свойства (property). // Файл ObjectPlacer.prop со всеми описанными ниже параметрами будет сгенерирован Компонентной системой в папке 'data' вашего проекта при первом запуске приложения COMPONENT_DEFINE(ObjectPlacer, ComponentBase); // регистрация методов, которые будут вызываться на соответствующих этапах жизненного цикла приложения (сами методы объявляются в protected-блоке ниже) COMPONENT_INIT(init); // объединим параметры в группы: для первого и второго объектов PROP_GROUP("First_Object_Placement") PROP_PARAM(File, placeable, NULL); PROP_PARAM(Node, spawnPoints1, nullptr); // верхняя нода иерархии точек размещения PROP_GROUP("Second_Object_Placement") PROP_PARAM(File, meshAsset, NULL); PROP_PARAM(Material, meshMat, nullptr); PROP_PARAM(Node, spawnPoints2, nullptr); // верхняя нода иерархии точек размещения protected: // объявление методов, которые будут вызываться на соответствующих этапах жизненного цикла приложения void init(); };
#include "ObjectPlacer.h" #include "Removable.h" // Регистрация компонента ObjectPlacer REGISTER_COMPONENT(ObjectPlacer); using namespace Unigine; using namespace Math; void ObjectPlacer::init() { // проверяем, существует ли указанный node-файл, содержащий объект(ы) для размещения, и указан ли для него список точек if (FileSystem::isFileExist(FileSystem::guidToPath(FileSystem::getGUID(placeable.getRaw()))) && (spawnPoints1 != NULL)) { // загружаем содержимое указанного .node-ассета и расставляем по всем точкам, // отмеченным дочерними нодами указанного списка (Spawn-points) for (int i = 0; i < spawnPoints1->getNumChildren(); i++) { NodeReferencePtr node_ref = NodeReference::create(FileSystem::guidToPath(FileSystem::getGUID(placeable.getRaw()))); node_ref->setWorldTransform(spawnPoints1->getChild(i)->getWorldTransform()); ComponentSystem::get()->addComponent<Removable>(node_ref); } } // проверяем, существует ли указанный mesh-файл для создания объекта Static Mesh и указан ли для него список точек if (FileSystem::isFileExist(FileSystem::guidToPath(FileSystem::getGUID(meshAsset.getRaw()))) && (spawnPoints2 != NULL)) { // расставляем по всем точкам, отмеченным дочерними нодами указанного списка (Spawn-points) for (int i = 0; i < spawnPoints2->getNumChildren(); i++) { // создаем ноду типа Static Mesh с использованием указанного mesh-ассета и назначаем ей указанный материал ObjectMeshStaticPtr mesh = ObjectMeshStatic::create(FileSystem::guidToPath(FileSystem::getGUID(meshAsset.getRaw()))); mesh->setName(String::format("generated_mesh_%d", i)); mesh->setMaterial(meshMat, "*"); mesh->setWorldTransform(spawnPoints2->getChild(i)->getWorldTransform()); mesh->setIntersection(true, 0); // ƒобавляем ноде компонент Removable, который позволит удалить ее по правому щелчку ComponentSystem::get()->addComponent<Removable>(mesh); } } }
-
Let's save our files and then build and run our application by hitting Ctrl + F5 to make the Component System generate a property to be used to assign our component to nodes. Close the application after running it and switch to UnigineEditor.Сохраним файлы, а затем соберем и запустим приложение нажав Ctrl + F5, чтобы Компонентная система сгенерировала property для связи компонента с нодой. После запуска приложения закроем его и вернемся в UnigineEditor.
Now let's find the interior node (NodeDummy) and assign our ObjectPlacer property to it.Теперь найдем ноду interior (NodeDummy) и назначим на нее свойство (property) ObjectPlacer.
-
The parameters of the component are divided into two groups: the first one is for placement of the specified *.node asset, and the second one is for generation of Mesh Static objects from the specified *.mesh asset and assignment of the specified material. Next, let's assign a flower (the pot_ceramic_small.node asset) to the Placeable field in the First Object Placement group, toy_fox.mesh and toy_fox_mat material to the Mesh Asset and Mesh Material fields in the Second Object Placement group. Параметры компонента разбиты на две группы: первая для расстановки указанного *.node-ассета, а вторая – для генерации объектов Mesh Static из указанного *.mesh-ассета, с назначением указанного материала. Далее назначим цветок (ассет pot_ceramic_small.node) в поле Placeable в группе First Object Placement, toy_fox.mesh и материал toy_fox_mat в поля Mesh Asset и Mesh Material в группе Second Object Placement.
-
Next, let's define the points to place flowers: create a NodeDummy, name it plant_spawn_points and create several NodeDummy children for it (by clicking on it in the World Hierarchy and choosing Create → Node → Dummy), name them plant_spawn_point_1, plant_spawn_point_2 and plant_spawn_point_3 respectively and place them in the scene in the places where we need flowers. Do the same for the fox figure. We will have the following hierarchy:Затем, определимся с точками для расстановки цветов: создадим ноду NodeDummy, назовем ее plant_spawn_points и создадим несколько дочерних NodeDummy для нее (кликнув по ней в World Hierarchy и выбрав Create → Node → Dummy), назовем их plant_spawn_point_1, plant_spawn_point_2 и plant_spawn_point_3 соответственно и расставим по сцене в тех местах, где нам нужны цветы. То же самое сделаем для фигурки лисы. У нас получится вот такая иерархия:
-
Now drag the plant_spawn_points and fox_spawn_points nodes to the Spawn Point fields in the corresponding groups of the ObjectPlacer component:Теперь перетаскиваем ноды plant_spawn_points и fox_spawn_points в поля Spawn Point в соответствующих группах компонента ObjectPlacer:
Save the world by hitting Ctrl + S. Switch to SDK Browser and launch our application by clicking the Run button on our project's card and check the spawned objects.Сохраним мир, нажав Ctrl + S. Переключимся в SDK Browser и запустим наше приложение, нажав кнопку Run на карточке проекта и посмотрим на расставленные объекты.