Game Development Community

What's the difference between %this and %obj?

by ChrisG · in Torque Game Engine · 06/19/2007 (7:25 pm) · 14 replies

I"ve noticed in TorqueScript that a lot of datablock functions take the arguements %this and %obj
eg.
function ThisThing::onAdd(%this, %obj)
{
    ....
}

It seems to me that usually both of these objects refer to the instanciated object and the data members can be altered through either reference.

ie.
%this.myvar = 1;   // is the same as
%obj.myvar = 1;
So is there a difference between %this and %obj and when should each be used?

#1
06/19/2007 (7:35 pm)
%this = the datablock
%obj = the object

Do you want to change something in the datablock (for all objects), or change it for the individual object? Note: changing a datablock variable doesn't get sent across the net, so you're not going to be able to make changes like that, but you can make some changes for the server IIRC.
#2
06/19/2007 (8:01 pm)
So changing something using %this is similar to changing a static data member shared by all objects and calling a method using %this would be the same as calling a static method.. yeah?
#3
06/20/2007 (7:21 am)
I don't know the exact terminology, but say you have an Item that uses ItemData for its datablock. When you do ItemData::onAdd, %this refers to ItemData, whereas %obj isthe Item object that was just created. So say, for example, you want the Item to have a field containing some datablock value (don't know why, but for argument's sake...). You would say %obj.variable = %this.someValue;
#4
06/20/2007 (7:41 am)
Quote:%this- Is a variable that will contain the handle of the 'calling object'.

At a minimum, Console Methods require that you pass them an object handle. You will often see the first argument named %this. People use this as a hint, but you can name it anything you want. As with Console functions any number of additional arguments can be specified separated by commas. Also, a console method may return an optional value.


www.garagegames.com/docs/tge/general/ch05s02.php
#5
06/20/2007 (3:44 pm)
Thanks, I think I'm starting to get my head around it, but it might take a bit of experimentation.
One thing I did read in the link mb supplied is
Quote:
In addition to normal fields, which are common between all instances of an object type, TorqueScript allows you to create dynamic fields. Dynamic fields are associated with a single instance of an object and can be added and removed at will.
Does %this only refer to data stored in variables create from the datablock, while %obj refers to all variables stored in the object? - but then Tim suggests that %this will change data in all instances created from the datablock...

Coming into TorqueScript as a C++ programmer this distinction between the ItemData and the object data seems a bit vague.
Quote:
%this = the datablock
%obj = the object
this makes sense, but then it seems contradictory to:
Quote:
%this- Is a variable that will contain the handle of the 'calling object',
which suggests the %this is a reference to the object, the same as %obj

anyway I'll do a bit of experimentation tonight and update this thread once I get a clear picture.

- edit to fix typos
#6
06/20/2007 (4:41 pm)
There are two script objects associated with say a player:

1. a datablock object
- this is static, non-changing generic info about this player or any player of this type,
for example what DTS file it uses, what animations it has, etc.
- five different player instances will all use the same datablock object.

2. a player instance object
- this is data specific to the player instance at hand,
for example its name, hit points, location, etc.

many callbacks into script from the engine are called on the datablock object, but also pass in the player object.
in the onAdd(%this, %obj) example above, as has been said many times in this thread, %this is the datablock object, and %obj is the playerObject.

you could rewrite onAdd() so that it looks like this:
function ThisThing::OnAdd(%objDatablock, %objInstance)
{
}

personally i don't understand why the engine calls a method on the datablock instead of the player object,
since the player object itself contains a reference to the datablock, but that's how it is.
(%objInstance.getDatablock() should always be equal to %objDatablock.)
#7
06/20/2007 (4:42 pm)
Quote:
Quote:

%this = the datablock
%obj = the object


this makes sense, but then it seems contradictory to:

Quote:

%this- Is a variable that will contain the handle of the 'calling object',
They are both right. When you use a callback like onAdd(%this, %obj) or onCollision(%this, %obj), "%this" is the dataBlock and "%obj" is the object. However, when making a function like "Player::doSomething(%this)" %this refers to the object.

This can be very confusing, so forget about "%this" and "%obj", these are just names. It might as well be "%tom" and "%jerry"! It doesn't really madder, just remember that in a callback, first comes the dataBlock (commonly referred to as "%this"), and then comes the object (commonly referred to as "%obj"). And in a function, the first argument is saved for the calling object.

