2012-06-24 7 views
7

Intento utilizar Bullet Physics solo para detección de colisión. No necesito mover ningún objeto para mí ni manejar la representación con devoluciones de llamada. Solo quiero actualizar las ubicaciones de los objetos en cada cuadro y usarlo para decirme cuándo tengo colisiones. Para que funcione el ejemplo más simple, estoy tratando de encontrar colisiones entre los objetos con btBoxShape como su forma. Todo funciona bien sin bloqueos o pérdidas de memoria aparentes, pero no tengo colisiones, así que debo cometer algunos errores en alguna parte. Trataré de mantener esto lo más breve posible sin dejar nada importante.Ejemplo simple de colisión de física de bala

Aquí es mi función de configuración mundo:

collisionConfig  = new btDefaultCollisionConfiguration(); 
dispatcher   = new btCollisionDispatcher(collisionConfig); 
overlappingPairCache = new btDbvtBroadphase(); 
solver    = new btSequentialImpulseConstraintSolver; 
dynamicsWorld  = new btDiscreteDynamicsWorld(dispatcher, 
overlappingPairCache, solver, collisionConfig);   

dynamicsWorld->setGravity(btVector3(0.0f, -9.8f, 0.0f)); 

Ahora mismo tengo los jugadores y enemigos objetos del tipo btCollisionObject *. Los estoy configuración de la siguiente manera:

mPlayerBox = new btBoxShape(btVector3(1,3,1)); 
mPlayerObject = new btCollisionObject(); 
mPlayerObject->setCollisionShape(mPlayerBox); 
btTransform playerWorld; 
playerWorld.setIdentity(); 
//playerPos is a D3DXVECTOR3 that holds the camera position. 
playerWorld.setOrigin(btVector3(playerPos.x, playerPos.y, playerPos.z)); 
mPlayerObject->setWorldTransform(playerWorld); 
mPlayerObject->forceActivationState(DISABLE_DEACTIVATION);//maybe not needed 
dynamicsWorld->addCollisionObject(mPlayerObject); 

hago esencialmente lo mismo con mis objetos enemigos.

Entonces cada cuadro actualizo todos mis objetos con algo como esto:

btTransform updatedWorld; 
updatedWorld.setIdentity(); 
updatedWorld.setOrigin(btVector3(position.x, position.y, position.z)); 
mPlayerObject->setWorldTransform(updatedWorld); 

//do the same for my enemies, and then... 

dynamicsWorld->performDiscreteCollisionDetection(); 
//Also tried doing this with stepSimulation(deltaTime, 7), but nothing changed. 
//stepSimulation seems to only be for letting Bullet set world Transforms? 

//check collisions with player 
dynamicsWorld->contactTest(mPlayerObject, resultCallback); 
int numManifolds = dynamicsWorld->getDispatcher()->getNumManifolds(); 
if(numManifolds > 0) 
{ 
    //there's a collision, execute blah blah blah 
} 

Y, por último, aquí es la estructura que define mi resultado de devolución de llamada:

struct rCallBack : public btCollisionWorld::ContactResultCallback 
{ 
btScalar rCallback::addSingleResult(btManifoldPoint& cp, const btCollisionObject* 
colObj0, int partId0, int index0, const btCollisionObject* colObj1, int partId1, 
int index1) 
{ 
    btVector3 ptA = cp.getPositionWorldOnA(); 
    btVector3 ptB = cp.getPositionWorldOnB(); 
    return 0; 
} 
} 

He mirado en un montón de los demos, pero parecen estar dejando el movimiento a Bullet, y como estoy moviendo personajes a una velocidad establecida sin ninguna física especial cuando colisionan, tuve problemas para adaptar los ejemplos a mi aplicación. La devolución de llamada del resultado en realidad vino de esta publicación en los foros: http://bulletphysics.org/Bullet/phpBB3/viewtopic.php?t=6816 Se trata de usar mallas triangulares, pero parecía estar más cerca de lo que estaba tratando de implementar.

