Jump to content

Multiple components interaction


photo

Recommended Posts

Hi,

Following the post made in IG section (CIGIClient to CIGIConnector migration), I'm wondering if there is a (suitable) way to setup interaction between components.

Let's consider this ig_config declaration (but could take editor world hierarchy as well).

    <entity id="160" name="h160">
      <path>entities/h160/h160.node</path>
      <component id="34" name="rotor_main">
        <property>RotorComponent</property>
        <node>dynamic/rotor_main</node>
        <parameter name="data1">rpm</parameter>
        <parameter name="data2">propagate</parameter>
      </component>
      <component id="36" name="rotor_tail">
        <property>RotorComponent</property>
        <node>dynamic/rotor_tail</node>
        <parameter name="data1">rpm</parameter>
        <parameter name="data2">propagate</parameter>
      </component>
      [...]
    </entity>

Such declaration works perfectly, you can properly change rpm on each rotor, but using same RotorComponent code.
The question would be here: how can we setup kind of propagation from one rotor to the other one?

Following code from previous post, I make it working with callback on prop changed in RotorComponent and a propagation in RotorControlller on all registred RotorComponents.

// ===================== in RotorComponent =======================

void RotorComponent::init()
{
	getProperty()->addCallback(
		Property::CALLBACK_PARAMETER_CHANGED,
		MakeCallback(this, &RotorComponent::parameter_changed));

	RotorController::get()->registrate(node->getID(), this);
}

void RotorComponent::parameter_changed(PropertyPtr property, int num)
{
	if (rpm.getID() == num)
	{
		auto value = property->getParameterPtr(num)->getValueInt();
		Log::message("parameter_changed node &s rpm %i\n", node->getName(), value);

		//hopefully property changed is trigger really when new value differ from current
		//otherwise will loop long..
		if (propagate.get())
			RotorController::get()->propagateRPM(node->getID(), value);
	}
	//this can be required e.g. with CIGI data declaration order and previous rpm changed can miss propagation
	else if (propagate.getID() == num)
	{
		if (propagate.get())
			RotorController::get()->propagateRPM(node->getID(), rpm);
	}
}

// ===================== in RotorController =======================

void RotorController::propagateRPM(int64_t entity_id, int value)
{
	for (auto kvp : rotors)
		if (kvp.key != entity_id)
			kvp.data->rpm = value;
}

Working yes, but is it component system compliant? OO suitable?
Is there any another better way to set it up?
Maybe simply with a parameter or RotorComponent pointing directly to related other components.. but this requires manual setup (from editor).

Kind regards,
Charles

Link to comment

Do you want to control both engines equally?
It's enough

void setRPM(int64_t entity_id, int value)
{
	IEntity * entity = ig_manager->getEntity(entity_id); 
	Vector<RotorComponent *> rotors;
	ComponentSystem::get()->getComponentsInChildren<RotorComponent>(entity->getNode(), rotors); // take all rotors on entity
	for (auto & it : rotors)
	{
		it->rpm = value;	// set value to each rotor
	}
}

maybe I misunderstood the question? I remember that you want to manage both through CIGI and without it.

Can you describe your task in more detail with helicopter engines? Do you want to control engines together or separately?

Link to comment

Hi,

You're right that it is much easier / already done to use ComponentSystem::get()->getComponentsInChildren<RotorComponent> instead of RotorController::register/unregister and duplicate collection of RotorComponent.

But you do agree that your code as mine may enter in kind of infinite loop, if you don't set the same value but e.g. half of it, since prop changed event will trigger all the time.. up to 0 in fact.
So, a fix / my question is more: is there a way to proper define relation / constraint between component? (and propagate once correct value on targeted components)

In this example with rotor_main and rotor_tail, let's try to force rotor_tail to turn 2 times faster than rotor_main, given the fact you're right rpm update can come from CIGI or not,  and update can be done on rotor_main as on rotor_tail.

Kind regards,
Charles

Link to comment

there is no one right solution for the interaction of components. I can’t know which way is best for you.

for example, how do I see it
 

// many existing engine types
enum class RotorType{
	MAIN,
	TAIL,
	/* .. */
};

// Component for engine - just rotate blades, nothing more. located on the blade nodes, indicate which axis rotates
class RotorEngineComponent : public ComponentBase
{
	public:
		COMPONENT(RotorEngineComponent, ComponentBase);
		COMPONENT_SHUTDOWN(update);

		PROP_NAME("RotorEngineComponent");
		PROP_AUTOSAVE(0);
		PROP_PARAM(Float, rpm, 0.0); 
		PROP_PARAM(Vec3, axis, vec3::UP);
		PROP_PARAM(Switch, rotor_type, 0, "Main,Tail, ..."); // indicates specific type
		
		RotorType getType() const { return RotorType(rotor_type); }

	private:
		void update() // just rotate. nothing more
		{
			if (rpm > 0)
				node->setRotate(quat(axis.get, Game::get()->getTime() * rpm * 0.016666));
		}
}

// blade control component. - a single access point in the model on the root node.
// helicopter can have any number of rotors.
class RotorsComponent : public ComponentBase
{
	public:
		COMPONENT(RotorsComponent, ComponentBase);
		COMPONENT_INIT(init);
		COMPONENT_UPDATE(update);

		PROP_NAME("RotorsComponent");
		PROP_AUTOSAVE(0);
		PROP_PARAM(Switch, rotor_type, 0, "Main,Tail, ...");
		PROP_PARAM(Float, rpm, 0.0f);
		PROP_PARAM(Toggle, propagate, 0);
		
		void setRPM(RotorType type, float rpm, bool propagate = false)
		{
			for (auto & rotor : rotors)
			{
				if (rotor->getType() == type)
				{
					rotor->rpm = rpm;
				}
				else
				{
					if (propagate)
					{
						// propagate logic here - for example the tail rotor is twice as fast as the main rotor

						if (type == RotorType::MAIN && rotor->getType() == RotorType::TAIL)
						{
							rotor->rpm = rpm * 2.0f;
						}

						if (type == RotorType::TAIL && rotor->getType() == RotorType::MAIN)
						{
							rotor->rpm = rpm / 2.0f;
						}

						// some additional propagate logic ... 
					}
				}
			}

		}

	private:
		
		Vector<RotorEngineComponent *> rotors;
		bool need_refresh = false;

		void parameter_changed(PropertyPtr property, int num)
		{
			need_refresh = true;
		}

		void init()
		{
			ComponentSystem::get()->getComponentsInChildren<RotorComponent>(node, rotors);
		}

		void update()
		{
			if (need_refresh)
			{
				setRPM(RotorType(rotor_type.get()), rpm, propagate.get() > 0);
				need_refresh = false;
			}
		}
}



/// without CIGI
auto c = ComponentSystem::get()->getComponentInChildren<RotorsComponent>(entity->getNode());
if (c)
{
	c->setRPM(RotorType::MAIN, 20.0f, true); // set all "MAIN" rotors on 20rpm and all "TAIL" rotors on 40 rpm

	c->setRPM(RotorType::TAIL, 10, false); // set all "TAIL" rotors on 10 rpm. without propagation.
}


/// from CIGI send one packet with 3 data - type, speed, propagate
<entity id="160" name="h160">
     <path>entities/h160/h160.node</path>
     <component id="34" name="rotors">
     <property>RotorsComponent</property>
        // <node>dynamic/rotor_main</node>
     <parameter name="data1">rotor_type</parameter>
     <parameter name="data2">rpm</parameter>
     <parameter name="data3">propagate</parameter>
  </component>
</entity>

 

Link to comment
×
×
  • Create New...