Game Development Community

Datablocks, Classes, and OOP

by Deozaan · in Torque Game Builder · 06/09/2007 (3:42 pm) · 19 replies

Hi all,

I'm having a really hard time understanding how to make my own custom classes for objects. I think datablocks are the things I want to use to make classes, but I don't understand how the inheritance works or how to make subclasses or anything like that.

I've held off asking this question because I've been scouring the documentation and TDN and other TGB forum posts for answers, but I haven't found anything that's really cleared this one up for me.

I come from a Flash ActionScript background and was just starting to get my head wrapped around OOP before I got TGB. I don't really know C/C++ but I think I understand the concepts of OOP. I'm just not sure how to implement them in TorqueScript.

Any help would greatly be appreciated!

#1
06/10/2007 (12:03 am)
You should follow the tutorials in TGB documentation. All of them. I think it's well explained as I didn't knew OOP a year before and now it seems pretty natural to me, thanks to these tutos.
(in TGB : Help > Documentation)
#2
06/12/2007 (1:56 pm)
I've been trying but most of the tutorials are broken in some way that keeps me from being able to complete them. I was especially discouraged by the Checkers tutorial, which I finally managed to figure out after about a week, with the help of others.

Thank you, Benjamin, for the response. I'll make another effort to go through them but if you have any specific tutorials (that work!) you could point out to me I would appreciate it very much.
#3
06/12/2007 (4:50 pm)
Be sure to use TGB 1.3 and not 1.5 beta.
The fish tutorial was my first one, then I did the shooter. As long as you don't try to use the ones at TDN but those in your documentation folder they should be clean (at maybe the exception of the checker tuto).
#4
06/30/2007 (8:30 pm)
And ninja platformer if it's in there, don't think it is but they finally fixed the one on tdn i heard
#5
06/30/2007 (9:24 pm)
Deozaan ... sorry, I just read through this thread ... your question was related to OOP and Inheritance in TorqueScript and I failed to see anyone actually answer that question ... so I'm going to try ... bare with me :)


function CustomClass:customFunction(%param1, %param2)
{
}

function SubClass::customFunction(%param1)
{
}

function Class::customerFunction()
{
}

I'm going to explain this the way it comes more naturally to me, which is C# ...

In the above example ... if you have an object named "foo" which is an instance of SubClass, which inherits from CustomClass (which in turn inherits from Class) ... you'd expect to have three overloads ... in TorqueScript ... you might find this is a bit different ...

%x = new ScriptObject() { class = "SubClass"; superClass = "CustomClass"; };

Now %x is an instance of SubClass, an is also inheriting from CustomClass ... note that 'Custom' is not referenced here ... this is a basic 'pure torquescript' approach ... I'm not sure if datablocks would allow for more chaining to occur (I know you can chain your datablocks for making 'templates' ... but I believe the 'class' and 'superClass' portion of the datablocks is skipped in inherited versions ...)

Now, if you wanted to be able to call the single parameter version of 'customFunction' which exists in 'SubClass' you'd simply just call %x.customFunction(value) ... however, the two parameter version is not available ... you can call it by naming it directly like a static function CustomClass::customFunction(val1, val2) ... however ...

There is also the, I believe, 'parent::customFunction' convention as well ... pretty sure it's "parent" and not "base" ... however, to keep the code easier to read ... just access it like a static function.


What I do when I'm laying out an OOP design for TorqueScript is create all my classes as if they are static, and then just instantiate an object ... and tie any special functions I need to call in it to it ... generically ... I'll create a single 'master' class which contains all my functions for everything ... then on an as-needed basis, reference it ...

OOP exists in TorqueScript, but is fairly flakey ... and if your a true OOP developer ... it's hard to follow along ... just try to remember that TorqueScript is a scripting language and it has it's flaws ...

Any more questions ... just let me know.


#6
07/04/2007 (12:38 am)
Thanks David for the explanation. I'm afraid you lost me somewhere in there.

I'm not sure if it was the terminology or just all the ellipsis points. I couldn't tell where one idea ended and another began.

In your example I don't see where "Class" class is used. Also, just a funny little thing, you wrote

function Class::custom[b]er[/b]Function()

I'm also not sure what you meant by calling it as if it were a static function. I guess maybe I don't understand OOP as well as I thought I did.

