Game Development Community

Datablocks...classes...functions...mind exploding

by Storm Kiernan · in Torque 3D Professional · 03/23/2011 (5:37 pm) · 15 replies

I come from a C/C++ background and I've got to say that it's really not helping me a whole lot here; at least not when it comes to TorqueScript's classes and datablocks.

For instance, normally I could do something like:
class Player
{
   int age = 5;
   
   public void Speak()
   {
   }
}
In which case:
new Player().Speak();
makes sense.

I am having some trouble converting over to TorqueScript ideology. Here is the script I am trying to write (not for any game, just to learn) and attached is the error output:
//my new script

datablock SimObject(MyPlayer)
{
   className = "Player";
};
function MyPlayer::Speak(%this, %text)
{
   echo(%text);
}
%myVeryOwnPlayer = new SimObject(Bob)
{
   dataBlock = MyPlayer;
};
echo("********************************************************************");
echo("********************************************************************");
%myVeryOwnPlayer.Speak("Hello World! #1");
MyPlayer::Speak(%myVeryOwnPlayer, "Hello World! #2");
echo("********************************************************************");
echo("********************************************************************");

/*
6: Unable to instantiate non-datablock class SimObject.
********************************************************************
********************************************************************
scripts/server/MyNewScript.cs (18): Unknown command Speak.
  Object Bob(1486) Bob -> SimObject
Hello World! #2
********************************************************************
********************************************************************
********************************************************************
*/

#1
03/23/2011 (6:01 pm)
Hmm, if i where to guess (without trying your code), I believe you have to call:
%myVeryOwnPlayer.getDataBlock().Speak("Hello World! #1");

An object created will inherit the classes functions and the className's functions. In order to call datablock functions from a created object, you need to call it from the datablock by using getDataBlock(). If you wanted to call %myVeryOwnPlayer.Speak (in your case), you'd need to create Speak() as a child of SimObject (or the className). The player's code is a quiet good example of how this works (scripts/server/player.cs). If you study the engine side player, and the torque side player, you'll see that there is a link between PlayerData (the datablock) and Player (the class).

Quote:
6: Unable to instantiate non-datablock class SimObject.
This would be because SimObject is not an torque datablock/class, so to say. All classes/datablocks that are used used in torque have IMPLEMENT_CO_DATABLOCK_V1(SomethingData); (adds a datablock) or
IMPLEMENT_CONOBJECT(Something); (adds a class) in them.
#2
03/23/2011 (6:42 pm)
OK, so I got the calling of the functions to work:
datablock SimDataBlock(MyPlayerDatablock)
{
   
};
function SimObject::Speak(%this, %text)
{
   echo(%text);
}
%myPlayer = new SimObject(Bob)
{
   dataBlock = MyPlayerDatablock;
};

echo("********************************************************************");
echo("********************************************************************");
%myPlayer.Speak("Hello World! #1");
SimObject::Speak(%myPlayer, "Hello World! #2");
echo("********************************************************************");
echo("********************************************************************");

However, I want to know how to create my own class so that I can do something like:
datablock MyPlayerDataBlock(SomeNewPlayerDatablock)
{
};
function MyPlayer::Speak(%this, %text)
{
   echo(%text);
}
%myPlayer = new MyPlayer(Bob)
{
   dataBlock = SomeNewPlayerDatablock;
};
#3
03/23/2011 (6:55 pm)
Quote:
I want to know how to create my own class so that I can...
I'm assuming in script given the discussion.

You can add the "className" field inside your datablock declaration. This will allow scripted class-like (sort of) functionality that basically gives you an added layer of namespace calling. For example:
datablock PlayerData(SomeNewPlayerDatablock)  
{  
    // stuff
    className = FakedClassPlayer;
    // more stuff
};
With this, instead of calling functions by the PlayerData name you can now create a new set of methods under the namespace of FakedClassPlayer and these new methods can only be acessed by the FakedClassPlayer script-class while allowing the script-class to still have the option of making use of the parent class (PlayerData) methods.

'True' coded classes, that you are probably used to, will have to done engine side in C++, although some nifty things can be done using script objects.
#4
03/23/2011 (7:35 pm)
torquescript is NOT a true object-oriented language, just a facsimile of one. objects instantiated in torquescript using "new" or "datablock" keywords are based on predefined templates defined on the C++ side of things. creating a completely new object in torquescript would require first adding it to the engine source inheriting from simobject or simdatablock.

however, if you need a simple structure in torquescript just to hold a few primitive data types, you can use the "scriptobject" class.

%obj = new scriptobject(Date)
{
  month = 3;
  day = 23;
  year = 2011;
};