De todos modos, si lees hasta aquí, ¡gracias! Cualquier consejo o enlace que pueda tener sería muy apreciado.

+3

"Solo quiero actualizar las ubicaciones de los objetos en cada cuadro y usarlo para decirme cuándo tengo colisiones". Eso es generalmente antitético de cómo funciona un sistema * física *. Deberías intentar trabajar con tu motor de física, no * en contra de * it. Si tienes personajes que se mueven a una velocidad establecida, entonces realmente deberías dejar que tu sistema de física los mueva. Puede hacerlo bien. –

+0

sí, me gustaría poder etiquetar esto como la respuesta. Después de leer esto y observarlo un poco más, me di cuenta de que podía hacer la detección de colisiones usando volúmenes de encuadernación y algunas matemáticas. ¡Gracias! – Aztal

+0

¿Cuál es la entrada y la salida? P. ej .: para cada cuadro Entrada = posición + velocidad (por paso) de todos los objetos Salida = ¿qué pares de objetos colisionaron en qué punto del espacio? ¿Y actualiza manualmente las posiciones/velocidades usando un método personalizado? –

Respuesta

4

Estoy escribiendo una aplicación IOS con flighter disparando entre sí en la escena 3D. Uso la física de bala para la detección de colisiones Configuro el cañón como objeto cinemático, mi lógica mueve el cañón y luego actualiza el btMotionState worldTransform del objeto cinemático. Asimismo, no consigo ningún detección de colisiones hasta que cambie las dos afirmaciones siguientes (establecidos el enmascaramiento y el grupo de la misma para el jugador y el enemigo)

dynamicsWorld->addRigidBody(mPlayerObject,1,1); 
dynamicsWorld->addRigidBody(mEnemyObject,1,1); 
... 
dynamicsWorld->setInternalTickCallback(myTickCallback); 

entonces puedo ver el

void myTickCallback(btDynamicsWorld *world, btScalar timeStep) { 
    int numManifolds = world->getDispatcher()->getNumManifolds(); 
    printf("numManifolds = %d\n",numManifolds); 
} 

El valor numManifolds se convierte en 1 cuando el objeto colisiona.

1

Puede comprobar la información de contacto como se explica here:

Información de contacto

La mejor manera de determinar si las colisiones ocurrieron entre objetos existentes en el mundo, es iterar sobre todos los contactos colectores. Este se debe realizar durante una devolución de llamada de simulación (subespaciado), porque los contactos se pueden agregar y eliminar durante varios subetapas de una llamada de simulación de un solo paso .Un colector de contacto es un caché que contiene todos los puntos de contacto entre pares de objetos de colisión. Una buena forma de hacerlo es iterar sobre todos los pares de objetos en todo el mundo /dinámica de colisión:

//Assume world->stepSimulation or world->performDiscreteCollisionDetection has been called 

    int numManifolds = world->getDispatcher()->getNumManifolds(); 
    for (int i=0;i<numManifolds;i++) 
    { 
     btPersistentManifold* contactManifold = world->getDispatcher()->getManifoldByIndexInternal(i); 
     btCollisionObject* obA = static_cast<btCollisionObject*>(contactManifold->getBody0()); 
     btCollisionObject* obB = static_cast<btCollisionObject*>(contactManifold->getBody1()); 

     int numContacts = contactManifold->getNumContacts(); 
     for (int j=0;j<numContacts;j++) 
     { 
      btManifoldPoint& pt = contactManifold->getContactPoint(j); 
      if (pt.getDistance()<0.f) 
      { 
       const btVector3& ptA = pt.getPositionWorldOnA(); 
       const btVector3& ptB = pt.getPositionWorldOnB(); 
       const btVector3& normalOnB = pt.m_normalWorldOnB; 
      } 
     } 
    } 

Usted puede estar interesado en btGhostObject que realiza un seguimiento de sus propios pares superpuestos.

0

Minimal ejemplo runnable

Una esfera caer y golpear el suelo.

