Constructors
by Shawn LeBlanc · in Torque Game Builder · 02/11/2007 (6:25 pm) · 9 replies
Coming from a C++ background, I was wondering: when an object is created with "new", can I make it so a callback of that object get called? It's just a curiosity, really. I imagine I can call an Init function of some kind after I've created my object, but I was wondering if there was something more automatic.
Thanks much!
Shawn
Thanks much!
Shawn
#2
Unless you have some particularly specialized stuff to do, I've found it's better to work with the engine and let it set up everything it needs to before messing with an object instance. 'onAdd' is a good place to do that.
02/11/2007 (7:13 pm)
I'd suggest overridding 'onAdd' in script. It gets called for each instance when it's added to the scenegraph (or whatever the equivalent is in TGB -- been working in TGE lately).Unless you have some particularly specialized stuff to do, I've found it's better to work with the engine and let it set up everything it needs to before messing with an object instance. 'onAdd' is a good place to do that.
#3
the above mentioned C++ tweak, would allow ConsoleObjects, which are not derived from t2dSceneObject ... to have callbacks as well, such as TCPObject, HTTPObject, my XMLObject resource ... etc, etc, etc ...
However, this may add some additional strain on the engine, which may be why it wasn't included "out of the box" ... especially if you add TONS of objects ... take for example a level with 2000 objects in it (very possible) ... and you want an "onCreated" callback in your game for one object type specifically, which might be anything derived from ScriptObject ... you'd be best to add this callback to the ScriptObject class itself ... rather then to the SimObject class ... as all 2000 objects you just created will now attempt to generate a callback to something that does not exist ... wasting 'load' time doing extra work ... just because you want the functionality for one object type in this game ... and in the next, you want it on another object type ...
Good practice to keep your C++ tweaks, especially ones that are specific to a game, in there own code-base ... I personally have multiple copies of TGB installed ... I have one 'fresh' install, thats completely bare, and whenever I want to make C++ changes that are specific to a game, I make a copy of the entire install dir, and do them in the copy ... if I'm making a reusable resource, I have another source tree I make those changes in, then I copy them over to any game trees that I want that functionality in ...
helps cut down the unnecessary code in games that don't need them ...
I even have a source tree I'm working on now, that I'm actually attempting to remove a number of "stock" objects from, as my game just does not use them ... all my game uses is t2dStaticSprite and t2dSceneObject ... and since Static Sprite extends Scene Object ... I need Scene Object anyhow ... even if I didn't use the scene objects in my game ... so what I'm attempting to do is strip out the t2dPath, t2dTrigger, etc, etc objects one by one ... and testing to see if i broke anything, as I don't see the need for the code to exist in the final game EXE if I don't use it ...
Anyhow, I think I went a little off topic there ... haha ... hope my point is clear ...
02/11/2007 (7:19 pm)
Actually, you may want to look into "onLevelLoaded" -- depending on your needs ... I sort of jumped into auto-assumption mode when you mentioned your C++ background ... but yeah, if you haven't seen or read about it yet, look at the "onLevelLoaded" callback -- as I said, I don't know off hand if it's called at t2dSceneObject creation or through the loadLevel of the SceneWindow ... but it should be all you really need for most purposes -- the above mentioned C++ tweak, would allow ConsoleObjects, which are not derived from t2dSceneObject ... to have callbacks as well, such as TCPObject, HTTPObject, my XMLObject resource ... etc, etc, etc ...
However, this may add some additional strain on the engine, which may be why it wasn't included "out of the box" ... especially if you add TONS of objects ... take for example a level with 2000 objects in it (very possible) ... and you want an "onCreated" callback in your game for one object type specifically, which might be anything derived from ScriptObject ... you'd be best to add this callback to the ScriptObject class itself ... rather then to the SimObject class ... as all 2000 objects you just created will now attempt to generate a callback to something that does not exist ... wasting 'load' time doing extra work ... just because you want the functionality for one object type in this game ... and in the next, you want it on another object type ...
Good practice to keep your C++ tweaks, especially ones that are specific to a game, in there own code-base ... I personally have multiple copies of TGB installed ... I have one 'fresh' install, thats completely bare, and whenever I want to make C++ changes that are specific to a game, I make a copy of the entire install dir, and do them in the copy ... if I'm making a reusable resource, I have another source tree I make those changes in, then I copy them over to any game trees that I want that functionality in ...
helps cut down the unnecessary code in games that don't need them ...
I even have a source tree I'm working on now, that I'm actually attempting to remove a number of "stock" objects from, as my game just does not use them ... all my game uses is t2dStaticSprite and t2dSceneObject ... and since Static Sprite extends Scene Object ... I need Scene Object anyhow ... even if I didn't use the scene objects in my game ... so what I'm attempting to do is strip out the t2dPath, t2dTrigger, etc, etc objects one by one ... and testing to see if i broke anything, as I don't see the need for the code to exist in the final game EXE if I don't use it ...
Anyhow, I think I went a little off topic there ... haha ... hope my point is clear ...
#4
But if onAdd is specific to the scenegraph, then it would not be useful for non-sceneobject classes such as TCPObject, etc ... so again, I think were back at "registerObject" for an "all purpose object constructor callback"
All depends on his needs though, I guess -- if he just wants an "on added to scenegraph" callback, I believe onLevelLoaded may do the trick -- IF it's called whenever an object is loaded into the level (level being the scene graph, afaik)
02/11/2007 (7:22 pm)
@Brian, yeah, I was thinking of suggesting "onAdd" but I wasn't sure when that was called ... and as most of my C++ experience thus far was in adding methods to existing classes, that returned instances of new objects ... I knew that registerObject had to be called ... But if onAdd is specific to the scenegraph, then it would not be useful for non-sceneobject classes such as TCPObject, etc ... so again, I think were back at "registerObject" for an "all purpose object constructor callback"
All depends on his needs though, I guess -- if he just wants an "on added to scenegraph" callback, I believe onLevelLoaded may do the trick -- IF it's called whenever an object is loaded into the level (level being the scene graph, afaik)
#5
02/11/2007 (8:09 pm)
OnAdd is called when the object is constructed. onLevelLoaded is called when the object is added to the scenegraph. I frequently use onAdd for constructor type initialization.
#6
02/11/2007 (8:49 pm)
Hrm, I didn't realize "onAdd" was actually exposed as a Callback ... haha -- guess all my discussion in this thread was pointless, oh well ... ;)
#7
This is a step (or possibly two) after the c++ constructor is called, and means that the object now has a SimID and possibly a by name reference in the simulation lookup hash. Subtle but important difference.
02/11/2007 (8:52 pm)
Just to be pedantic, the ::onAdd() callback is called when the object is successfully registered with the simulation.This is a step (or possibly two) after the c++ constructor is called, and means that the object now has a SimID and possibly a by name reference in the simulation lookup hash. Subtle but important difference.
#8
Pedanticalness is important when dealing with code. Thanks for the distinction.
02/11/2007 (8:59 pm)
@StephenPedanticalness is important when dealing with code. Thanks for the distinction.
#9
Then you can do in script:
Note that processArguments() is called after the name is assigned and any fields are copied from the parent object, but before the object is registered with the sim - thus, it has no ID yet and (i think) none of the fields specified with new will be set. Also note that although the name is assigned, it's not really usable for lookup purposes until the object is registered with the sim.
This is not really used much in the engine and is of pretty limited use, but you can do some interesting things with it.
T.
Edit: Damn code that looked like markup and screwed everything up ... ;)
02/11/2007 (10:55 pm)
Just for the sake of a complete discussion, if you're using a custom C++ object then you also have other options. For c'tor style initialization in C++ 99.9% of the time either the c'tor, onAdd or both are used ... but there's one more little known method you can override that lets you do some nifty stuff. Here's an example:bool FooObject::processArguments(S32 argc, const char **argv)
{
for(S32 i = 0; i < argc; i++)
{
Con::printf("FooObject::processArguments - Got Argument \"%s\"", argv[ i ]);
}
return true;
}Then you can do in script:
new FooObject(Name : Parent, Arg1, Arg2, ... ArgN);
Note that processArguments() is called after the name is assigned and any fields are copied from the parent object, but before the object is registered with the sim - thus, it has no ID yet and (i think) none of the fields specified with new will be set. Also note that although the name is assigned, it's not really usable for lookup purposes until the object is registered with the sim.
This is not really used much in the engine and is of pretty limited use, but you can do some interesting things with it.
T.
Edit: Damn code that looked like markup and screwed everything up ... ;)
Associate David Higgins
DPHCoders.com
However, on the C++ side of things, you could probably create a new callback fairly easily ... you'd have to create the callback AFTER the object has been registered ...
It would appear that the most likely place to put such a callback would be in the 'registerObject' method -- or at the end of the SimObject method calls "registerObject" on itself -- I looked briefly, but did not find where SimObject attempted to register itself ... as this is most likely an internal thing done by the TorqueScript parser when an object is created by it ... as all objects created in C++ have to call registerObject explicitly to be passed back to TorqueScript ...
So yeah ... sift around in the C++ and see if you can find a good place to toss it in ... it's more or less fairly simply, you just put something like "Con::executef("callBack");" in the code ... it's a bit more detailed then that, but it's more or less that simple ...
Here's an example of a C++ Object I made, specifically one of it's methods that makes a 'callback':
void XMLObject::processingInstruction(const char *target, const char *data) { Con::executef(this, 4, "onProcessingInstruction", target, data); }Whenever I call this C++ method, it generates a TorqueScript callback and passes the value of 'target' and 'data' to it ... and the 'this' parameter just means the callback is specific to the object itself ... and makes it look for Object, Class and SuperClass versions of the method ... the value 4 is just the number of arguments passed to the callback + 2 ... the 2 is required, even if the callback expects no parameters as the callback itself and the object reference are always passed (internal engine thing) ...
So ... putting Con::executef(this, 2, "onCreated"); in the registerObject in simMananger.cc ... or somewhere else that might make more sense (follow the trail of creation?) ... you could then have:
function myObjectNameClassOrSuperClass::onCreated(%this) { echo("My object was created ... "); }Hope this helps ...