SOoooo.......
If I used a callback:
function Player::onAdd(%this, %obj)
{
   //%this, is the dataBlock
   //%obj, is the object
}
And if I made a function, it always goes functionName(calling object, other arguments)
function Player::doSomething(%this, %arg1, %arg2)
{
   //%this, is the calling object, followed by,
   //%arg1, argument 1, and
   //%arg2, argument 2
}
This is also the same:
function Player::doSomething(%bla, %arg1, %arg2)
{
   //%bla, is the calling object, followed by,
   //%arg1, argument 1, and
   //%arg2, argument 2
}
I hope my blabbering made some sort of sense.

EDIT- Oops, Orion posted sooner. :-)
#8
06/20/2007 (5:41 pm)
Thanks everyone - I think I now understand...

it wasn't the datablock vs object that was confusing me but rather
the difference between callbacks and console functions -
in these 2 contexts the %this refers to different things

cheers
#9
06/20/2007 (6:20 pm)
To answer Orion's semi-question:

The primary reason we make quite a few of the callbacks to a datablock namespace instead of the object's namespace is to allow for moddability.

Simply by creating a new datablock, you can completely change how callbacks are handled--for example, I like to use the following types of players:

Scuba
Human
Robot

Now, I could create a new c++ class for each of these player types, but the way the datablock and datablock callback system works, I just have to make 3 new (script) datablocks:

ScubaPlayerData
HumanPlayerData
RobotPlayerData

And this lets me write a separate callback handler for as many of the callbacks (that are made on datablocks) as I wish..for example, all three of my players work with water differently, so::

function ScubaPlayerData::onEnterLiquid(...)
{
// code to schedule decrementing the amount of oxygen in my tank
}

and

function HumanPlayerData::onEnterLiquid(...)
{
// code to start taking damage over time after my (held) breath runs out
}

and finally
RobotPlayerData::onEnterLiquid(...)
{
// nothing to do, unless robots rust!
}

Now, compare this to calling the ::onEnterLiquid on the Player class namespace:

Player::onEnterLiquid(%this)
{
  switch (%this.getDataBlock() )
  case : HumanPlayerData -- do stuff
  case : ScubaPlayerData -- do stuff
  case : RobotPlayerData -- do stuff
  // end switch
}

With the callbacks on the datablock namespace, all a scripter needs to do to create totally new types of players is create the new datablock, and create some new callback handlers for that datablock. These could be in separate files, all in one file, or arranged however we like for our normal programming practices.

If we called on the object's namespace, every time we wanted to add new types of players, we would have to go back and edit this single Player::onEnterLiquid(), and it could soon become extremely massive and hard to manage...and one little mistake that got checked in to our repository would break every type of player we have until fixed.

@ChrisG: hate to do it to you, but...

Quote:
t wasn't the datablock vs object that was confusing me but rather
the difference between callbacks and console functions -
in these 2 contexts the %this refers to different things

Not true unfortunately. Let me try to be exact, so you can fully understand the various scenarios:

ConsoleMethods are c++ code written within a class that is exposed to script, and can be called by script.

Callbacks are a method for c++ to "call" script, used when an event occurs within the engine that the c++ developer thinks might be useful for the scripter to "know about", and have a chance to do something about it.

In all methods implemented in TorqueScript--both Callback Handlers and Script Methods (functions that have a namespace, and therefore act upon an object), regardless of being called from another script method, or in result to a callback triggered by c++, the engine aways places the object ID of the "calling object" as the first argument. By naming convention, we use the name %this often, because it's very similar to the concept of the c++ "this" pointer, but that is a naming convention only.

Now, in callbacks specifically, the c++ developer indicates which object (and therefore which namespace) the callback should be executed within, simply by providing a pointer to that object as the first argument in the statement. By convention, for many of the event callbacks in the engine this is done on the datablock for some classes, due to reasons outlined above.

Example:
// assuming in our current scope, the "this" pointer points to a Player class--in other words, this callback 
// might occur in Player::processTick()
Con::executef(mDataBlock, 3, "onEnterLiquid", scriptThis(), Con::getFloatArg(mDepth) );

We have chosen explicitly to execute this callback (the Con::executef statement) on the namespace of the object mDataBlock points to--which is our assigned datablock for this player. We could have easily done:

Con::executef(this, 3, "onEnterLiquid", mDataBlock, Con::getFloatArg(mDepth) );

which would have been received in the "Player" namespace, but as stated above, that isn't as useful.

Now here is where it can get really confusing:

ConsoleMethods, when implemented are actually pre-processor macros, not class methods. In c++ class methods, the compiler always provides the calling object reference inside the "this" pointer, but that doesn't happen in macros--but it's important sometimes to have, so for the c++ ConsoleMethod macro only, we provide the token "object", which acts in exactly the same way (contains a pointer to the calling object).

