Game Development Community

Creating a SimObject in C++

by baylor wetzel · in Torque Game Builder · 08/27/2009 (2:51 pm) · 3 replies

My C++ object works if it's created in TorqueScript. If i create it in C++, however, the ID is 0. There must be some kind of initialization needed but i don't know what it is

My console function:
ConsoleFunction(createEnsemble, S32, 1, 1, "().")
{
	EnsembleWrapper* ensemble = new EnsembleWrapper();

Con::printf("Created a new ensemble in C++!");
Con::printf("  getID()           = %d", ensemble->getId());
Con::printf("  getIdString()     = %s", ensemble->getIdString());
Con::printf("  getClassName()    = %s", ensemble->getClassName());
Con::printf("  getInternalName() = %s", ensemble->getInternalName());
Con::printf("  getName()         = %s", ensemble->getName());
Con::printf("  isProperlyAdded() = %d", ensemble->isProperlyAdded());

//--- Don't know what this does but what the hell, let's try it
Con::printf("calling initPersistFields()");
ensemble->initPersistFields();
Con::printf("OK, values once more:");
Con::printf("  getID()           = %d", ensemble->getId());
Con::printf("  getIdString()     = %s", ensemble->getIdString());
Con::printf("  getClassName()    = %s", ensemble->getClassName());
Con::printf("  getInternalName() = %s", ensemble->getInternalName());
Con::printf("  getName()         = %s", ensemble->getName());
Con::printf("  isProperlyAdded() = %d", ensemble->isProperlyAdded());

	return ensemble->getId();
}

Results:
calling initPersistFields()
OK, values once more:
  getID()           = 0
  getIdString()     = 0
  getClassName()    = EnsembleWrapper
  getInternalName() = (null)
  getName()         = (null)
  isProperlyAdded() = 0

What am i doing wrong?

The objects create just fine if i create them from TorqueScript. Interesting thing i learned - you cannot pass arguments to the constructor. Well, you can try but no matter what you do, you get the default constructor. For example:

C++
EnsembleWrapper()
{
	Con::printf("default constructor");
	age = 12;
};
EnsembleWrapper(S32 x, S32 y)
{
	Con::printf("constructor EnsembleWrapper(S32 %d, S32 %d)", x, y);
	age = 37;
	sum = x + y;
}
ConsoleMethod(EnsembleWrapper, showAndTell, void, 2, 2, "().")
{
	Con::printf("age=[%d] sum=[%d]", object->age, object->sum);
}

TS
$e = new EnsembleWrapper(2, 3);
  prints "default constructor"
$e.showAndTell();
  prints "age=[12] sum=[-800000]"


#1
08/27/2009 (5:04 pm)
After:
EnsembleWrapper* ensemble = new EnsembleWrapper();
add:
ensemble->registerObject();
You can specify the name as a param.
#2
08/27/2009 (5:28 pm)
i saw registerObject but only the version where you had to pass in an ID - i didn't notice it had 4 variants. Now my code works great. Thanks!
#3
08/27/2009 (11:06 pm)
Since you were asking about the constructor arguments, I thought I'd throw in the following. First, as you've observed, when an object is created, the no-argument C++ constructor is always used. TorqueScript apparently does support constructor arguments, but hardly any classes use them. If you really want to use them, you can override bool processArguments(S32 argc, const char** argv).

The more typical way that construction arguments are passed are via named fields. In initPersistFields(), you tell Torque what your public fields are by calling addField() or addProtectedField(), and then in TorqueScript do something like:
new MyObject() {
   someField = "someValue";
   anotherField = "anotherValue";
   yetAnotherField = "yetAnotherValue";
};

registerObject() calls onAdd(), which is where your main initialization code should be. In the case of construction via TorqueScript, this is called AFTER all those fields above are initialized, which gives you access to that data for creation.

So, to recap, the order of events for creation of an object via TS is:

1. C++ no-arg constructor: Here you should initialize your field values to sensible defaults, but you probably should not do much in the way of any other initialization.
2. processArguments(): Processes constructor arguments from TS. Very rarely used; probably since most of the persistent storage tools (level builders) don't write any constructor arguments to their script files.
3. setDataField(): Assigns field values. If you're creating an object for TS from C++, you can generally just use the C++ interface for setting these values rather than using setDataField().
4a. registerObject(): Assigns the object ID, and then calls...
4b. onAdd(): Does the actual initialization based on the values stored in steps 2 and 3.

Please also remember that these steps can fail. If processArguments() returns false or onAdd() (and therefore registerObject()) returns false, TS deletes the object immediately afterwards. It's probably wise to do the same if you're calling registerObject() yourself from C++.