Game Development Community

Can I create custom objects (with inheritence, etc) in script?

by John Klimek · in Torque Game Builder · 02/21/2006 (6:58 am) · 7 replies

Can I create custom objects in TorqueScript?

For exampe, I'd like a GameObject object which all of my other objects inherit from. This object would contain fields like Name, ObjectID, OwnerID, Health, etc. After that object is created I'd like to create a PlayerObject object which inherits from GameObject (so it automatically has those fields and any methods I defined).

Is this possible in TorqueScript or must I do it in C++? If I must do it in C++ I'll probably just forego OOP and do it the old-fashioned way in script only....

#1
02/21/2006 (10:05 am)
I believe there is some lengthy discussion on this already in previous posts. Just search for 'Inheritance' or 'OOP'. I belive that code changes are required for OOP style inheritance though there are code examples posted which you could integrate with your build.

-Unk
#2
02/21/2006 (12:02 pm)
As far as I can tell there is no way to get OOP in torquescript without modifying the engine. That said, by working with how torquescript does things you can (in most cases) get a reasonable facsimile.

Torquescript name space is designed to superficially resemble class functions. That is, you can give a function a name that looks rather like a namespace designation and access the function in the same manner. E.g.,
$myVar = new scriptObject() { class = myClass; };
function myClass::myFunction() { }
$myVar.myFunction();

But you can only create script classes based on the scriptObject. If you want to create subclasses of any T2D object you must either write it in C or apply Bryan Edds OOP resource (modify the engine).

You can have to levels of inheritance, class and superclass. As far as I know there is no structure here. For example, you could:
$myVar1 = new scriptObject() { class = myClass; superClass = mySuperClass; };
$myVar2 = new scriptObject() { class = mySuperClass; superClass = myClass; };

I'm not saying you would want to, but my impression is that torquescript doesn't really have any concept of inheritance and you could do that.

Also, each object is distinct so you can't inherit data members. This isn't that much of a limitation, it just means you have to adjust your way of thinking if you are used to C++. Others have given good examples of this that a search should find, but here is a quick one off the top of my head that I don't recall seeing. I happen to like using functions, especially ones that get called automatically.
mySuperClass::onAdd(%this) {
  %this.Health = 40;
  }
myClass::onAdd(%this) {
  %this.superClass.onAdd();
  %this.Health += 20;
  }
$myVar = new scriptObject() { class = myClass; superClass = mySuperClass; };

onAdd() is called immediately after an object is created so this is somewhat like doing the C++ constructor. Only the first function found is used so if you want the superclass's onAdd called you must do it yourself.

All objects can have a name. This is specified when they are created and can later be used to identify them. Note that if you reuse a name it sort-of identifies the old object it was associated with and is definitely associated with the new object. I don't like this confusion so I tend to avoid names. As an example:
function myName::myFunction(%this) { echo("Hello World"); }
$myVar1 = new scriptObject(myName);
$myVar1.myFunction();
> Hello World
myName.myFunction();
> Hello World
myName.newAttrib = 5;
echo($myVar1.newAttrib);
> 5
$myVar2 = new scriptObject(myName);
echo(myName.newAttrib);
>
$myVar1.myFunction();
> Hello World
$myVar2.myFunction();
> Hello World

This is from memory so I can't claim there isn't a mistake in the above but it should be easy enough to check. In essence, when you name an object it points to that object and includes a function name space. If you re-use a name then it no longer points at the old object, but the old object keeps the same function name space.

Also note that you probably don't need ObjectID. When you create an object an address is returned that uniquely identifies it. This works for any global id. If you want to track objects of a type you can use SimSets. You can add, remove and list items in a SimSet. So if you have a class that is items available in a store you might have something like:
$StoreItemList = new SimSet();
myClass::onAdd(%this) {
  $StoreItemList.add(%this);
  }
$myItem1 = new scriptObject() { class = myClass; };
$myItem1.price = 12;
$myItem1.weight = 5;
$myItem2 = new scriptObject() { class = myClass; };
$myItem2.price = 1;
$myItem2.weight = 1;

I can't remember the simset functions reliably, but doing a dump() will reveal them.

