Hi everybody,
Me and my team are currently developing a game using bullet physics. The genre of the is basically a tower defense. Well, I'm trying to use characters controllers for IA purposes, because using only rigidBody makes unstable behavior between IA's and the world.
I thought in configure the world to avoid this penetrations between rigid bodies, etc.
But finally I decided to use the KinematicCharacterController class. It works fine, but this is my problem:
-Before I take this decision, I implemented triggers for the game purposes using btPairCachingGhostObject. This works like a charm when I was using ridigBodies, the behavior was the expected. But well, when I change the IA physic part from rigidBodies to characterController (it uses internally a btPairCachingGhostObject, too), the behavior wasn't the expected.
After this little introduction I ask now:
-Is the collision between btPairCachingGhostObject provided?
Basically I'm using the next methods to detect the collision when a body joins into the trigger:
Code: Select all
//---------------------------------------------------------
void CManualTrigger::TriggerTick()
{
btDiscreteDynamicsWorld* m_dynamicsWorld = CPhysicEngine::Ref().GetbtSoftDynamicWorld();
//Retrives the content from all ghost objects in the world and call ProcessObectsInsideGhostObjects(...) for each.
if ( m_dynamicsWorld )
{
btAlignedObjectArray < btCollisionObject* > objsInsidePairCachingGhostObject; // We might want this to be a member variable...
btAlignedObjectArray < btCollisionObject* >* pObjsInsideGhostObject = NULL; // We will store a reference of the current array in this pointer
const btAlignedObjectArray < btCollisionObject* >& objs = m_dynamicsWorld->getCollisionObjectArray();
for (int i=0,sz=objs.size();i<sz;i++)
{
btCollisionObject* o = objs[i];
btGhostObject* go = btGhostObject::upcast(o);
if (go)
{
objsInsidePairCachingGhostObject.resize(0);
btPairCachingGhostObject* pgo = dynamic_cast < btPairCachingGhostObject* > (go); // No upcast functionality...
if (pgo)
{
GetCollidingObjectsInsidePairCachingGhostObject(static_cast < btDiscreteDynamicsWorld* > (m_dynamicsWorld),pgo,objsInsidePairCachingGhostObject);
pObjsInsideGhostObject = &objsInsidePairCachingGhostObject;
}
else
{
// It's better not to try and copy the whole array, but to keep a reference to it!
pObjsInsideGhostObject = &go->getOverlappingPairs();
// Side Note: (at the moment) makes my program crash on my system...
//btAlignedObjectArray < btCollisionObject* > objs = go->getOverlappingPairs();
// Nevermind, that was the wrong way of doing it:
//btAlignedObjectArray < btCollisionObject* >& objs2 = go->getOverlappingPairs();
//is much better.
}
// Here pObjsInsideGhostObject should be valid.
ProcessObectsInsideGhostObjects(*pObjsInsideGhostObject, pgo);
}
}
}
}
//---------------------------------------------------------
// This static method is useful for the demo only. It's called by "void GhostObjectsDemo::clientMoveAndDisplay()".
// Basically we must find a way to "display" the objects inside ghost objects to the user.
// We choose to apply a vertical impulse to them (just because it's easier).
// (The strength of the impulse is different depending on the type of ghost object)
void CManualTrigger::ProcessObectsInsideGhostObjects(btAlignedObjectArray < btCollisionObject* >& objs, const bool isPairCachingGhostObject)
{
//Primer paso: Comprobando colisiones e insertando los nuevos objetos que no estaban ya insertados en el mapa de colisiones.
for (int j=0,jsz=objs.size();j<jsz;j++)
{
btPairCachingGhostObject* b = dynamic_cast < btPairCachingGhostObject* >(objs[j]);
//This line was the original one, before I change by the line above.
//btRigidBody* c = btRigidBody::upcast(objs[j]);
if (b)
{
//Obtenemos la informacion que relaciona bullet con la logica (TActorInfo).
TActorInfo *info = (TActorInfo *)b->getUserPointer();
//Obtenemos el componente entidad fisica al que pertenece el rigidBody 'b'.
CPhysicEntity *physicEntity = reinterpret_cast<CPhysicEntity*> (info->pPhysicObj->userData);
//Obtenemos la entidad logica que contiene el componente anterior.
CEntity *entityCollisioned = physicEntity->getEntity();
if (entityCollisioned->getType() == "StuffedAnimal" || entityCollisioned->getType() == "Player")
{
//Primero comprobamos que el objeto no conste ya en el mapa de objetos colisionados con el trigger.
_it = _objectsColliding.find(b);
if (_it == _objectsColliding.end())
{
//Si no estuviera, lo insertamos.
_objectsColliding.insert(std::pair<btCollisionObject*,bool>(b, true));
//Y mandamos el mensaje oportuno.
//Dependiendo de la trampa, habrá un comportamiento u otro.
doAction(entityCollisioned);
}
}
}
}
//Si hay algo en contacto con el trigger.
if (objs.size() > 0)
{
//Segundo paso: Marcando en el mapa de colisiones los objetos que ya no esten colisionando con el trigger.
for (_it = _objectsColliding.begin(); _it != _objectsColliding.end(); ++_it)
{
//Si la busqueda devuelve un entero igual al tamanyo del map objs, es que no se ha encontrado el elemento.
if ( objs.findLinearSearch(_it->first) == objs.size() )
{
_it->second = false;
}
}
//Tercer paso: Notificar a las entidades que ya no estan colisionando con el trigger con su correspondiente mensaje.
notifyingOnExitEntities();
}
}
//---------------------------------------------------------
// Portable static method: prerequisite call: m_dynamicsWorld->getBroadphase()->getOverlappingPairCache()->setInternalGhostPairCallback(new btGhostPairCallback());
void CManualTrigger::GetCollidingObjectsInsidePairCachingGhostObject(btDiscreteDynamicsWorld* m_dynamicsWorld,
btPairCachingGhostObject* m_pairCachingGhostObject,
btAlignedObjectArray<btCollisionObject*>& collisionArrayOut)
{
collisionArrayOut.resize(0);
if (!m_pairCachingGhostObject || !m_dynamicsWorld)
return;
// With "false" things don't change much, and the code is a bit faster and cleaner...
const bool addOnlyObjectsWithNegativeDistance(true);
btBroadphasePairArray& collisionPairs = m_pairCachingGhostObject->getOverlappingPairCache()->getOverlappingPairArray();
const int numObjects=collisionPairs.size();
static btManifoldArray m_manifoldArray;
bool added;
for(int i=0;i<numObjects;i++)
{
m_manifoldArray.resize(0);
added = false;
for (int j=0;j<m_manifoldArray.size();j++)
{
btPersistentManifold* manifold = m_manifoldArray[j];
// Here we are in the narrowphase, but can happen that manifold->getNumContacts()==0:
if (addOnlyObjectsWithNegativeDistance)
{
for (int p=0,numContacts=manifold->getNumContacts();p<numContacts;p++)
{
const btManifoldPoint&pt = manifold->getContactPoint(p);
if (pt.getDistance() < 0.0)
{
// How can I be sure that the colObjs are all distinct ? I use the "added" flag.
collisionArrayOut.push_back((btCollisionObject*) (manifold->getBody0() == m_pairCachingGhostObject ? manifold->getBody1() : manifold->getBody0()));
added = true;
break;
}
}
if (added)
break;
}
else if (manifold->getNumContacts()>0)
{
collisionArrayOut.push_back((btCollisionObject*) (manifold->getBody0() == m_pairCachingGhostObject ? manifold->getBody1() : manifold->getBody0()));
break;
}
}
}
}
So, Is this possible?, I mean, Can we detect collision between characters controllers (between GhostObjects)?. This would be useful too, to avoid the overlapping between two or more btKinematicCharacterControllers.
Thanks!