Las colisiones se detectan e imprimen a stdout.

Gnuplot visualización:

La línea de "colisión" va a 1 cada vez que la esfera toca el suelo.

Y para los coeficientes de restitución más pequeños (y 0.50.5):

Aquí la bola se detiene por completo y saltar toca el suelo de forma continua.

Código:

#include <cstdio> 
#include <cstdlib> 
#include <vector> 

#include <btBulletDynamicsCommon.h> 

#define PRINTF_FLOAT "%7.3f" 

constexpr float gravity = -10.0f; 
constexpr float initialY = 10.0f; 
constexpr float timeStep = 1.0f/60.0f; 
// TODO some combinations of coefficients smaller than 1.0 
// make the ball go up higher/not lose height. Why? 
constexpr float groundRestitution = 0.9f; 
constexpr float sphereRestitution = 0.9f; 
constexpr int maxNPoints = 500; 

std::vector<btVector3> collisions; 
void myTickCallback(btDynamicsWorld *dynamicsWorld, btScalar timeStep) { 
    collisions.clear(); 
    int numManifolds = dynamicsWorld->getDispatcher()->getNumManifolds(); 
    for (int i = 0; i < numManifolds; i++) { 
     btPersistentManifold *contactManifold = dynamicsWorld->getDispatcher()->getManifoldByIndexInternal(i); 
     // TODO those are unused. What can be done with them? 
     // I think they are the same objects as those in the main loop 
     // dynamicsWorld->getCollisionObjectArray() and we could compare 
     // the pointers to see which object collided with which. 
     { 
      const btCollisionObject *objA = contactManifold->getBody0(); 
      const btCollisionObject *objB = contactManifold->getBody1(); 
     } 
     int numContacts = contactManifold->getNumContacts(); 
     for (int j = 0; j < numContacts; j++) { 
      btManifoldPoint& pt = contactManifold->getContactPoint(j); 
      const btVector3& ptA = pt.getPositionWorldOnA(); 
      const btVector3& ptB = pt.getPositionWorldOnB(); 
      const btVector3& normalOnB = pt.m_normalWorldOnB; 
      collisions.push_back(ptA); 
      collisions.push_back(ptB); 
      collisions.push_back(normalOnB); 
     } 
    } 
} 

