Working With Components in Editor
C++ components in Editor are not managed directly, but rather via the associated properties. Unlike C# components that you can create right in UnigineEditor, C++ components are to be added to your C++ project via the IDE first, then compiled, and then registered by the Component System, which generates associated properties to be assigned to nodes in UnigineEditor.
Workflow#
To add a C++ component you should do the following:
1. Open Project in IDE#
First, open your project in an IDE (Microsoft Visual Studio is recommended) - go to SDK Browser and choose Open Code IDE on your project's card.
2. Add New C++ Component Class#
In an IDE create a new C++ class (Project → Add Class) and call it MyComponent.
Make sure it inherits from the ComponentBase class. You can build a whole hierarchy using your own components, by inheriting some components from others in accordance with the OOP principles (we'll review this in detail later).
Two files*.h and *.cpp will be added to your C++ project.
3. Write Component Code#
Header File (*.H)#
In the header file (MyComponent.h) you should do the following:
-
Declare a component class using the following macro:
COMPONENT_DEFINE(MyComponent, Unigine::ComponentBase);
Class name defines the name of the property (the corresponding *.prop file is automatically saved in your project's data folder).
-
Declare parameters with their default values (if any). The simplest way to declare a parameter is to use the following macro (optional arguments are enclosed in square brackets []):
PROP_PARAM(param_type, name[, default_value, title, tooltip, group]);
A set of macros enables you to define multiple types of component parameters to be displayed and adjusted in UnigineEditor. See the the ComponentBase class documentation for the detailed information about supported parameter types and attributes.
The list of available macros includes the following ones to declare complex-type parameters (arrays, structures, and arrays of structures):
PROP_STRUCT(type, name); PROP_ARRAY(type, name); PROP_ARRAY_STRUCT(type, name);
-
Declare which methods you are going to use to implement component's logic, and during which stages of the execution sequence to call them:
public: COMPONENT_INIT(my_init); COMPONENT_UPDATE(my_update); COMPONENT_SHUTDOWN(my_shutdown); // ... protected: void my_init(); void my_update(); void my_shutdown();
- Declare all necessary auxiliary parameters and functions.
Implementation File (*.CPP)#
In the implementation file (MyComponent.cpp) you should do the following:
-
Register the component class in the Component System using the following macro:
REGISTER_COMPONENT(MyComponent);
- Implement your component's logic in the methods that you've declared in the header file.
We'll make a simple component that will rotate a node to which it is assigned every frame at a certain angle. Summarizing all the things above we can make our component look like it is shown below. You can copy the code, paste it to the corresponding files in your project and save them in your IDE.
#pragma once
// include the ComponentSystem header
#include <UnigineComponentSystem.h>
class MyComponent :
public Unigine::ComponentBase
{
// declaring constructor and destructor for our class and define the name of the property to be associated with the component.
// The MyComponent.prop file containing all parameters listed below will be generated in your project's data folder after running the app for the first time
COMPONENT_DEFINE(MyComponent, Unigine::ComponentBase);
// declaring parameters
PROP_PARAM(Float, angle, 10.0f, "Angle", "Node rotation angle", 0, "max=360;min=0");
// registering methods to be called at the corresponding stages of the world logic (methods are declared in the protected-section below)
COMPONENT_UPDATE(update);
protected:
// declaring methods to be called at the corresponding stages of the world logic
void update();
};
#include "MyComponent.h"
// registering the MyComponent component in the Component System
REGISTER_COMPONENT(MyComponent);
using namespace Unigine;
using namespace Math;
// method to be called every frame
void MyComponent::update()
{
// turn the object each frame around the Z axis at an anlge set dy the component parameter (Angle)
node->rotate(0.0f, 0.0f, angle);
}
4. Compile And Run To Generate Property#
Don't forget to set the appropriate platform and configuration settings for your project before compiling your code in Visual Studio.
Build and run your application by hitting Ctrl + F5 to make the Component System register the component and generate an associated property with the corresponding set of parameters (according to your component's class declaration) to be used to assign the component to nodes. Close the application after running it and switch to UnigineEditor.
5. Assign Component To Nodes In Editor#
After successful registration all available properties associated with your C++ components are displayed in the Properties window as children of the basic node_base property.
The component is ready, but now it must be assigned to some node, otherwise its code won't be executed. Decide which object you want to rotate (e.g. Material Ball), drag the property (MyComponent.cpp) from the Properties hierarchy and drop onto this object (you can drop it on the object directly in the viewport or on the node in the World Nodes window).
In the Parameters window set the desired Angle value with a slider and save the world by hitting Ctrl + S.
The component can be assigned to a node via code as well:
// like this:
ComponentSystem::get()->addComponent<MyComponent>(node);
// or like this (via adding the property associated with the component):
node->addProperty("mycomponent_prop_name");
Now, we're ready to launch our application via the SDK Browser by selecting the project on the Projects tab and clicking Run. But before doing so let's make sure, that appropriate Customize Run Options (Debug version in our case) are selected, by clicking an ellipsis under the Run button (check Debug and Remember then click Run).
Check if the object rotates as intended!
Hierarchy and Inheritance, Overriding Parameters and Overloading Methods#
Suppose you have a component that implements a certain functionality, and you need a certain number of subcomponents that have exactly the same functionality but different parameter values. This is a common situation when implementing quality presets or control configurations. Simply inherit a property from the base component in the Editor for each preset and configure the required parameter values. You can then assign these inherited properties to nodes, thus attaching the logic of the base component with parameter values taken from the inherited properties. No unnecessary copying, no redundant code.
In addition to overriding parameter values, you can also extend the functionality of base components in child components. This is done in accordance with the OOP concept by inheriting the child component class from the base component class, for example:
// The Interactable class is a child of the base ComponentBase class
class Interactable : public Unigine::ComponentBase
{
public:
COMPONENT_DEFINE(Interactable, ComponentBase);
// parameter
PROP_PARAM(String, tooltip, "Info about the object");
// virtual method implementing and action (implemented in children)
virtual void action(int num = 0) {};
};
// ------------------------------------------------------------------
// The Toggle class is a child of the Interactable class
class Toggle : public Interactable
{
public:
COMPONENT_DEFINE(Toggle, Interactable);
// additional parameter
PROP_PARAM(Node, controlNode, nullptr);
// ...
// overloading the parent method Action
void action(int num = 0)
{
if (num != 0)
return;
Unigine::Log::message("Toggle action for: %s \n ", node->getName());
// ...
}
// extending the parent functionality with a new method
void someNewMethod()
{
//...
}
// ...
};
We'll review more examples a bit later.
Debugging Components#
The Microsoft Visual Studio environment is known for its excellent debugging tools. You can test the source code of C++ components while your application is running, whether the application is launched via the SDK Browser or created and running from the IDE.
Debugging in IDE#
To compile all your components, build the application, and debug it by running it directly from the IDE, select Debug → Start Debugging (or press F5):
Now you can add the necessary breakpoints and just press F5 to start the debugging process.
At startup, the application will run with the world set for your project in SDK Browser at creation. If the default world is not set, you will see a black screen after launching the app. In this case, you can open the console (press "~" on the keyboard) and load the desired world using the world_load <world_name> console command.
Attaching to Running Application#
You can also connect Visual Studio debugger to an instance of your application that is already running. The most common case is launching it via the SDK Browser. Just add necessary breakpoints and select Debug → Attach To Process from the main menu.
Type your project's name in the search field in the Available processes section to find the process you need. Select it in the list and click Attach
Setting Breakpoints#
Breakpoints are used to pause your application by stopping execution of your code and giving you a chance to inspect and analyze your program's operation. You can check values of variables, and move step-by-step through your code.
To set up a breakpoint put the cursor to the desired line of your code and press F9 or use the menu.