I really do appreciate the offer of help you've made here, and I've found your insight helpful in other forum topics, but I think I'm more confused now than I was before I read this post.
#7
07/04/2007 (12:44 am)
Deozaan, I apology for the confusion ... let's see if I can clear it up ...

Class, SubClass and CustomClass ... all have a 'customFunction' method/function

foo is an instance of CustomClass, like so;

%foo = new ScriptObject() { class = "CustomClass"; };

We can inherit from either Class or SubClass in this by using the "superClass" attribute in the 'new ScriptObject() { };" code block ...

Now, if you wanted to use the CustomClass -> SubClass -> Class model I tried to hint toward, you could do something like this;

function CustomClass::customFunction(%params)
{
  Class::customFunction(%params);
  SubClass::customFunction(%params);

  // your CustomClass specific code here
}

Now ... the original instantiation above;

%foo = new ScriptObject() { class = "CustomClass"; };

Can be treated as if it inherits from SubClass and Class


You can conditionalize your execution of the other class functions -- so if a parameter is not passed for example, you fail to call SubClass::customFunction ... or you pass a default value in (ie; overload it)

Now ... hope that clears things up ...

And yeah, I wrote that at an extremely early hour in the morning...

#8
07/04/2007 (12:45 am)
Oh and ... static means ... you don't need an object instance to execute functions ... Class::customFunction() is a 'static' use of the 'customFunction' ... where as %foo.customFunction() [where %foo is an instance of a Class object] is a instant execution ...
#9
07/04/2007 (1:00 am)
None of these will work, there is the %this paramater missing in the function declaration that specifies the object that calls it.

Same goes for the static call of them where you would need to manually pass the %this object to the function or it will bomb with a 0 object not found error if you try to access any field on the object.
#10
07/04/2007 (1:38 am)
Thanks David, that is a lot clearer now!

So I can call a function that belongs to any class statically just by typing (for example) Class::function() ? That's cool.

Now just one more little thing to help me understand, in this example:

function CustomClass::customFunction(%params)
{
  Class::customFunction(%params);
  SubClass::customFunction(%params);

  // your CustomClass specific code here
}

Because both of those functions share the same name, does Subclass::function overwrite Class::function entirely or just any parts that are different? Standard OOP says that only different parts will be changed, with SubClass overriding Class, but you said TorqueScript works a little differently, so I just thought I'd ask.

Anyway, just knowing that I can call or add a function by calling it statically as you described is a great help!

@ Marc: I think the %params in the example was meant to include the %this parameter. Plus, I don't think the code given was meant to be working code; just a quick and dirty example to explain the point. Thanks for the clarification, though.
#11
07/04/2007 (6:49 am)
One more question.

If I have 3 classes, like so:

function GrandDaddy::function(%params)
{
    //stuff
}

function Momma::function(%params)
{
    //stuff
}

function Baby::function(%params)
{
    //stuff
}

and then I wanted Baby to be a subclass of Momma (the parent class) and also inherit from GrandDaddy (grandparent), how would I do that? Would it automatically happen or would I need something like this?

function GrandDaddy::function(%params)
{
    //stuff
}

function Momma::function(%params)
{
    GrandDaddy::function(%params);
    //stuff
}

function Baby::function(%params)
{
    Momma::function(%params)
    //stuff
}

Or in other words, how does multi-generational inheritance work?
#12
07/04/2007 (10:31 am)
@Marc, your absolutely incorrect ... %this does -not- have to be passed, and it doesn't even have to be called %this ...

I was making simple examples, not usable code.

Any function called on an object, that is in fact from an instance object (static functions do not pass '%this'), will automatically pass an instance of the object as the first parameter.

function class::function(%foo, %bar)
{
}

%foo is what you are referring to as %this -- and I skipped passing %this for the simple fact that it's mentioned in the documentation very clearly


@Deozaan,

In the case of calling the functions statically, all the code in Class::customFunction() and SubClass::customFunction() will execute in it's entirety ... so none of it is overriden or ignored ... now, if Class::customFunction() moves an object 5 units to the left and SubClass::customFunction() moves the object 5 units to the right, then the unit effectively does not move as far as the end-user is concerned because it went -5, +5 ... which winds up being 0 (or the original location)

In the case of using "class" and "superClass" in the instantiation block, the parent will override the child and the child will not be called unless specifically told ...

%foo = new ScriptObject() { class = "CustomClass"; superClass = "subClass"; };