int main() { 
    int i, j; 

    btDefaultCollisionConfiguration *collisionConfiguration 
      = new btDefaultCollisionConfiguration(); 
    btCollisionDispatcher *dispatcher = new btCollisionDispatcher(collisionConfiguration); 
    btBroadphaseInterface *overlappingPairCache = new btDbvtBroadphase(); 
    btSequentialImpulseConstraintSolver* solver = new btSequentialImpulseConstraintSolver; 
    btDiscreteDynamicsWorld *dynamicsWorld = new btDiscreteDynamicsWorld(
      dispatcher, overlappingPairCache, solver, collisionConfiguration); 
    dynamicsWorld->setGravity(btVector3(0, gravity, 0)); 
    dynamicsWorld->setInternalTickCallback(myTickCallback); 
    btAlignedObjectArray<btCollisionShape*> collisionShapes; 

    // Ground. 
    { 
     btTransform groundTransform; 
     groundTransform.setIdentity(); 
     groundTransform.setOrigin(btVector3(0, 0, 0)); 
     btCollisionShape* groundShape; 
#if 1 
     // x/z plane at y = -1. 
     groundShape = new btStaticPlaneShape(btVector3(0, 1, 0), -1); 
#else 
     // A cube of width 10 at y = -6. 
     // Does not fall because we won't call: 
     // colShape->calculateLocalInertia 
     // TODO: remove this from this example into a collision shape example. 
     groundTransform.setOrigin(btVector3(0, -6, 0)); 
     groundShape = new btBoxShape(
       btVector3(btScalar(5.0), btScalar(5.0), btScalar(5.0))); 

#endif 
     collisionShapes.push_back(groundShape); 
     btDefaultMotionState* myMotionState = new btDefaultMotionState(groundTransform); 
     btRigidBody::btRigidBodyConstructionInfo rbInfo(0, myMotionState, groundShape, btVector3(0, 0, 0)); 
     btRigidBody* body = new btRigidBody(rbInfo); 
     body->setRestitution(groundRestitution); 
     dynamicsWorld->addRigidBody(body); 
    } 

    // Sphere. 
    { 
     btCollisionShape* colShape = new btSphereShape(btScalar(1.0)); 
     collisionShapes.push_back(colShape); 
     btTransform startTransform; 
     startTransform.setIdentity(); 
     startTransform.setOrigin(btVector3(0, initialY, 0)); 
     btVector3 localInertia(0, 0, 0); 
     btScalar mass(1.0f); 
     colShape->calculateLocalInertia(mass, localInertia); 
     btDefaultMotionState *myMotionState = new btDefaultMotionState(startTransform); 
     btRigidBody *body = new btRigidBody(btRigidBody::btRigidBodyConstructionInfo(
       mass, myMotionState, colShape, localInertia)); 
     body->setRestitution(sphereRestitution); 
     dynamicsWorld->addRigidBody(body); 
    } 

    // Main loop. 
    std::printf("step body x y z collision a b normal\n"); 
    for (i = 0; i < maxNPoints; ++i) { 
     dynamicsWorld->stepSimulation(timeStep); 
     for (j = dynamicsWorld->getNumCollisionObjects() - 1; j >= 0; --j) { 
      btCollisionObject *obj = dynamicsWorld->getCollisionObjectArray()[j]; 
      btRigidBody *body = btRigidBody::upcast(obj); 
      btTransform trans; 
      if (body && body->getMotionState()) { 
       body->getMotionState()->getWorldTransform(trans); 
      } else { 
       trans = obj->getWorldTransform(); 
      } 
      btVector3 origin = trans.getOrigin(); 
      std::printf("%d %d " PRINTF_FLOAT " " PRINTF_FLOAT " " PRINTF_FLOAT " ", 
        i, 
        j, 
        float(origin.getX()), 
        float(origin.getY()), 
        float(origin.getZ())); 
      if (collisions.empty()) { 
       std::printf("0 "); 
      } else { 
       std::printf("1 "); 
       // Yes, this is getting reprinted for all bodies when collisions happen. 
       // It's just a quick and dirty way to visualize it, should be outside 
       // of this loop normally. 
       for (auto& v : collisions) { 
        std::printf(
          PRINTF_FLOAT " " PRINTF_FLOAT " " PRINTF_FLOAT " ", 
          v.getX(), v.getY(), v.getZ()); 
       } 
      } 
      puts(""); 
     } 
    } 

    // Cleanup. 
    for (i = dynamicsWorld->getNumCollisionObjects() - 1; i >= 0; --i) { 
     btCollisionObject* obj = dynamicsWorld->getCollisionObjectArray()[i]; 
     btRigidBody* body = btRigidBody::upcast(obj); 
     if (body && body->getMotionState()) { 
      delete body->getMotionState(); 
     } 
     dynamicsWorld->removeCollisionObject(obj); 
     delete obj; 
    } 
    for (i = 0; i < collisionShapes.size(); ++i) { 
     delete collisionShapes[i]; 
    } 
    delete dynamicsWorld; 
    delete solver; 
    delete overlappingPairCache; 
    delete dispatcher; 
    delete collisionConfiguration; 
    collisionShapes.clear(); 
} 

Basado en: http://www.bulletphysics.org/mediawiki-1.5.8/index.php

versión de esta enfocado en distinguir qué objeto tocado qué objeto: https://gamedev.stackexchange.com/a/120881/25171

GitHub aguas arriba: https://github.com/cirosantilli/cpp-cheat/blob/503a3b6487ccb75334798839b5ed912270446d14/bullet/ground_ball.cpp

probado en Bala 2,83 , Ubuntu 15.10.