Tim Doty
#3
02/21/2006 (12:56 pm)
Thanks for the excellent replies!

Can you explain SimSets or point me in the right direction of documentation for them? I've heard them mentioned before but I have no idea what they are.

For the game I'm developing I plan to have up to 8 players at a time so I'm thinking of creating a "Player" scriptObject for each player and then storing all of them in an array (is this the best method?). Each player can have multiple weapons and buildings so I would have my Player scriptObject contain Weapon/Building scriptObjects (again, stored in arrays so I can easily loop through them).

Does my design sound appropriate?

Thanks again for the help!
#4
02/21/2006 (1:35 pm)
I'm no expert with torquescript and you can certainly handle your players with an array, but using a SimSet gives you a logical grouping that may prove handy.

Torquescript arrays make me a little nervous because they aren't really arrays but just a naming convention that sort of looks like an array. It took a while for the distinction to really settle in for me. Don't think I'm trying to talk you out of using arrays because I make significant use of them.

A SimSet is a container object to which other objects can be added or removed. You can retrieve an object via it's index in the container. You can get a count of the number of objects in a container which lets you iterate through it. Although objects in a SimSet are accessed only through a single index you can simulate a two dimensional array by having a SimSet containing other SimSets which contain the actual objects.

To loop through a SimSet you might do the following:
$mySimSet = new SimSet();
$mySimSet.add($player);
...
for (%i=0; %i<$mySimSet.getCount(); %i += 1) {
  %tObj = $mySimSet.getObject(%i);
  // do something with %tObj
  }
This is from memory and I can't remember if TorqueScript allows %i++ or not (one of the languages I'm flipping around between doesn't, I just can't remember which) and I can't remember for sure if it is .getCount().

Anyway, the SimSet keeps track of the number of objects for you, which a torquescript array won't so that is one less (global) variable to worry about.

It'd be nice if you could pass a function pointer to the simset and process that or at least have iterators, but that's why we have the source code, eh? ;^)

Obviously I'm leaning toward using SimSets, but I'm not in a position to say what is best.

One place I am using arrays is in managing a two-dimensional set of data [for a tile layer]. (Okay, so I could use the custom data for that, I forgot, okay ;^) -- that is certainly a case where I find it much easier to deal with an array than a SimSet.

Tim Doty
#5
02/21/2006 (3:14 pm)
Ok, suppose we have two layers of SimSets:

Players is a SimSet containing a Buildings SimSet (each player can have many buildings).

Let's say I wanted to find if anybody had a certain type of building. Would I basically iterate the first simset (players) and then iterate the buildings simset? ...or is there an easier way?
#6
02/21/2006 (3:25 pm)
Wouldn't it be easier to have a seperate simset for the buildings that contains something like:
buildingType = "house";
isOwnedBy = "p1";
Then you're just looking for all buildings with p1 as the owner, and are of the required type.
#7
02/21/2006 (3:33 pm)
I'm not sure I follow your example, but I'll take a stab at it.

If each player object has a SimSet containing the buildings owned by him then you have a one-way relationship. You can find what buildings belong to a player but the only way to find what players own a building requires an iteration across all players looking for that building.

If that is indeed what you are after the way to do that is to have a two-way relationship. Not only does a player point to buildings, but a building points to a player. If only one player can "own" a building at a time then this relationship can be suitably described by a "player" field in the building object.

Now, if there are different types of buildings, say, hut, town house, manor house and castle, and you want to find everyone who owns a castle then you can do this with just the one-way relationship, iterating over all buildings owned by all players looking for the type. However, a more efficient method of searching would be to have more SimSets and have a two-way relationship. Then you could do something like the following:
for (%i=0;%i<$CastleList.getCount();%i+=1) {
  if ($CastleList.getObject(%i).player == $thisPlayer) // do something
  }

But if all you wanted was a tally of buildings owned I would probably just set it up as an array. $BuildingsOwned[%player,%building] = number of buildings of that type owned by player. This gives quick and easy access to the totals. It would have to be updated whenever someone obtained or lost a building, of course.

I ran across a link for more information on SimSets doing a search, I recommend doing that and perusing what you find.

Tim Doty

Edit: I've been trying to type 'quote' for 'code' all day. Sigh.