Way for a component to access variables from another component?
by Brian Doig · in Torque X 2D · 01/06/2007 (11:50 am) · 18 replies
Is there a good way for one component to access variables from another component? What I was thinking was that I would have some components that would rely on the presence of another component. I.E. I don't allways want specific behavior to be built into a component but would like to be able to add functionality on top of a base component.
As an example, I would take the DestructibleComponent from the SpaceShooter project and add a second component that would allow a DestructibleComponent to be repaired slowly over time. Currently, I added the functionality directly to DestructibleComponent but it would be nice if I could move it out into a second component I could add to objects that have a DestructibleComponent. This would simplify the code for DestructibleComponent since it wouldn't need to have all the other code related to being repaired in it.
What would be the best (clean) way to set up such a mechanism?
As an example, I would take the DestructibleComponent from the SpaceShooter project and add a second component that would allow a DestructibleComponent to be repaired slowly over time. Currently, I added the functionality directly to DestructibleComponent but it would be nice if I could move it out into a second component I could add to objects that have a DestructibleComponent. This would simplify the code for DestructibleComponent since it wouldn't need to have all the other code related to being repaired in it.
What would be the best (clean) way to set up such a mechanism?
#3
Two other suggestions.
1. Make your custom component a component of the DestructibleComponent. Components can themselves have components. It might make sense from a high level to do this but the sub-component's Owner is still the sprite, not the parent component.
2. Make your component a child of the DestructibleComponent through inheritance. Then you gain the advantage of keeping the DestructibleComponent code encapsulated as well as being sure your component is only added to object with a DestructibleComponent because your component is a DestructibleComponent.
01/07/2007 (1:31 am)
Yes, you can use that code to find out if the component is on the object. I would modify it a little to use Owner though.CompType myComp = Owner.Components.FindComponent<CompType>();
if (myComp != null)
{
// my code here using the data from myComp
}Two other suggestions.
1. Make your custom component a component of the DestructibleComponent. Components can themselves have components. It might make sense from a high level to do this but the sub-component's Owner is still the sprite, not the parent component.
2. Make your component a child of the DestructibleComponent through inheritance. Then you gain the advantage of keeping the DestructibleComponent code encapsulated as well as being sure your component is only added to object with a DestructibleComponent because your component is a DestructibleComponent.
#4
I think that option 2 is probably the technically supperior one, but I am just modifying one of the sample projects at the moment to learn about TorqueX and TGBX. Since all the components are allready added and values set in TGBX, I will probably go with my original idea for this test project and then migrate to inheritance once I start my real project.
01/07/2007 (8:55 am)
Thanks! I actually like the idea of making the component a subcomponent, but there doesn't seem to be support for this in the TGBX editor. Since I'm using this at the moment, I will have to go with either my original idea or with your option 2. I think that option 2 is probably the technically supperior one, but I am just modifying one of the sample projects at the moment to learn about TorqueX and TGBX. Since all the components are allready added and values set in TGBX, I will probably go with my original idea for this test project and then migrate to inheritance once I start my real project.
#5
01/07/2007 (11:08 am)
You are correct. There is currently no support in TGBX for subcomponents :(
#6
The way it would work is that your destructible component would expose an interface to it's damage variable (damage would be held in a ValueInterface rather than a straight float and would be registered with Owner in the RegisterInterfaces callback). Then the repair component would look up the damage variable in it's init components call (Owner.FindInterface).
The advantage of doing things this way is that destructible doesn't need to know anything about the repair component and in fact other components can also use the damage variable. E.g., you could have a special effect component which controlled the amount of smoke generated based on the damage variable. The fx component wouldn't even need to know what damage meant, just that it was a float with a given range (so same fx component could be re-used for other purposes).
But for what you are doing and to get it up and going quickly, the other options are preferable. TorqueInterfaces are really for when you want to write really general components.
01/07/2007 (11:44 am)
Either looking up the component as you first suggested or using inheritence are good ways to go here. Another option that is a little more involved is to use a TorqueInterface. For simplicity I wouldn't recommend it here, but probably worth keeping the option in mind. The way it would work is that your destructible component would expose an interface to it's damage variable (damage would be held in a ValueInterface
The advantage of doing things this way is that destructible doesn't need to know anything about the repair component and in fact other components can also use the damage variable. E.g., you could have a special effect component which controlled the amount of smoke generated based on the damage variable. The fx component wouldn't even need to know what damage meant, just that it was a float with a given range (so same fx component could be re-used for other purposes).
But for what you are doing and to get it up and going quickly, the other options are preferable. TorqueInterfaces are really for when you want to write really general components.
#7
01/07/2007 (3:18 pm)
Thanks, the torque interface was really the answer I was looking for. But I will stick with the simple method first and refactor to a torque interface later. I was looking for the 'right' way to do it when designing a component. Do any of the sample projects have an example of this being done?
#8
? Looking in the help file, it says that ValueInterface is an abstract class. Has this changed and the help is just not updated? Or is there something else you have to do to expose this way? If so could you provide an example of the code that would need to be added to each component?
01/07/2007 (3:29 pm)
Can you create a ValueInterface
#9
), which would be the preferred way of generating forces if you didn't want to use the force component (and is in fact how the force component communicates with the physics component).
I'll try to walk you through what must be done. First, you need to expose an interface for your damage variable. The simplest way to do this is create a value interface which will wrap the variable. So now the actual variable will be stored in this interface. So you'd have the following member variable in your destructible component:
And when you wanted to change that value internally you'd do the following:
In other words, the interface becomes not just an interface but also your actual variable.
In order to make this interface available to other components you need to register it. Do this like so:
Note that the "float" refers to the interface type. The type name used is totally by convention, but generally follows the c# name at least for predefined c# types.
Doing it this way assumes that there is a single interface that is predefined. In more complicated situations you might want to add these interfaces on the fly, or at least have programatically deteremined number of interfaces. E.g., the force component exposes the strength of each force as a float interface. This can't be hard coded since you don't know at compile time what forces will be added. To add interfaces on the fly you'd instead do this:
Since you don't supply an interface you'll get a _GetInterfaces callback if someone requests the named interface. Since you didn't supply a name, you'll always get the callback. So you've basically requested that whenever someone asks for a float interface on this object to ask this component.
Then you'd define _GetInterfaces like so:
The pattern match variables let the caller do wildcard matches. E.g. match all "vector*" interfaces or "*" for name if you want to get all interfaces of a given type. (Note: someone want to write an inspector tool?).
So now, assuming you have used the first method of simply cacheing your "damage" interface, you can look it up like so:
Note: the reason we use ValueInterface when we look it up but ValueInPlace when we define the interface is that ValueInterface is the base class. The in place version actually holds the variable in the interface. But you could also derive from ValueInterface to do different tasks when the Value property is get/set (e.g., you could create an interface which literally initiated a destruction sequence when the value goes to zero).
Note2: we could expose a lot more to interfaces. E.g., size, position, velocity, etc. We haven't done this because it would tend to bloat the objects. But one can write handy components which expose whatever you want. E.g., by deriving an interface from ValueInterface, you can write something which will expose any publicly available variable you want. So if you wanted physics variables exposed to interfaces you could write a PhysicsExposerComponent (or something like that) which exposes various physics variables. (When I say derive from ValueInterface I mean that you'll need to override the value property to get/set the variable you want). This becomes an admittedly tedious process since you need a new interface type for each variable.
01/07/2007 (3:53 pm)
No, I don't think any of the samples do this. Internally they are used in a couple places. E.g., the visibility parameter is exposed as an interface (named "alpha") and the physics component looks for a force generator interface (literally ValueInterfaceI'll try to walk you through what must be done. First, you need to expose an interface for your damage variable. The simplest way to do this is create a value interface which will wrap the variable. So now the actual variable will be stored in this interface. So you'd have the following member variable in your destructible component:
ValueInPlaceInterface<float> _damage = new ValueInPlaceInterface<float>();
And when you wanted to change that value internally you'd do the following:
_damage.Value = 0.5f; // or whatever value
In other words, the interface becomes not just an interface but also your actual variable.
In order to make this interface available to other components you need to register it. Do this like so:
protected internal override void _RegisterInterfaces(TorqueObject owner)
{
Owner.RegisterCachedInterface("float", "damage", this, _damage);
base._RegisterInterfaces(owner);
}Note that the "float" refers to the interface type. The type name used is totally by convention, but generally follows the c# name at least for predefined c# types.
Doing it this way assumes that there is a single interface that is predefined. In more complicated situations you might want to add these interfaces on the fly, or at least have programatically deteremined number of interfaces. E.g., the force component exposes the strength of each force as a float interface. This can't be hard coded since you don't know at compile time what forces will be added. To add interfaces on the fly you'd instead do this:
protected internal override void _RegisterInterfaces(TorqueObject owner)
{
Owner.RegisterCachedInterface("float", null, this, null);
base._RegisterInterfaces(owner);
}Since you don't supply an interface you'll get a _GetInterfaces callback if someone requests the named interface. Since you didn't supply a name, you'll always get the callback. So you've basically requested that whenever someone asks for a float interface on this object to ask this component.
Then you'd define _GetInterfaces like so:
protected internal override void _GetInterfaces(PatternMatch typeMatch, PatternMatch nameMatch, List<TorqueInterface> list)
{
if (typeMatch.Match("float"))
{
// loop through potential interfaces, exporting any that match name
if (nameMatch.Math(testThisName))
list.Add(thisInterface);
}
} The pattern match variables let the caller do wildcard matches. E.g. match all "vector*" interfaces or "*" for name if you want to get all interfaces of a given type. (Note: someone want to write an inspector tool?).
So now, assuming you have used the first method of simply cacheing your "damage" interface, you can look it up like so:
ValueInterface<float> iface = SceneObject.Components.GetInterface<ValueInterface<float>>("float", "damage");Note: the reason we use ValueInterface when we look it up but ValueInPlace when we define the interface is that ValueInterface is the base class. The in place version actually holds the variable in the interface. But you could also derive from ValueInterface to do different tasks when the Value property is get/set (e.g., you could create an interface which literally initiated a destruction sequence when the value goes to zero).
Note2: we could expose a lot more to interfaces. E.g., size, position, velocity, etc. We haven't done this because it would tend to bloat the objects. But one can write handy components which expose whatever you want. E.g., by deriving an interface from ValueInterface, you can write something which will expose any publicly available variable you want. So if you wanted physics variables exposed to interfaces you could write a PhysicsExposerComponent (or something like that) which exposes various physics variables. (When I say derive from ValueInterface I mean that you'll need to override the value property to get/set the variable you want). This becomes an admittedly tedious process since you need a new interface type for each variable.
#10
Regarding RegisterCachedInterface I noticed that use called the first parameter "float" but in the TorqueX Overview.html file they use this line
01/07/2007 (4:31 pm)
Thanks for the detailed explanation! I figured you had something nice like this worked into your API but I couldn't quite dig it out. I'll give it a try and see how it works. Quick question, suppose I had a property I wanted to expose without chaning all the code. Is there a way to expose an existing value without chaning the code in the component to _variable.Value everywhere it is referenced? Or could I just have the compiler refactor _variable to _variable.Value?Regarding RegisterCachedInterface I noticed that use called the first parameter "float" but in the TorqueX Overview.html file they use this line
Owner.RegisterCachedInterface("force", String.Empty, this, _forceInterface);What is the difference? Could you say group various exposed values by use with the first value? That's what it seems to be doing in the sample document.
#11
In C++, the way this would be done is the in place version would have a pointer to the variable in question. But in managed code we can't have pointers (C# has pointers but it would make the code "unsafe" and unusable on the 360). So instead we just wrap the variable inside the interface. Not as convenient, but you do what you can.
The difference between the "force" example and the "float" example is that they are exposing two different types of interfaces. The float example exposes a ValueInterface whereas the force example is exposing a force generator. In both cases the type name is by convention (i.e., as long as the code that registers the interface and the code that looks it up agrees, you can use whatever type name you want).
01/07/2007 (5:08 pm)
I think you'll probably just have to change the code line by line. Shouldn't be too hard. Just change the type of the variable and the compiler will tell you what needs to change.In C++, the way this would be done is the in place version would have a pointer to the variable in question. But in managed code we can't have pointers (C# has pointers but it would make the code "unsafe" and unusable on the 360). So instead we just wrap the variable inside the interface. Not as convenient, but you do what you can.
The difference between the "force" example and the "float" example is that they are exposing two different types of interfaces. The float example exposes a ValueInterface
#12
01/08/2007 (4:28 pm)
Ok, next question. How do I declare and use an interface that is published? they give an example of the force, but they don't show how it's declared or how member functions would be invoked.
#13
01/08/2007 (4:36 pm)
@Brian - might be misunderstanding you, but see the "GetInterfaces" call in my examples above. That will lookup an interface on an object. To then use it you would just get or set the value (iface.Value). If it were an interface that did more than just hold a value then you would just call methods on it like any other object.
#14
01/08/2007 (5:21 pm)
So you would say do something like this to get access to the force interface?TorqueInterface inf = owner.components.GetInterface("force", String.Empty);
inf.PreUpdateForces(...);
#15
Given the following interface
How would I have one component declare and register this function as being available?
How would I have the other component find and then invoke this? I tried the following to declare it as follows
Then I tried to place the following code to get the interface
01/08/2007 (6:00 pm)
Ok, I'm having problems figureing out how to do what I wanted to do and I've run into a few problems.Given the following interface
interface IRepairComponent
{
void Hit();
}How would I have one component declare and register this function as being available?
How would I have the other component find and then invoke this? I tried the following to declare it as follows
TorqueInterfaceWrap<IRepairComponent> _iRepairComponent = new TorqueInterfaceWrap<IRepairComponent>();
protected override void _RegisterInterfaces(TorqueObject owner)
{
owner.RegisterCachedInterface("IRepairComponent", String.Empty, this, _iRepairComponent);
base._RegisterInterfaces(owner);
}Then I tried to place the following code to get the interface
TorqueInterface inf = owner.Components.GetInterface<IRepairComponent>("IRepairComponent", String.Empty);But this failed to compile with the following errorError 1 The type 'SpaceShooter.IRepairComponent' must be convertible to 'GarageGames.Torque.Core.TorqueInterface' in order to use it as parameter 'T' in the generic type or method 'GarageGames.Torque.Core.TorqueComponentContainer.GetInterface<T>(string, string)'
#16
Should be more like this:
Also, when you first create the wrapped interface, you need to be sure that your component actually implements the interface.
01/08/2007 (9:02 pm)
This:TorqueInterface inf = owner.Components.GetInterface<IRepairComponent>("IRepairComponent", String.Empty);Should be more like this:
TorqueInterfaceWrap<IRepairComponent> inf = owner.Components.GetInterface<TorqueInterfaceWrap<IRepairComponent>>("IRepairComponent", String.Empty);Also, when you first create the wrapped interface, you need to be sure that your component actually implements the interface.
#17
First, I have a gCurrentPlayer object which is an extern so I can easily use it from anywhere in the engine, as almost all the game code hinges off of it. So at the top of another component that I want to access function from the player component, I do:
And of course, gCurrentPlayer is assigned in the playerComponent.cpp file.
Now is where I get a little stuck. I can see 2 ways to do this, and I'm having problems with both. The first is of course just to call functions on the gCurrentPlayer object...
As one might expect, this complains that doMyFunkyFunctions() is not a method in t2dSceneObject. Ok, that makes sense. So I try and grab the behavior from the gCurrentPlayer object so I can do things with that..
And this gives me the error of "error C2275: 'playerComponent' : illegal use of this type as an expression"
So, how does one call methods on a component controlled object from inside another component on the C++ side?
11/29/2009 (2:24 am)
I'm trying to do this on the C++ side and I'm getting some funky errors. Here is how I'm setting things up...First, I have a gCurrentPlayer object which is an extern so I can easily use it from anywhere in the engine, as almost all the game code hinges off of it. So at the top of another component that I want to access function from the player component, I do:
extern t2dSceneObject* gCurrentPlayer;
And of course, gCurrentPlayer is assigned in the playerComponent.cpp file.
Now is where I get a little stuck. I can see 2 ways to do this, and I'm having problems with both. The first is of course just to call functions on the gCurrentPlayer object...
gCurrentPlayer->doMyFunkyFunctions();
As one might expect, this complains that doMyFunkyFunctions() is not a method in t2dSceneObject. Ok, that makes sense. So I try and grab the behavior from the gCurrentPlayer object so I can do things with that..
playerComponent* activePlayer = gCurrentPlayer->getComponent<playerComponent>();
And this gives me the error of "error C2275: 'playerComponent' : illegal use of this type as an expression"
So, how does one call methods on a component controlled object from inside another component on the C++ side?
#18
11/29/2009 (1:07 pm)
Torque X doesn't have a C++ side

Torque Owner Brian Doig
CompType myComp = T2DStaticSprite.Components.FindComponent
if (myComp != null)
{
my code here using the data from myComp
}