Hope that helps someone, hehehe.
#10
06/20/2007 (7:28 pm)
Now I understand why there was a book written called 'The Black Art of 3-D Game Programming' - I think I found Runic Divination a lot easier to understand...

@Stephen - thanks for the lengthly explanation.

When I mentioned console methods previously I actually meant script methods...

So tell me if this short explanation on the difference between %this and %obj is close to being correct
(and I hopefully I'm beginning to understand)....

Usually (by convention) both refer to an object instance (by object ID) but
- usually %obj refers the datablock namespace that the object was instanciated from (eg. ScubaPlayerData)
- while %this can refer to the same namespace, it may refer to a namespace further up the chain (eg. PlayerData). This depends on if the object ID came from a callback, and how that callback was programmed in C++

In C++ parlance its like having 2 pointers to the same object, just that one of them (%this) may be cast to one of the parent classes that the object's class of derived from.
#11
06/20/2007 (8:03 pm)
I think part of your confusion is that you are focusing on the name of the parameter (which has zero value other than as a convention), and the order of the parameter, which is what is important.

The first argument to a script method is always the Object ID of the calling object. In the case of callbacks executed from c++, the c++ developer can make the choice of what is the "calling object". Many times, it turns out to be more useful that given the following relationship:

Object Y has datablock XXX

we want to execute the callback on the XXX datablock object--for the reasons I state above.

This decision is made simply for modding useability--and when this decision is made, it is also very useful to provide an additional objectID that refers to the actual Object Y--which in many cases comes as the second parameter (but again, the person that writes the Con::executef statement has complete control over that).

The other thing that may be confusing you is that you aren't considering datablocks as "objects" when we say "calling object". They actually are--when it comes down to it just about everything is an Object in Torque at this level.

Let's go back to my example, the ::onEnterLiquid() callback.

We issue this callback on the datablock of the Player object who's current Player::processTick() is being executed, because we want to be able to have different types of players deal with entering liquid differently. Viewed from this perspective, a datablock can be considered the way we indicate what "type" of player we are working with.

However, we still need an objectID for the actual Player object, because that's the object we want to affect--so we provide that objectID as well, in the second parameter.

So, for my example, the callback handler header (in TorqueScript) would look like this:

function ScubaPlayerData::onEnterLiquid(%this, %obj, %depth)
{
  // %this is the objectID of the datablock object named ScubaPlayerData
  // %obj is the objectID of the actual Player object that is entering the liquid
  // %depth is the percentage depth they are currently in the water
}

If you happen to be an OOP experienced/focused guy, the best way to view the relationship between a class and it's corresponding datablock class is the "has a" relationship.

Every Player (class) "has a" PlayerData (class) datablock

ScubaPlayerData, HumanPlayerData, and RobotPlayerData are all objects of the class PlayerData, and allow us to organize how these different "types" of players work in our game.

Specifically in C++, if you look at Player.h, you'll see the within the Player class definition the following declaration:
PlayerData * mDataBlock;

In other words, every object that is of class Player "has a" datablock. They are different objects, so the objectIDs that are sent in the first and second arguments to our callback handler (%this, and %obj accordingly) point to different objects.
#12
06/20/2007 (8:45 pm)
Hey Stephen -

i'm totally with you on avoiding case statements like your example.
they're just bad code and very prone to error as a project grows.

but when Bob falls in the water, to me it seems more natural to call onEnterWater() on Bob rather than on Bob's datablock. Bob can easily delegate the actual functionality to his datablock and thus avoid the case statements:

function Player::onEnterWater(%this)
{
   %this.getDatablock().onEnterWater(%this);
}

function HumanPlayerData::onEnterWater(%this, %playerObj)
{
   // do stuff
}

function ScubePlayerData::onEnterWater(%this, %playerObj)
{
   // do stuff
}

etc

i realize this is somewhat less direct than having the callback happen immediately on the DB,
but it feels more natural to me. Bob's datablock didn't fall in the water; Bob did.
#13
06/20/2007 (8:47 pm)
Thanks again for the explanations.
I believe I'm beginning to understand - the 'has a' relationship was something I hadn't quite figured out before. Now I think I need to play around with the scripts a bit first before I asking any more questions...
#14
06/20/2007 (9:49 pm)
@Orion: Not necessarily disagreeing with you, but it's one of those "this is how they decided to do it way back when", and it's now the standard :)

I think in the long run it does provide slightly more flexibility to call on the datablock namespace directly, but from a normal viewpoint it's simply a convention choice.