Inheritance / polymorphism?
by Vern Jensen · in Torque Game Builder · 09/14/2006 (9:37 pm) · 10 replies
Does TorqueScript support any kind of inheritance and polymorphism?
That is, could I make a base "BadGuy" class that had some methods, such as "function BadGuy::die(%this)" and then have other classes "inherit" from BadGuy, and *override* the die() method? That is, when Squirrel dies, it dies in its own unique way, but then it can still call the parent die() method, to do stuff common to all bad guy deaths?
Is any of that possible in TorqueScript?
That is, could I make a base "BadGuy" class that had some methods, such as "function BadGuy::die(%this)" and then have other classes "inherit" from BadGuy, and *override* the die() method? That is, when Squirrel dies, it dies in its own unique way, but then it can still call the parent die() method, to do stuff common to all bad guy deaths?
Is any of that possible in TorqueScript?
#2
An object will look within it's own namespaces, and if it doesn't find a function it is looking for, will look in the namespace indicated by it's "class" persistent field, and then it's "superclass" persistent field.
Note that these values are initialized at object instantiation, and cannot be changed dynamically on an existing object.
FYI, TorqueScript will also look in the entire class hierarchy (c++) of your object's class for appropriate functions as well, both methods provided by the class implementation, as well as scripted versions.
Also note that while correct, Paul's explanation isn't the only way to do things. A more detailed explanation might be:
superclass = Enemy
class1 = WalkingEnemy
class2 = FlyingEnemy
class3 = StaticEnemy
Object1: Squirrel
Object2: Cat
Object3: Butterfly
Object4: VenusFlyTrap
Assuming Squirrel, Cat, Butterfly and VenusFlyTrap were all instantiated as named objects with the appropriate class and superclasses, you could have the following:
A Squirrel collsion would hit Squirrel::onCollision() and stop (or could chain up if you did Paul's technique if you so desire, A Cat collision would hit WalkingEnemy::onCollision(), a Butterfly collision would hit FlyingEnemy::onCollision(), and a venus fly trap collision would hit Enemy::onCollision().
Basically, treat all methods as "virtual" (in theory, not truly practice), and you'll have a good feel of the walk of the method namespaces.
09/15/2006 (1:31 am)
@Vern: As Paul describes, TorqueScript provides limited inheritence similar to what you describe, with the caveat that it's always namespace based.An object will look within it's own namespaces, and if it doesn't find a function it is looking for, will look in the namespace indicated by it's "class" persistent field, and then it's "superclass" persistent field.
Note that these values are initialized at object instantiation, and cannot be changed dynamically on an existing object.
FYI, TorqueScript will also look in the entire class hierarchy (c++) of your object's class for appropriate functions as well, both methods provided by the class implementation, as well as scripted versions.
Also note that while correct, Paul's explanation isn't the only way to do things. A more detailed explanation might be:
superclass = Enemy
class1 = WalkingEnemy
class2 = FlyingEnemy
class3 = StaticEnemy
Object1: Squirrel
Object2: Cat
Object3: Butterfly
Object4: VenusFlyTrap
Assuming Squirrel, Cat, Butterfly and VenusFlyTrap were all instantiated as named objects with the appropriate class and superclasses, you could have the following:
Enemy::onCollision()
{
// basic onCollision stuff
}
WalkingEnemy::onCollision()
{
// collision handler for ground based movement
}
FlyingEnemy::onCollision()
{
// collision handler for flying
}
Squirrel::onCollision()
{
// squirrel only collision handler
}A Squirrel collsion would hit Squirrel::onCollision() and stop (or could chain up if you did Paul's technique if you so desire, A Cat collision would hit WalkingEnemy::onCollision(), a Butterfly collision would hit FlyingEnemy::onCollision(), and a venus fly trap collision would hit Enemy::onCollision().
Basically, treat all methods as "virtual" (in theory, not truly practice), and you'll have a good feel of the walk of the method namespaces.
#3
The actual class/superclass varibles refer to namespaces (namespaced function lists) thus it can't look in the superclass of your superclass to provide infinate inheratence?
I hope I'm wrong, if not then I'm kicking myself I didn't get the Pro version of TGB. I only got the non-pro version as the page said TS was like C++ but without true inheratence it's more like plain old C.
Was hoping to port over my AI code from java but... :(
Is there any way to upgrade to the pro version for less? I only purchased TGB non-pro yesterday.
09/23/2006 (11:54 am)
Let me get this straight. We only get 2 levels of semi-inheratence. class or superclass...The actual class/superclass varibles refer to namespaces (namespaced function lists) thus it can't look in the superclass of your superclass to provide infinate inheratence?
I hope I'm wrong, if not then I'm kicking myself I didn't get the Pro version of TGB. I only got the non-pro version as the page said TS was like C++ but without true inheratence it's more like plain old C.
Was hoping to port over my AI code from java but... :(
Is there any way to upgrade to the pro version for less? I only purchased TGB non-pro yesterday.
#4
name -> class -> superclass -> (engine type hierarchy)
I approach my game architecture in a very OO way and have not yet been limited by this. TS has no inheritance, only a namespace trick that allows you to use the benefits of inheritance. When you specify a name, class, and superclass on an object you don't actually inherit anything, you just add those names to the namespace call chain.
As far as polymorphism goes, this is a concept that is not really relevant in a "typeless" scripting language like TS. In essence TS can't be considered anything other than 100% polymorphic because at all times any type of data can be treated like any other type of data. The responsibility to use these in correct polymorphic ways falls on you, the developer. The only thing missing is strong type checking that prevents you from shooting yourself in the foot.
09/23/2006 (10:41 pm)
In spite of the fact they used the words "class" and "superclass" in TS there are no such thing as classes in TS. Your objects will always trace up the call chain in this order:name -> class -> superclass -> (engine type hierarchy)
I approach my game architecture in a very OO way and have not yet been limited by this. TS has no inheritance, only a namespace trick that allows you to use the benefits of inheritance. When you specify a name, class, and superclass on an object you don't actually inherit anything, you just add those names to the namespace call chain.
As far as polymorphism goes, this is a concept that is not really relevant in a "typeless" scripting language like TS. In essence TS can't be considered anything other than 100% polymorphic because at all times any type of data can be treated like any other type of data. The responsibility to use these in correct polymorphic ways falls on you, the developer. The only thing missing is strong type checking that prevents you from shooting yourself in the foot.
#5
The class field itself doesn't override any datablock values for objects of the class. Think of class/superclass as more of 'groups' that you can execute/trigger functions on (as mentioned above).
The class fields are also helpful when doing checks before executing a script on an object:
Datablock Inheritence happens by specifying a parent datablock to copy from:
In the above example, 'lil_joe' is based off of 'big_papa'. Any values specified in 'lil_joe' will overwrite those inherited.
As far as I know this is not 'inheritance' in the true sense... the values are just copied. But the result is the same.
If you need to do a lookup that is out of scope for the object in question you can store/lookup id's in one of these ways:
Store it in a dynamic field: I have used this most often. If you have an object type that always needs to know the id of another related object type then just create a dynamic field and assign the lookup id as a value. There is no reason to do recursive lookups on something that can be implicitly stated.
Store it in a global: Not the best method but handy if you have a unique object that needs to be referenced by all functions often.
Store it in a variable of a global: I saw this recently and am going to be moving all my globals to this method soon. The great bit is all you have to do is destroy the one global when you shut down the game.
www.garagegames.com/mg/forums/result.thread.php?qt=19752
Create a groups/simsets: You can group objects using simgroups(unique per object) or simsets(objects can belong to multiple simsets). Store the id of the group as a dynamic field of an group member (see above). Look up this field when you need the group, then call this group id when you process functions on that group.
This seems like more work at first but I am actually finding it to be a really flexible scripting system. Rather than being tied in to a strict object system where everything has to be processed a given way, you can build a system that manages your objects in a way that makes sense for your game... and your next game... and...
-Unk
09/26/2006 (4:56 pm)
Something that was not immediately obvious to me was that in addition to class/superclass you can inherit properties from other datablocks then override them:new t2dSceneObjectDatablock([b]big_papa[/b])
{
class = "big_papa";
superclass = "human";
health = "10";
deathanim = "someanim";
};
new t2dSceneObjectDatablock(lil_joe[b]:big_papa[/b])
{
class = "lil_joe";
health = "5";
deathanim = "someotheranim";
};
new t2dSceneObjectDatablock(lil_susie[b]:big_papa[/b])
{
class = "lil_susie";
health = "3";
deathanim = "someotheranim2";
};
new t2dSceneObjectDatablock(tiny_tim[b]:lil_suzie[/b])
{
class = "tiny_tim";
deathanim = "someotheranim3";
};The class field itself doesn't override any datablock values for objects of the class. Think of class/superclass as more of 'groups' that you can execute/trigger functions on (as mentioned above).
function human::onAdd(%this)
{
echo("human::onAdd() - A new human was added: ", %this);
}The class fields are also helpful when doing checks before executing a script on an object:
if(%obj.getFieldValue(class) != "lil_susie")
{
//then do something...
}Datablock Inheritence happens by specifying a parent datablock to copy from:
new t2dSceneObjectDatablock(lil_joe[b]:big_papa[/b])
{
class = "lil_joe";
health = "5";
deathanim = "someotheranim";
};In the above example, 'lil_joe' is based off of 'big_papa'. Any values specified in 'lil_joe' will overwrite those inherited.
As far as I know this is not 'inheritance' in the true sense... the values are just copied. But the result is the same.
If you need to do a lookup that is out of scope for the object in question you can store/lookup id's in one of these ways:
Store it in a dynamic field: I have used this most often. If you have an object type that always needs to know the id of another related object type then just create a dynamic field and assign the lookup id as a value. There is no reason to do recursive lookups on something that can be implicitly stated.
%obj.setFieldValue(big_joe,%id); %bigjoe = %obj.getFieldValue(big_joe);
Store it in a global: Not the best method but handy if you have a unique object that needs to be referenced by all functions often.
$big_joe = %id; %bigjoe = $big_joe;
Store it in a variable of a global: I saw this recently and am going to be moving all my globals to this method soon. The great bit is all you have to do is destroy the one global when you shut down the game.
$global_storage::big_joe = %id; %bigjoe = $global_storage::big_joe;
www.garagegames.com/mg/forums/result.thread.php?qt=19752
Create a groups/simsets: You can group objects using simgroups(unique per object) or simsets(objects can belong to multiple simsets). Store the id of the group as a dynamic field of an group member (see above). Look up this field when you need the group, then call this group id when you process functions on that group.
//on group creation
%group = new SimSet("humans01");
//on group member add
%group.add(%obj);
%obj.setFieldValue(humangroup,%group);
//on group lookup
%objgroup = %obj.getFieldValue(humangroup);This seems like more work at first but I am actually finding it to be a really flexible scripting system. Rather than being tied in to a strict object system where everything has to be processed a given way, you can build a system that manages your objects in a way that makes sense for your game... and your next game... and...
-Unk
#6
What is the difference between:
...I know I might be completely misunderstanding what setFieldValue does.
And wnat does this mean:
I'm guessing it's a typo, but if it's not I'm really confused :)
09/26/2006 (9:18 pm)
Excellent writeup Unk. A couple of questions...What is the difference between:
%obj.setFieldValue(big_joe,%id); and %obj.big_joe = %id;
...I know I might be completely misunderstanding what setFieldValue does.
And wnat does this mean:
obj.setFieldValue(humangroup=%group);
I'm guessing it's a typo, but if it's not I'm really confused :)
#7
09/27/2006 (12:38 am)
Heh... yah... I have some typos... hehe. fixing...
#8
Fixed:
Additional Info:
All setFieldValue() does is overwrite a value in an objects instance. GetFieldValue() looks up a value from an objects instance.
The idea is that as long an an object exists in the world you can store data in it. So why not use objects to store relevant data? This is handy in situations where you already know the id of one object but need access to the id of a related object. This also has the benefit of not creating additional reasources as well as destroying variables once they go out of scope (on object deletion).
This would create a new datablock with a dynamic field of 'somefield':
This would create a new static sprite based off of the lil_joe datablock called 'player';
Lets say we create another sprite we want to associate with the player called 'home'.
Maybe it is possible to click on the player but you can't click on the home. How do you look up the id without creating a global? Lets store the home inside the player!
Now whenever you want to send the player home you just look up the players field value:
-Unk
09/27/2006 (1:21 am)
I found the typo. Thanks man.Fixed:
obj.setFieldValue(humangroup=%group); was supposed to read: obj.setFieldValue(humangroup,%group);
Additional Info:
All setFieldValue() does is overwrite a value in an objects instance. GetFieldValue() looks up a value from an objects instance.
The idea is that as long an an object exists in the world you can store data in it. So why not use objects to store relevant data? This is handy in situations where you already know the id of one object but need access to the id of a related object. This also has the benefit of not creating additional reasources as well as destroying variables once they go out of scope (on object deletion).
This would create a new datablock with a dynamic field of 'somefield':
new t2dSceneObjectDatablock(lil_joe)
{
somefield = "";
};This would create a new static sprite based off of the lil_joe datablock called 'player';
%player = new t2dStaticSprite(player)
{
config = "lil_joe";
};Lets say we create another sprite we want to associate with the player called 'home'.
%home = new t2dStaticSprite(){};Maybe it is possible to click on the player but you can't click on the home. How do you look up the id without creating a global? Lets store the home inside the player!
%player.setFieldValue(home,%home);
Now whenever you want to send the player home you just look up the players field value:
%home = %player.getFieldValue(home); %player.setPosition(%home.getPosition());
-Unk
#9
The answer is to Ken's question is:
09/27/2006 (5:59 am)
@Unk & KenThe answer is to Ken's question is:
%player.setFieldValue(home,%home); // should be 100% equivalent to %player.home = %home;
#10
Thanks Ben & Ken. =P
-Unk
09/27/2006 (1:40 pm)
Awesome. Typing out setFieldValue all the time always did seem a little unweildy. =)Thanks Ben & Ken. =P
-Unk
Torque Owner Paul /*Wedge*/ DElia