If you call %foo.customFunction() you will be executing the code in the "CustomClass" definition, and the subClass definition will only be executed if CustomClass calls "parent::customFunction()" or "SubClass::customFunction()" ...

"parent" is a special identifier that says "calling the parent class", which, in the above case would be subClass ... but if the superClass was set to "bar" then "parent" would reference "bar" ...

In some cases, you might want to do something like:

function customClass::customFunction(%params)
{
  parent::customFunction(%params);
  

  // your other logic here
}

And this will allow the parent class code to execute, then the current classes code to execute ...

Sometimes this is a wanted behaviour ... say you have a Player and Enemy class, and you classify them both as "Mobile" ... a lot of the code for Player and Enemy will be shared, such as how they move and interact with the world, however, the major difference between the two is one is end-user controlled while the other is AI controlled.

Let me try to give a better example:

function Mobile::moveLeft(%this) // better Marc? :P
{
  %this.moveLeft = true;
  %this.updateMovement();
}

function Mobile::moveRight(%this)
{
  %this.moveRight = true;
  %this.updateMovement();
}

function Player::moveLeft(%this)
{
  // some logic that decides if you CAN or not
  parent::moveLeft(%this);
  // some other 'after move' logic
}

function Enemy::AIUpdate(%this) // this is a function called on a schedule that handles AI though
{
  // some logic
    %this.moveLeft(%this);
  // some more logic
    %this.moveRight(%this);
}


Now ... you have one location that actually controls your left/right movement of both your player and your enemy ... so the objects will physically move around in the exact same manner, the only difference here is ... what causes them to move in one direction or the other ...

In your code, you might do a moveMap.bindCmd("keyboard", "up", $player @ ".moveLeft(true)", $player @ ".moveLeft(false)"); which would cause the Player::moveLeft() to be called ... which would then perform some custom logic and allow or disallow the movement, but the "Mobile::moveLeft" can be shared between the two for any specific movement code ... as well as the updateMovement function that is called ...


#13
07/04/2007 (10:39 am)
Deozaan, for the multi-generational stuff ... the way you have it would work ... but in the case of two generations, you can use "class" and "superClass" ...

Momma as class, and GrandDaddy as superClass ... but if you want to go past that ... you'd need to do the static call references.

In the case of using "class" and "superClass" though, you could use the "parent" referencing I mentioned in the above post ... so you could just say "parent::function()" in the Momma version to call the GrandDaddy version ... which allows you to have a GrandDaddy and GrandMomma class ... and you can do:

%foo = new ScriptObject() { class = "Momma"; superClass="GrandDaddy"; };
%bar = new ScriptObject() { class = "Momma"; superClass="GrandMomma"; };

in the case of genetics, you could have two objects that were effectively the same but one derives a good portion of it's looks from the grand mother ... where the other gets a good portion from the grand father ... which is sometimes a wanted effect ...


#14
07/04/2007 (2:24 pm)
Thanks again, David. You've been very helpful.

You seemed to have contradicted yourself in this statement:

Quote:In the case of using "class" and "superClass" in the instantiation block, the parent will override the child and the child will not be called unless specifically told ...

%foo = new ScriptObject() { class = "CustomClass"; superClass = "subClass"; };

If you call %foo.customFunction() you will be executing the code in the "CustomClass" definition, and the subClass definition will only be executed if CustomClass calls "parent::customFunction()" or "SubClass::customFunction()"

From my understanding, Class = child and SuperClass = parent. Before your code snippet, you say parent (SuperClass) will override child (Class) but after the code snippet you say child overrides parent. That is, in order to call the parent function, you need to specifically say parent::function().

So if I'm to understand all this correctly, basically a child only inherits functions from a parent if the child doesn't have that function defined. If the child does have it defined, then the parent function is entirely ignored unless specifically called.

In OOP in other languages (or at least in ActionScript) a child would inherit all functions from its parent and you could make minor changes to the parent functions in the child classes by doing some sort of import on the parent function and then changing or adding certain parts. Thus you wouldn't have to rewrite most of the function with just a few changes. TorqueScript doesn't work this way, apparently.

Again, thank you very much David. You've been very helpful and I (think I) have a much better understanding of OOP and inheritance in TorqueScript.
#15
07/04/2007 (2:40 pm)
I want to make an out of band observation here that I've found is very important to most users:

