Game Development Community

Handles in C++

by Bucko · in Torque Game Builder · 04/28/2005 (1:21 pm) · 10 replies

How do I get a pointer to an actual C++ object from its "handle" in TScript?
I am pretty sure this is an easy question but I cannot find it. The forums are appalingly unsearcheable for me. When I write something to search for in Forums I usually get nothing (shouldn't they be called Fora btw).

#1
04/28/2005 (1:24 pm)
You cannot access memory directly in TGEScript, so the actual pointer for what the object ID is the "handle" of doesn't make much sense.

There is nothing you can 'do' with a pointer in TGEScript either--it would just be a number.

What is it exactly you are trying to do--what are your requirements that led you to thinking you should have a pointer?
#2
04/28/2005 (1:54 pm)
I want to do some stuff in C++ and then (through console commands as per usual) give "handles" to TScript objects from TScript.
$enemy= new fxAnimatedSprite2D()
{
scenegraph = t2dSceneGraph;
};
$enemy2= new fxAnimatedSprite2D()
{
scenegraph = t2dSceneGraph;
};

$MyAI = MyCPlusPlusAiManager();
$MyAI.Add($enemy);
$MyAI.Add($enemy2);

The ai manager will now service $enemy and $enemy2 through $MyAI.Service();

What I want is a way to get access to the actual C++ fxAnimatedSprite2D objects from the $Enemy and $Enemy2 supplied.
#3
04/28/2005 (2:40 pm)
Torque script only works with object ID's--as I mentioned above, it has absolutely no concept of pointers. To access an object in script, you simply use the object id itself. When you pass objects to the c++ side of things, you pass it the object id (you can also pass it the object name in some cases), and then use a Sim::findObject to return a pointer within C++ of the object.

Sorry for the cut/paste, but here is some documentation from simBase.h about SimObjects, from which most every game object is derived (eventually!):

//--------------------------------------------------------------------------- 
/// Base class for objects involved in the simulation.
///
/// @section simobject_intro Introduction
///
/// SimObject is a base class for most of the classes you'll encounter
/// working in Torque. It provides fundamental services allowing "smart"
/// object referencing, creation, destruction, organization, and location.
/// Along with SimEvent, it gives you a flexible event-scheduling system,
/// as well as laying the foundation for the in-game editors, GUI system,
/// and other vital subsystems.
///
/// @section simobject_subclassing Subclassing
///
/// You will spend a lot of your time in Torque subclassing, or working
/// with subclasses of, SimObject. SimObject is designed to be easy to 
/// subclass.
///
/// You should not need to override anything in a subclass except:
///     - The constructor/destructor.
///     - processArguments()
///     - onAdd()/onRemove()
///     - onGroupAdd()/onGroupRemove()
///     - onNameChange()
///     - onStaticModified()
///     - onDeleteNotify()
///     - onEditorEnable()/onEditorDisable()
///     - inspectPreApply()/inspectPostApply()
///     - things from ConsoleObject (see ConsoleObject docs for specifics)
///
/// Of course, if you know what you're doing, go nuts! But in most cases, you
/// shouldn't need to touch things not on that list.
///
/// When you subclass, you should define a typedef in the class, called Parent,
/// that references the class you're inheriting from. 
///
/// @code
/// class mySubClass : public SimObject {
///     typedef SimObject Parent;
///     ...
/// @endcode
///
/// Then, when you override a method, put in:
///
/// @code
/// bool mySubClass::onAdd()
/// {
///     if(!Parent::onAdd())
///         return false;
///
///     // ... do other things ...
/// }
/// @endcode
///
/// Of course, you want to replace onAdd with the appropriate method call.
///
/// @section simobject_lifecycle A SimObject's Life Cycle
/// 
/// SimObjects do not live apart. One of the primary benefits of using a
/// SimObject is that you can uniquely identify it and easily find it (using
/// its ID). Torque does this by keeping a global hierarchy of SimGroups -
/// a tree - containing every registered SimObject. You can then query
/// for a given object using Sim::findObject() (or SimSet::findObject() if
/// you want to search only a specific set).
///

(con't)
#4
04/28/2005 (2:43 pm)
And here's a quick example of how to do what I think you are talking about, within a console method:

ConsoleMethod(fxSceneGraph2D, addToScene, void, 3, 3, "(fxSceneObject2D) - Add fxSceneObject2D to Scene.")
{
	// Find fxSceneObject2D Object.
	fxSceneObject2D* pSceneObject2D = static_cast<fxSceneObject2D*>(Sim::findObject(argv[2]));

	// Validate Object.
	if ( !pSceneObject2D )
	{
		Con::warnf("fxSceneGraph2D::addToScene - Couldn't find object '%s'.", argv[2]);
		return;
	}

	// Add to Scene.
	object->addToScene( pSceneObject2D );
}

Note the declaration and initialization of the pSceneObject2D at the beginning: it declares the variable as a pointer to an fxSceneObject2D, and then initializes it to a static cast of whatever Sim::findObject(argv[2])) is--in this case, argv[2] is going to be the object ID that is passed by the TGEScript layer.
#5
04/28/2005 (2:58 pm)
Forgot to add: to return an object id to the script layer, you simply return the object's ->getId() access method output:

return pSceneObject2D->getId();

in the above example.
#6
04/28/2005 (3:14 pm)
Thanks for the swift and very much to the point reply. It constantly amazes me how you guys can pounce so fast all the time. Thanks.
#7
04/28/2005 (3:59 pm)
How costly is actually the Sim::findObject() call?
As fxSceneGraph2D can be deleted at any time it would perhaps be reasonable to store the ID and get the object each timeI use it (unless the Sim::findObject() is too expensive). Or do I get smartpointerish behaviour from the fxSceneGraph2D* as it derives from SimObject?
#8
04/28/2005 (5:36 pm)
I don't mean to detract, but I couldn't resist: You're right, it should be fora...if we were speaking Latin. But English doesn't have to bend to its will, even though it has many times over the past 1000 years:)
#9
04/29/2005 (1:56 am)
Perhaps for the unwashed masses of the colonies, but for a gentleman of good breeding, Latin is still the norm.
#10
04/29/2005 (1:56 am)
@Bucko: Using "Sim::findObject(const char*)" takes more time to process than using "Sim::findObject(SimObjectId)" as it allows you to pass either an object name or an object id and is more flexible that way. It finds the id on the premise that names cannot begin numerically. You'd probably want to use the direct 'SimObjectId' lookup.

The lookup for an ID is very fast and is based upon a cyclic lookup-table using the ID as an index, like a hash. The search is done using the following...
SimObject* SimIdDictionary::find(S32 id)
{
   S32 idx = id & TableBitMask;
   SimObject *walk = table[idx];
   while(walk)
   {
      if(walk->getId() == U32(id))
         return walk;
      walk = walk->nextIdObject;
   }
   return NULL;
}
... so it is very reasonable to use it. If you want to get direct references then you can use safe-referencing in SimObject. Each SimObject has a "registerReference()" / "unregisterReference()" call that allow you to pass your own pointer so that the SimObject will reset that pointer to NULL if it gets destroyed.

- Melv.