ScriptObject Inheritance and Instancing
by Ricardo Arango · in Technical Issues · 10/12/2008 (5:28 am) · 2 replies
Hi,
I am having a bit of trouble understanding torquescript.
I need to have a class (ScriptObject??), let's call it 'system', that has many 'components', for example, a people component that creates, destroys, animates all the people, 'animals' for the animals, and so on. And inside each of those components there are instances of 'objects'. For example por the people component, there would be person1, person2, etc..
so the hierarchy would be like this:
Now if I were to do this in C++, it would be something (not strictly) like this:
Now I need is the equivalent of that in TorqueScript.
So, what I have done to create the system is this:
Now the problem is, if I check $system.People.nPeople, it returns "". That means that variable doesn't exist. So how can I correctly define and create an instance of a class?
Thanks.
I am having a bit of trouble understanding torquescript.
I need to have a class (ScriptObject??), let's call it 'system', that has many 'components', for example, a people component that creates, destroys, animates all the people, 'animals' for the animals, and so on. And inside each of those components there are instances of 'objects'. For example por the people component, there would be person1, person2, etc..
so the hierarchy would be like this:
System |_____________..... | | People Animals | | | | Person1 Animal1 Person2 Animal2 Person3 .... ...
Now if I were to do this in C++, it would be something (not strictly) like this:
class cPeople{
public:
...
int nPeople;
SimGroup groupPeople;
}
class cAnimals{
public:
...
int nAnimals;
SimGroup groupAnimals;
}
class GameSystem{
public:
...
cPeople People ;
cAnimals Animals;
}
// And to use any of that
system = new GameSystem();
int num_people = system.People.nPeople;Now I need is the equivalent of that in TorqueScript.
So, what I have done to create the system is this:
$system = new ScriptObject(GameSystem){
People = new ScriptObject(){
class = "classPeople";
};
Animals = new ScriptObject(){
class = "classAnimals";
};
};
new ScriptObject(){
class = "classPeople";
nPeople = 0;
groupPeople = new SimGroup();
};
new ScriptObject(){
class = "classAnimals";
nAnimals = 0;
groupAnimals = new SimGroup();
};Now the problem is, if I check $system.People.nPeople, it returns "". That means that variable doesn't exist. So how can I correctly define and create an instance of a class?
Thanks.
About the author
#2
Thanks for the long reply. I understand much better. And I finally got it to work using SimGroups, and initializang outside, and then just adding the objects to the 'system' variable.
10/14/2008 (10:17 am)
Hi,Thanks for the long reply. I understand much better. And I finally got it to work using SimGroups, and initializang outside, and then just adding the objects to the 'system' variable.
$system = new ScriptObject(GameSystem){
People = "";
Animals = "";
};
People = new ScriptObject(){
class = "classPeople";
nPeople = 0;
groupPeople = new SimGroup();
};
Animals = new ScriptObject(){
class = "classAnimals";
nAnimals = 0;
groupAnimals = new SimGroup();
};
$system.People = People;
$system.Animals = Animals;
Torque 3D Owner Stephen Zepp
---TorqueScript is not a fully functioning Object Oriented Programming language. By itself, it's not designed to do what you are requesting.
---to accomplish what you want (fully functional OOP within TorqueScript accessable objects)
1) Implement all of your objects in c++, correctly derived from the standard Torque base hierarchy. Most probably starting with GameBase/ShapeBase, and working from there down to your child classes
2) Expose these classes' helpers as either ConsoleMethods or persistent fields (::initPersistFields), and expose their use methods as ConsoleMethods.
3) Define and expose any necessary callbacks to TorqueScript via Con::executef(...)
4) Instantiate and utilize your objects via TorqueScript.
Long Answer #1 (Theory):
Regarding the first point a little more in depth:
--TorqueScript was never designed to be a fully implemented object oriented language--it was designed to allow two major features:
----rapid game play implementation and modification during game development without requiring major c++ changes/compilation
----allowing end user to "mod" games by exposing key game play defining values (datablocks), and game logic (TorqueScript) in a language and environment much easier to learn quickly than formal C++
The expected best practices when a game desires TorqueScript managed objects with large utilization of OOP principles is what I described in the short answer #2: design and implement the classes themselves in C++, then access them via TorqueScript with regards to the use of the two features described above.
Long Answer #2 (Your specific problem set)
The core misunderstanding here in your logic described above is two fold:
--understanding how the . (dot) operator works in TorqueScript.
The TorqueScript dot operator when used as such (not used as a decimal point) only accomplishes one thing: defining an object reference on the left, and a method or field on the right. The token to the left of a dot operator (in this usage) must evaluate to an object, and the token to the right must evaluate to a field or method. Dot operators may be chained, but the evaluation described above must hold.
--persistent fields must be defined at compile time, and dynamic fields must be populated at run time.
Now, using your code, let's apply the evaluation rule for the dot operator to your extended token:
$system.People.nPeople :: what does it evaluate to?
$system.People :
--$system is a token to the left of a dot operator, must be an object:
----$system is an object: "$system = new ScriptObject"
--the token "People" does not end in a (...) syntax, therefore it must be a field.
----People appears to be a field, specifically containing an object reference due to: "People = new ScriptObject()", however see not about inconsistencies in the TorqueScript compiler at the end
$system.People should correctly evaluate to an dynamic field, and specifically contain a reference to an object.
--token to the left previously evaluated, contains a reference to an object.
--the token nPeople does not end in a (...) syntax, therefore it must be a field.
But now the question is--what field? There is no automatic linking to your additional ScriptObject defined later, so this specific evaluation has no previously defined field "nPeople", and no way to reference any other object that might contain that field, so it is evaluated as a null field, and the parser will create a new field, and initialize it to ""
Now--there is a technique to accomplish what I think you are trying to do, but to implement it you need to think differently regarding how objects work, especially SimSets and SimGroups.
The first step were I to implement the above is to get rid of the concept of nested links to objects (hierarchies, which don't exist as such with objects created completely from TorqueScript), and instead use other OOP concepts, combined with name references to objects. Here's how I would set up my containers:
When I create a new instance of an object that would be a "People" (sic), I would do the following:
%myObject = new ScriptObject() { class = "classPeople"; }; People.add(%myObject);And if I wanted to get the total number of "People" in my set, I would use:
Note at end
One more thing to point out: The TorqueScript parser allows objects to be created within the creation of other objects in a very specific and mandatory syntax. From memory, the technique you are using should work, but there are subtle issues regarding how and when you spawn off a new instance of an object within the spawning of another object that can cause issues. It's normally a better, or at least more consistent technique to spawn child objects first, and then manually link them to the "parent" object via dynamic field assignments. For example:
%myChildOne = new ScriptObject(); %myChildTwo = new ScriptObject(); $myParentObject = new ScriptObject(ParentObject) { container = new SimSet(); }; $myParentObject.container.add(%myChildOne); $myParentObject.container.add(%myChildTwo);The above example is from memory, but should work more consistently. Even so, it can be fraught with incorrect usage, since $myParent.container is a dynamic field that happens to hold an object reference--but could be (accidentally) changed at any time during program execution...and therefore cause errors.