Game Development Community

dev|Pro Game Development Curriculum

Access prefab Sub Objects from torquescript

by Andrew Edmonds · 01/30/2010 (3:13 pm) · 4 comments

I use a prefab that I have created containing one object and 8 lights. I wanted a way to turn the lights on and off in-game and couldn't find a way to access the sub objects of a prefab through script (without exploding the prefab). These functions will allow you to access any object inside of a prefab.

Usage:
%prefab.getSubObjectCount() -- returns the number of objects inside the prefab
%prefab.getSubObject(%objectIndex) -- returns the id of the object at the specified index

The code:
In T3D\prefab.h at line 72 (in public declarations), add

// getSubObjectCount and getSubObject >>>
S32 getSubObjectCount();
S32 getSubObject(S32 subObjectIndex);
// getSubObjectCount and getSubObject <<<


Add to the end of T3D\prefab.cpp:

// getSubObjectCount and getSubObject >>>
S32 Prefab::getSubObjectCount()
{
   if (!mChildGroup) {
      Con::errorf("Not a prefab or no subObjects found.");
      return NULL;
   }

   SimGroup *group = mChildGroup;
   Vector<SceneObject*> foundObjects;

   group->findObjectByType(foundObjects);

   return foundObjects.size();
}

ConsoleMethod(Prefab, getSubObjectCount, S32, 2, 2, "prefab.getSubObjectCount() returns the number of subObjects inside the prefab.")

{
   TORQUE_UNUSED(argc); TORQUE_UNUSED(argv);

   return object->getSubObjectCount();

}

S32 Prefab::getSubObject(S32 subObjectIndex)
{
   if (!mChildGroup) {
      Con::errorf("Not a prefab or no subObjects found.");
      return NULL;
   }

   SimGroup *group = mChildGroup;
   Vector<SceneObject*> foundObjects;

   group->findObjectByType(foundObjects);

   S32 subObjectsCount = foundObjects.size();

   if (subObjectIndex >= subObjectsCount) {
      Con::errorf("%i out of bounds.", subObjectIndex);
      return NULL;
   }

   SceneObject *child = foundObjects[subObjectIndex];
   return child->getId();
}

ConsoleMethod(Prefab, getSubObject, S32, 3, 3, "prefab.getSubObject(index) returns an object within the prefab.")
{
   TORQUE_UNUSED(argc);
   S32 subObjectIndex = dAtoi(argv[2]);
   return object->getSubObject(subObjectIndex);
}
// getSubObjectCount and getSubObject <<<

Recompile and you're done!

About the author

Formed in 2005, EiKON Games is an indie games development project based in the UK working on the tactical first person shooter "Epoch: Incursion". See the Join Us or Contact Us pages at http://www.eikon-games.com/


#1
01/30/2010 (3:22 pm)
Awesome, thanks! This should be stock!
#2
03/13/2010 (7:32 pm)
The next release will include a Prefab::onLoad( %this, %simgroup ) callback where %simgroup contains all child objects of the prefab. I'll also add %prefab.getChildGroup() or something similar.
#3
05/19/2010 (9:24 pm)
This is very useful! Something it led me to was the need to get the prefab by the sub object's ID. What I did to make that work were the following changes...

In prefab.h at the end of the prefab class make the last two members public, by changing:
/// Lookup from a child object's id to its transform in 
   /// this Prefab's object space.
   ChildToMatMap mChildMap;

   typedef Map<SimObjectId,SimObjectId> ChildToPrefabMap;

   /// Lookup from a SimObject to its parent Prefab if it has one.
   static ChildToPrefabMap smChildToPrefabMap;
};

To:
/// Lookup from a child object's id to its transform in 
   /// this Prefab's object space.
   ChildToMatMap mChildMap;

public:  //<<<--Insert this

   typedef Map<SimObjectId,SimObjectId> ChildToPrefabMap;

   /// Lookup from a SimObject to its parent Prefab if it has one.
   static ChildToPrefabMap smChildToPrefabMap;
};

And insert the following at the end of prefab.cpp:
ConsoleFunction(getPrefabByChildID, S32, 2, 2, "getPrefabByChildID(id) returns the object's owning prefab.")
{
   TORQUE_UNUSED(argc);
   S32 subObjectIndex = dAtoi(argv[1]);

	Prefab::ChildToPrefabMap::Iterator itr = Prefab::smChildToPrefabMap.find( subObjectIndex );
   if ( itr == Prefab::smChildToPrefabMap.end() )
      return NULL; 

   Prefab *prefab;
   if ( !Sim::findObject( itr->value, prefab ) )
   {
      Con::errorf( "Prefab::getPrefabByChildID - child object mapped to a prefab that no longer exists." );
      return NULL;
   }

	return prefab->getId();
}

I'm not sure if exposing those ChildToPrefabMap variables has any negative side-effects, though in using it I haven't noticed anything detrimental.
#4
12/10/2012 (4:26 pm)
This can also be done only with torqueScript in T3D 1.2 (MIT) :
Create scripts/server/prefabsExt.cs
function Prefab::onLoad(%prefab,%sim)
{
   %prefab.TheSimGroup = %sim;
}

function Prefab::getSubObjectCount(%prefab)
{
   return %prefab.TheSimGroup.getObjectCount();
}

function Prefab::getSubObject(%prefab,%idx)
{
   return %prefab.TheSimGroup.getObject(%idx);
}
And in scripts/server/game.cs, after execs, add
exec("scripts/server/prefabsExt.cs");
That's done.