note that you could actually do this using any of torque's built-in script objects(including datablocks) since torquescript allows you to add an unlimited number of fields dynamically to any object. i just like to use the scriptobject because it's a light-weight object. not only can you add fields when defining an object, but you can also dynamically add fields to an existing object at runtime while it's in use!

%obj.nextMonth = 4;

if the nextMonth field already exists, it'll just get set to 4. if it doesnt already exist, the new field gets added to the object automatically and initialized to 4. it's script magic.
#5
03/23/2011 (8:08 pm)
The following code does not work:
datablock SimDataBlock(BasePlayerData)
{
   className = BasePlayer;
};
function BasePlayer::Speak(%this)
{
   echo("Hello World");
}
%newPlayer = new SimObject()
{
   dataBlock = BasePlayerData;
};
%newPlayer.Speak();
Error
scripts/server/myscript.cs (0): Unknown command Speak.
  Object (1486) SimObject

It is probably also worth noting that I cannot do the following without my game crashing on startup:
datablock PlayerData(BasePlayerData)
{
   className = BasePlayer;
};
#6
03/23/2011 (8:15 pm)
try %newPlayer.getDatablock().Speak();
#7
03/23/2011 (8:36 pm)
That didn't work:
scripts/server/myscript.cs (0): Unknown command getDataBlock.
  Object (1486) SimObject
scripts/server/myscript.cs (0): Unable to find object: '' attempting to call function 'Speak'
#8
03/23/2011 (9:25 pm)
OK so I got it to work sufficiently by doing:
datablock SimDataBlock(BasePlayerData)
{
   className = BasePlayer;
};
function BasePlayer::Speak(%this)
{
   echo("Hello world");
}
function BasePlayer::DoThis(%this)
{
   echo("Did that!");
}
function BasePlayer::DoThat(%this)
{
   echo("NO!");
   %this.DoThis();
}
%player = new SimObject(BasePlayer)
{
   dataBlock = BasePlayerData;
};
%player.Speak();
%player.DoThis();
%player.DoThat();
%player.dataBlock.className.Speak();

They key to this was to put BasePlayer inside the ()
#9
03/23/2011 (10:03 pm)
3D Game Programming All In One, Advanced 3DGPAIO, The Game Programmer's Guide to Torque and Multiplayer Gaming and Engine Coding for the Torque Game Engine are all great resources on coding in TorqueScript. One caveat - they're all for TGE/A. So, while the scripting language still functions the same way, the underlying engine is different and some of the script classes have changed. So, instead of UseThisDeprecatedClass::DoSomething() you'd use TryTheNewClass::DoSomething() instead. I can't think of a good example off the top of my head....

Fortunately, we all have access to TGE/A so that you can practice a little before applying what you've learned to T3D if you like. Or you can find the changes and adapt as you go.
#10
03/23/2011 (11:23 pm)
Yeah I bought The Game Programmer's Guide to Torque. Out of curiosity, what specifically are you responding to?
#11
03/25/2011 (8:34 pm)
Storm-

the system of namespaces is set up so you can have specialized objects which inherit from base objects. however, in my experience you can only subclass a base class 3 times:

1. function shapebase::onadd() -base, all shapebases would do this
2. function datablockName::onadd() - more specific
3. function datablockClassName::onadd() - morer specific
4. function objectName::onadd() - morest specific

the code you posted above is using #4, but I believe you're trying to do it using #3 instead. maybe datablocks aren't meant to be used with simobjects?
#12
03/26/2011 (12:26 am)
I used SimObject because that's the only class I knew at the time. You can swap the class name to be whatever suits you. The reason I am doing this is so that you can specify some class that you want, a bunch of overriding methods defined specifically for that class, and then instantiate objects that are of that class type. I could easily do this in C++, but I am trying to do it in TorqueScript.

Does this answer your question?
#13
03/26/2011 (5:21 am)
Fact: SimObjects do not use datablocks. You get no errors or anything, because it's perfectly legal to add a new member to a SimObject called 'dataBlock', but it's not actually used as a datablock, and SimObject doesn't define the getDataBlock function. GameBase is the first class that actually uses datablocks, from which most other common object classes are derived.

Note that you could just create a SimObject::getDataBlock function in script, which would allow you to emulate datablock functionality. It's just that SimObject isn't really designed to be used for anything... it's just a base class.
#14
03/26/2011 (10:10 am)
That is good information and explains why dump() didn't print out a getDataBlock() function. The datablock in this case wasn't actually necessary.
#15
03/28/2011 (7:03 am)
class and superclass fields can be used for non-datablock script objects. I'm not sure what class in the hierarchy first implements those fields though.