Video Tutorials
Interface
Essentials
Advanced
How To
UnigineEditor
Interface Overview
Assets Workflow
Settings and Preferences
Working With Projects
Adjusting Node Parameters
Setting Up Materials
Setting Up Properties
Lighting
Landscape Tool
Sandworm
Using Editor Tools for Specific Tasks
Extending Editor Functionality
Built-in Node Types
Nodes
Objects
Effects
Decals
Light Sources
Geodetics
World Objects
Sound Objects
Pathfinding Objects
Players
Programming
Fundamentals
Setting Up Development Environment
Usage Examples
UnigineScript
C++
C#
UUSL (Unified UNIGINE Shader Language)
File Formats
Rebuilding the Engine Tools
GUI
Double Precision Coordinates
API
Containers
Common Functionality
Controls-Related Classes
Engine-Related Classes
Filesystem Functionality
GUI-Related Classes
Math Functionality
Node-Related Classes
Objects-Related Classes
Networking Functionality
Pathfinding-Related Classes
Physics-Related Classes
Plugins-Related Classes
IG Plugin
CIGIConnector Plugin
Rendering-Related Classes
Content Creation
Content Optimization
Materials
Art Samples
Tutorials

Creating Custom Components

Warning
The functionality described in this article is not available in the Community SDK edition.
You should upgrade to Sim SDK edition to use it.

You can extend the initial set of components that can be added to entities by adding custom ones. As an example let us consider adding a water drop component for the Be-200 aircraft available in the IG Template.

Workflow#