At it's root, TorqueScript is not a true OOP language. David has provided some outstanding explanations and implementations example of how to use general OOP principles within TorqueScript, but at the top level, OOP will eventually break down when using TorqueScript alone.

You noted this specifically here:

Quote:
So if I'm to understand all this correctly, basically a child only inherits functions from a parent if the child doesn't have that function defined. If the child does have it defined, then the parent function is entirely ignored unless specifically called.

In OOP in other languages (or at least in ActionScript) a child would inherit all functions from its parent and you could make minor changes to the parent functions in the child classes by doing some sort of import on the parent function and then changing or adding certain parts. Thus you wouldn't have to rewrite most of the function with just a few changes. TorqueScript doesn't work this way, apparently.

And I'm glad that you did recognize it, and wanted to reinforce it--TorqueScript emulates a pretty good portion of OOP principles, and can be quite flexible in some ways, but it is important to understand that it is emulation, so you need to approach any OOP concept with that perspective.
#16
07/04/2007 (2:49 pm)
Deozaan, yeah, I see what you mean about the contradiction ... haha, I need to re-read things when i write them, eh?

The child overrides the parent, and the parent is only called if you specify the "parent::function()" calling convention ...

in the case of "child overrides parent, only where things are different" (or whatever the phrasing was) ... this is completely untrue, and I've never even heard of this as being an OOP standard ...

Generally speaking, when your designing objects in OOP ... if a parent class has a method that performs a functionality and you want to extend it ... what you do is:

function Class::function(%params)
{
  // move left by 5
}

function SubClass::function(%params)
{
  parent::function(%params);
  // move left by 2
}

Which effectively moves you 7 units ... because your 'SubClass' which inherits your 'Class' is adding more logic to the equation and moving you two more units ...

So, in the Mobile, Player, Enemy class hierarchy example Mobile might move the units the standard minimum amount, while Enemy and Player move them in additional units that are specific to the unit in question ...

So ... Mobile might move 5 units, and Enemy might move an additional factor value that is specified as a dynamic field of the Enemy class ... and player might move an additional factor amount that is relative to the speed and state of the player ...

and yeah, I thought I covered the 'torquescript isnt true oop' earlier, but Stephen made a good point ... you can't approach TorqueScript as if it's an OOP language ... cause it's not ... but you can use some basic OOP concepts and if your coming from a true OOP background, torquescript's "version" of it may find itself to be extremely confusing.

#17
07/04/2007 (2:57 pm)
Oh ... to follow up on the 'override where things are different' ... just re-read that, haha, novel thought eh?

The 'only where things are different' is a standard the developer follows, not the language ....


So basically, if you have 2 classes and one inherits from the other and overrides a function ... the only code that is executed is the code in the child when the child instance is called upon ... however, you can execute the ENTIRE code from the parent using the 'parent::function()' calling convention ... now, the child does not prevent any portion of the parent code from executing, it will execute in it's entirety ... the purpose of the child code is to add more logic or modifications on top of what the parent code has already done ...

So for example you have a child and a parent, and the parent uses a dynamic field to determine the units to move ... the child can change this dynamic field value prior to telling the parent code to execute, and then update the field back to it's original value before exiting:

function child::function()
{
  %this.value = %this.value - 5;
  parent::function();
  %this.value = %this.value + 5;
}

so basically, the child can both increase and decrease the effect of the parent's function call prior to it occuring ... allowing you to perform instance level verification and clean up that is specific to the lowest class in the chain (the child)

#18
07/04/2007 (7:00 pm)
Thank you Stephen.

And thanks again David. Your insight on how modify parent functions within the child class is great! I was just starting to get into OOP in ActionScript before I got Torque, but there was something like this (the syntax is incorrect, but the idea is there):

function Child::function(%params)
{
    use parent;
    %var1 = 50; // changing variables from the parent function
    %var2 = "Bob";
}

When run, the program would only execute the modified code, instead of running the entire logic of the parent function and then the additions in the child function.

Or maybe I have no idea what I'm talking about. In any case, you've been very helpful and I think I have a very good understanding of the OOP emulation in TorqueScript now.
#19
07/05/2007 (12:31 am)
Deozaan, glad I could help ... and as for the actionscript stuff, not really sure ... I played with actionscript very little when experimenting with Adobe Flex at work ... I never really touched the OOP concepts of it ...