# Creating Custom Components
> **Warning:** The functionality described in this article is not available in the Community SDK edition.
> You should upgrade to [**Sim**](https://l.unigine.com/SdhugY462) SDK edition to use it.
You can extend the initial set of components that can be added to entities by adding custom ones. In the article, we will consider 2 cases:
- Only 1 computer is used for simulation (i.e. no synchronization over network).
- Several computers synchronized over LAN are used for simulation.
## Creating a Simple Custom Component
When only 1 computer is used for simulation, your custom component won't be different from any other component of a UNIGINE-based application. As an example let us consider adding a water drop component for the *Be-200* aircraft available in the **IG Template**.
Follow the instructions given below to create you own custom component.
1. Create a new project using the **IG Template** as described [here](../ig/index.md#workflow). 
2. Create the following files describing your new component to the C++ project: In the header file (`WaterDropAircraftController.h`) define the name for the property file and describe parameters of the component: WaterDropAircraftController.h | Close
`WaterDropAircraftController.h` ```cpp // Specifying parameters for our component PROP_PARAM(Toggle, open, false, "Open", "Input parameter for enabling/disabling the effect", "Input"); PROP_PARAM(Float, normalize_flow, 1.0f, "Normalize Flow", "Input parameter for the effect power normalization", "Input"); PROP_PARAM(Float, normalize_payload, 1.0f, "Normalize Payload", "Input parameter for payload normalization", "Input"); PROP_PARAM(Node, particles_system, "Particles System", "ObjectParticles with the effect", "Effect"); PROP_PARAM(Float, spawn_rate_factor, 100.0f, "Spawn Rate Factor", "Multiplier for spawn rate", "Effect"); PROP_PARAM(Toggle, controlled_payload_time, false, "Controlled Payload Time", "Enable: water effect can be stopped automatically", "Payload") PROP_PARAM(Float, max_water_payload, 100.0f, "Max Water Payload", "Full water payload in units (weight or volume)", "Payload") PROP_PARAM(Float, max_flow_speed, 1.0f, "Max Flow Speed", "Maximum water flow speed (units per second)", "Payload"); ``` In the implementation file (`WaterDropAircraftController.cpp`) write your component's logic. At the initialization stage subscribe to parameter change event, as this component (property) will be associated with IG components changing these parameters when receiving commands from a host. WaterDropAircraftController.cpp | Close
`WaterDropAircraftController.cpp` ```cpp using namespace Unigine; void WaterDropAircraftController::init() { // Adding a callback on changing the property parameters getProperty()->addCallback(Property::CALLBACK_PARAMETER_CHANGED, MakeCallback(this, &WaterDropAircraftController::parameterChanged)); } ```
- WaterDropAircraftController.h | Close
`WaterDropAircraftController.h` ```cpp #pragma once #include #include #include class WaterDropAircraftController final: public Unigine::ComponentBase { public: COMPONENT_DEFINE(WaterDropAircraftController, Unigine::ComponentBase); COMPONENT_INIT(init); COMPONENT_UPDATE(update); COMPONENT_SHUTDOWN(shutdown); // Specifying parameters for our component PROP_PARAM(Toggle, open, false, "Open", "Input parameter for enabling/disabling the effect", "Input"); PROP_PARAM(Float, normalize_flow, 1.0f, "Normalize Flow", "Input parameter for the effect power normalization", "Input"); PROP_PARAM(Float, normalize_payload, 1.0f, "Normalize Payload", "Input parameter for payload normalization", "Input"); PROP_PARAM(Node, particles_system, "Particles System", "ObjectParticles with the effect", "Effect"); PROP_PARAM(Float, spawn_rate_factor, 100.0f, "Spawn Rate Factor", "Multiplier for spawn rate", "Effect"); PROP_PARAM(Toggle, controlled_payload_time, false, "Controlled Payload Time", "Enable: water effect can be stopped automatically", "Payload") PROP_PARAM(Float, max_water_payload, 100.0f, "Max Water Payload", "Full water payload in units (weight or volume)", "Payload") PROP_PARAM(Float, max_flow_speed, 1.0f, "Max Flow Speed", "Maximum water flow speed (units per second)", "Payload"); private: void init(); void update(); void shutdown(); // Declaring a callback on changing the property parameters void parameterChanged(Unigine::PropertyPtr property, int propID); void openWaterDropSystem(bool open); void setWaterDropSystemFlow(float value); void setWaterPayload(float value); private: // Declaring a particle system to be used for the water drop effect Unigine::ObjectParticlesPtr dropWaterEffect; float current_payload = 0.0f; Unigine::Plugins::IG::Manager *ig = nullptr; }; ```
- WaterDropAircraftController.cpp | Close
`WaterDropAircraftController.cpp` ```cpp #include "WaterDropAircraftController.h" #include #include #include // Registering the component in the Component System REGISTER_COMPONENT(WaterDropAircraftController); using namespace Unigine; void WaterDropAircraftController::init() { // Adding a callback on changing the property parameters getProperty()->addCallback(Property::CALLBACK_PARAMETER_CHANGED, MakeCallback(this, &WaterDropAircraftController::parameterChanged)); // Creating a particle system for our effect and setting its parameters dropWaterEffect = checked_ptr_cast(particles_system.get()); if (!dropWaterEffect) Log::error("WaterDropAircraftController::init(): particles_system node is not ObjectParticles!\n"); else dropWaterEffect->setEmitterEnabled(false); ig = Plugins::IG::Manager::get(); } void WaterDropAircraftController::update() { if (! dropWaterEffect || controlled_payload_time == 0 || open == 0 || normalize_flow <= 0 || current_payload <= 0) return; if (!dropWaterEffect->isEmitterEnabled()) dropWaterEffect->setEmitterEnabled(true); // decrease current payload current_payload -= ig->getIFps() * normalize_flow * max_flow_speed; // if payload is empty - disable effect if (current_payload < 0) dropWaterEffect->setEmitterEnabled(false); } void WaterDropAircraftController::shutdown() {} /// Callback function to be executed on changing property parameters void WaterDropAircraftController::parameterChanged(Unigine::PropertyPtr prop, int propID) { if (open.getID() == propID) openWaterDropSystem(prop->getParameterPtr(propID)->getValueToggle()); else if (normalize_flow.getID() == propID) setWaterDropSystemFlow(prop->getParameterPtr(propID)->getValueFloat()); else if (normalize_payload.getID() == propID) setWaterPayload(prop->getParameterPtr(propID)->getValueFloat()); } void WaterDropAircraftController::openWaterDropSystem(bool value) { if (dropWaterEffect) dropWaterEffect->setEmitterEnabled(value); } void WaterDropAircraftController::setWaterDropSystemFlow(float value) { if (dropWaterEffect) dropWaterEffect->setSpawnRate(value * spawn_rate_factor.get()); } void WaterDropAircraftController::setWaterPayload(float value) { current_payload = value * max_water_payload; } ```
3. Build and launch your project. At the initialization stage the Component System will create a property file named `WaterDropAircraftController.prop` for our component. ```xml 0 1 1 0 100 0 100 1 ```
4. Via the UNIGINE Editor, assign the new created property file (`WaterDropAircraftController.prop`) to a node, for which the component was created (*Be-200* in our case). Create a [Particle System](../objects/effects/particles/index.md) in the UNIGINE Editor and set its parameters as required. Add this Particle System to the corresponding field of the property. 
5. Add the description of our component to the desired entity in the [IG configuration file](../ig/config.md) (`ig_config.xml`). Its name and parameters must correspond to the ones described in the component's header file (see *[Step 3](#step_3))*. ```xml WaterDropAircraftController open normalize_flow normalize_payload ```
6. To test the new component, add its description to the configuration file of the host emulator (`/Default/Entities.def`). ```text entity { name = "Be-200"; type = 200; class = fixedwing; .... component { name = "water_drop"; id = 6; def_state = 0; state { name = "close"; value = 0; } state { name = "open"; value = 1; } } } ```
After launching your host application or a *CIGI Host Emulator* you can control your custom component by sending the corresponding packets to the IG.

## Creating a Custom Network Component
Usually, if you need to render a world across several computers synchronized over LAN, you use [Syncker](../code/plugins/syncker/index.md). It sends huge amount of data each frame from *[Master](../code/plugins/syncker/index.md#work)* to all *[Slaves](../code/plugins/syncker/index.md#work)*. Using Syncker allows for precise image synchronization, however, it is also resource-consuming.
**IG Template** provides functionality allowing you to reduce the amount of sent data and, therefore, network load. For example, to rotate an object, you can simply send a rotation velocity from Master to Slaves one time.
To create a network component with custom application logic using the IG Template functionality, you should perform the same steps as if you create a simple component (described above). The main difference is in their implementation:
- An IG network component is inherited from the **Unigine::Plugins::IG::NetworkComponentBase** class.
- Component declaration requires the *NET_COMPONENT_DEFINE* macro.
- Automatic registration of the network component requires the *NET_REGISTER_COMPONENT* macro.
> **Notice:** The network component will behave like a simple component in case there is no network.
When implementing the network component, you define data to be transferred over the network and send it only when necessary: you specify methods that can be invoked over a network (i.e. they can be called on Slaves) via the *NET_DEFINE_SLOT* macro and then call them on Master via the *NET_CALL* macro passing the required data. These methods will be called on Slaves as well.
As an example, let's implement rotation of a node around the Z-axis synchronized over the network using the IG Template functionality. In our case, the *RPM speed* is enough for calculation of the rotation angle. Here are key moments on the implementation of the component:
- On the component initialization (the *init()* function), there is no need to synchronize the node over the network. All changes are sent from Master to Slaves only if the RPM speed value is changed via the *setRPM()* function.
- Updating of the component (the *update()* function) is performed on Master and all Slaves simultaneously. Here you calculate the current rotation angle of the node and update its trasformation. For calculation, the *[IG::Manager::getIFps()](../api/library/plugins/ig/api/class.managerinterface_cpp.md#getIFps_float)* method is used: unlike the *[Game::getIFps()](../api/library/engine/class.game_cpp.md#getIFps_float)* method, it implements more accurate frame time calculation (including spike and freeze periods).
- When the RPM speed value is changed on Master, the *setRPM()* method of the component sends it to all Slaves and the *setRPM()* is called on all Slaves. For the *setRPM()* to be invoked over the network, use the *NET_CALL* macro. It has an effect only on Master, so you can leave this code on Slaves - it won't be invoked. > **Notice:** You cannot send data from Slaves to Master.
Follow the instructions given below to create a network component described above:
1. Create a new project using the **IG Template** as described [here](../ig/index.md#workflow). Make sure that the *[Syncker](../code/plugins/syncker/index.md)* plugin is included in the project (the **Plugins** button in the *Create New Project* window) as the IG Template uses it as the network for transferring commands. 
2. Create the following files implementing the *RotatorIGNetwork* component: > **Notice:** If you have ExtraSlaves, which can be connected after the simulation has started, and you want to synchronize your component (its internal state, any parameters, commands, etc.), you should override the following methods: > > > - **[saveState()](../api/library/plugins/ig/api/class.componentbaseinterface_cpp.md#saveState_const_BlobPtr_ref_void)** that writes the component data to be synchronized to a blob on Master. Try to write the minimum data that describes the full state. > - **[restoreState()](../api/library/plugins/ig/api/class.componentbaseinterface_cpp.md#restoreState_const_BlobPtr_ref_void)** that reads the data from the blob on Slaves. NetworkRotator.h | Close
`NetworkRotator.h` ```cpp #pragma once #include #include // synchronization of rotation using the IG Template functionality class RotatorIGNetwork : public Unigine::Plugins::IG::NetworkComponentBase { public: // declare constructor and destructor for the component and define a property name NET_COMPONENT_DEFINE(RotatorIGNetwork, Unigine::Plugins::IG::NetworkComponentBase); // declare methods to be called at the corresponding stages of the execution sequence COMPONENT_INIT(init); COMPONENT_UPDATE(update); // define that the setRPM() function can be invoked over the network NET_DEFINE_SLOT(setRPM); // specify the RPM speed for a node void setRPM(float speed); // return the current RPM speed of a node float getRPM() const; // saving and restoring the full state of the component is required when you have ExtraSlaves that can be connected with delay // try to write the minimum information on the state // saveState() is called on the Master when the ExtraSlave is connected virtual void saveState(const Unigine::BlobPtr &blob) const override; // restoreState() is called on the Slave connected with delay virtual void restoreState(const Unigine::BlobPtr &blob) override; private: Unigine::Math::Mat4 init_transform; float rpm_speed = 0.0f; double current_angle = 0.0f; void init(); void update(); }; ``` NetworkRotator.cpp | Close
`NetworkRotator.cpp` ```cpp #include "NetworkRotator.h" #include // register a new network component NET_REGISTER_COMPONENT(RotatorIGNetwork); // update the RPM speed void RotatorIGNetwork::setRPM(float speed) { rpm_speed = speed; // if the RPM speed value is changed on the Master, send the RPM speed value from the Master to all Slaves NET_CALL(setRPM, speed); } // get the current RPM speed float RotatorIGNetwork::getRPM() const { return rpm_speed; } // save the current RPM speed and the rotation angle into a blob void RotatorIGNetwork::saveState(const Unigine::BlobPtr &blob) const { blob->write(rpm_speed); blob->write(current_angle); } // restore the RMP speed and the rotation angle from the blob void RotatorIGNetwork::restoreState(const Unigine::BlobPtr &blob) { blob->read(rpm_speed); blob->read(current_angle); } void RotatorIGNetwork::init() { // get the node transformation init_transform = node->getTransform(); } // update() is executed on the Master and all Slaves simultaneously void RotatorIGNetwork::update() { // calculate the current rotation angle of the node current_angle += Unigine::Plugins::IG::Manager::get()->getIFps() * rpm_speed * 360.0; // update node transformation node->setTransform(init_transform * Unigine::Math::rotateZ(current_angle)); } ``` > **Notice:** You can also use the *IG_ONLY_FOR_MASTER* and *IG_ONLY_FOR_SLAVE* macros to define where the logic should be executed.
3. Build and launch your project. At the initialization stage, a property file named `RotatorIGNetwork.prop` is created.
4. In the UNIGINE Editor, assign the created property file to a node, for which the component was created.
5. Save changes and run your application.