Follow the instructions given below to create you own custom component.

  1. Create a new project using the IG Template and Component System as described here.

    Create a Project using the IG Template and Component System

  2. Add the following initialization code for the Component System to the AppSystemLogic::init() method in the AppSystemLogic.cpp file:

    Source code (C++)
    virtual int AppSystemLogic::init()
    {
    	// initialize ComponentSystem and register all components
    	Unigine::ComponentSystem::get()->initialize();
    
    	/*...*/
    	
    	return 1;
    }
  3. Create (or add) the following files describing your new component to the C++ project:

    Notice

    The component should be inherited from both the ComponentBase and the ComponentBaseInterface classes.

    If you want to synchronize your component (its internal state, any parameters, commands, etc.), you can override the following methods:

    • saveState() — for code writing component's data to be synchronized to a blob on the Master.
    • restoreState() — for code reading the data from the blob on Slaves.
    • WaterDropAircraftController.h

      Source code (C++)
      #ifndef _WATER_DROP_AIRCRAFT_CONTROLLER_INCLUDED_
      #define _WATER_DROP_AIRCRAFT_CONTROLLER_INCLUDED_
      
      #include <UnigineGame.h>
      #include <plugins/UnigineIG.h>
      #include <UnigineComponentSystem.h>
      
      class WaterDropAircraftController final : public Unigine::ComponentBase, public Unigine::Plugins::IG::ComponentBaseInterface
      {
      public:
      	COMPONENT(WaterDropAircraftController, Unigine::ComponentBase);
      	COMPONENT_INIT(init);
      	COMPONENT_UPDATE(update);
      	COMPONENT_SHUTDOWN(shutdown);
      	
      	// Specifying the name of the property file and parameters for our component
      	PROP_NAME("WaterDropAircraftController");
      	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;
      
      	void saveState(const Unigine::BlobPtr &blob) override;
      	void restoreState(const Unigine::BlobPtr &blob) override;
      };
      
      #endif // _WATER_DROP_AIRCRAFT_CONTROLLER_INCLUDED_
    • WaterDropAircraftController.cpp

      Source code (C++)
      #include "WaterDropAircraftController.h"
      #include <UnigineProperties.h>
      #include <UnigineObjects.h>
      #include <UnigineEditor.h>
      // Registering the component in the Component System
      REGISTER_COMPONENT(WaterDropAircraftController);
      
      void WaterDropAircraftController::init()
      {
      	using namespace Unigine;
      	// 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<ObjectParticles>(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;
      }
      
      void WaterDropAircraftController::saveState(const BlobPtr &blob)
      {
      	// master logic 
      	// when new slave is connected
      	blob->writeBool(open > 0);
      	blob->writeFloat(normalize_flow);
      	blob->writeFloat(current_payload);
      }
      
      void WaterDropAircraftController::restoreState(const BlobPtr &blob)
      {
      	// slave logic
      	// new slave received these parameters
      	open = blob->readBool();
      	normalize_flow = blob->readFloat();
      	current_payload = blob->readFloat();
      }

    In the header file (WaterDropAircraftController.h) define the name for the property file and describe parameters of the component:

    Source code (C++)
    // WaterDropAircraftController.h
    	// ...
        // Specifying the name of the property file and parameters for our component
        PROP_NAME("WaterDropAircraftController");
        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");
    	
    	// ...

    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.

    Source code (C++)
    // WaterDropAircraftController.cpp)
    	// ...
        
        void WaterDropAircraftController::init()
        {
        	using namespace Unigine;
    		
    		// Adding a callback on changing property parameters
        	getProperty()->addCallback(Property::CALLBACK_PARAMETER_CHANGED,
        		MakeCallback(this, &WaterDropAircraftController::parameterChanged));
                // ...
        }
  4. Build and launch your project. At the initialization stage the Component System will create a property file named WaterDropAircraftController.prop for our component.

    Source code (XML)
    <?xml version="1.0" encoding="utf-8"?>
    <property version="2.7.3.0" name="WaterDropAircraftController" manual="1" parent_name="node_base">
    	<parameter name="open" type="toggle" title="Open" tooltip="Input parameter for enabling/disabling the effect" group="Input">0</parameter>
    	<parameter name="normalize_flow" type="float" title="Normalize Flow" tooltip="Input parameter for the effect power normalization" group="Input">1</parameter>
    	<parameter name="normalize_payload" type="float" title="Normalize Payload" tooltip="Input parameter for payload normalization" group="Input">1</parameter>
    	<parameter name="particles_system" type="node" title="Particles System" tooltip="ObjectParticles with the effect" group="Effect">0</parameter>
    	<parameter name="spawn_rate_factor" type="float" title="Spawn Rate Factor" tooltip="Multiplier for spawn rate" group="Effect">100</parameter>
    	<parameter name="controlled_payload_time" type="toggle" title="Controlled Payload Time" tooltip="Enable: water effect can be stopped automatically" group="Payload">0</parameter>
    	<parameter name="max_water_payload" type="float" title="Max Water Payload" tooltip="Full water payload in units (weight or volume)" group="Payload">100</parameter>
    	<parameter name="max_flow_speed" type="float" title="Max Flow Speed" tooltip="Maximum water flow speed (units per second)" group="Payload">1</parameter>
    </property>
  5. Via UnigineEditor 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 in UnigineEditor and set its parameters as required. Add this Particle System to the corresponding field of the property.

  6. Add the description of our component to the desired entity in the IG configuration file (ig_config.xml). Its name and parameters must correspond to the ones described in the component's header file (see Step 3).

    Source code (XML)
    <!-- ..... -->
    	<entity id="200" name="be-200">
    		<!-- ..... -->
    		<component id="6" name="water_drop">
                    <property>WaterDropAircraftController</property>
                    <parameter name="state">open</parameter>
                    <parameter name="data1">normalize_flow</parameter>
                    <parameter name="data2">normalize_payload</parameter>
    				<!-- ..... -->
    		</component>
    		<!-- ..... -->
    	</entity>
    <!-- ..... -->
  7. To test the new component add its description to the configuration file of the host emulator (<path_to_host>/Default/Entities.def).

    Notice
    Component ID must be the same as the one specified in the ig_config.xml.
    Source code
    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.

Create a Project using the IG Template and Component System

Last update: 2021-04-29
Build: ()