2012-06-24 21 views
7

Sto cercando di utilizzare Bullet Physics solo per il rilevamento delle collisioni. Non ho bisogno di spostare oggetti per me o gestire il rendering con i callback. Voglio solo aggiornare le posizioni dell'oggetto ogni frame e usarlo per dirmi quando ho delle collisioni. Per ottenere l'esempio più semplice, sto cercando di trovare le collisioni tra gli oggetti con btBoxShape come forma. Tutto funziona bene senza arresti anomali o perdite di memoria apparenti, ma non ho nessuna collisione, quindi devo fare alcuni errori da qualche parte. Cercherò di tenerlo il più breve possibile senza lasciare nulla di importante.Fisica di proiettile Esempio di collisione più semplice

Ecco la mia funzione di configurazione mondo:

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)); 

In questo momento ho dei giocatori e nemici oggetti del tipo btCollisionObject *. Le sto configurando in questo modo:

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); 

Io essenzialmente faccio la stessa cosa con i miei oggetti nemici.

Poi ogni fotogramma posso aggiornare tutti i miei oggetti con qualcosa di simile:

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 
} 

E, infine, ecco la struttura che definisce il mio risultato di callback:

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; 
} 
} 

Ho guardato un sacco dei demo, ma sembrano per lo più lasciare il movimento a Bullet, e dal momento che sto spostando i personaggi a una velocità impostata senza alcuna fisica speciale quando si scontrano, ho avuto difficoltà ad adattare gli esempi alla mia applicazione. Il callback del risultato proviene in realtà da questo post nei forum: http://bulletphysics.org/Bullet/phpBB3/viewtopic.php?t=6816 Si tratta di utilizzare mesh triangolari, ma sembrava più vicino a quello che stavo cercando di implementare.

In ogni caso, se avete letto fino a qui, grazie !! Qualsiasi consiglio o link che potresti risparmiare sarebbe molto apprezzato.

+3

"Voglio solo aggiornare le posizioni degli oggetti ogni fotogramma e usarlo per dirmi quando ho delle collisioni." Questo è generalmente antitetico su come funziona un sistema * physics *. Dovresti provare a lavorare con il tuo motore fisico, non * contro * it. Se hai personaggi che si muovono a una velocità prestabilita, dovresti lasciare che il tuo sistema fisico li sposti. Può farlo bene. –

+0

sì, vorrei poter taggare questo come risposta. Dopo aver letto questo e averlo esaminato ancora di più, mi sono reso conto che posso solo eseguire il rilevamento delle collisioni usando volumi limite e alcuni calcoli matematici. Grazie! – Aztal

+0

Che cos'è l'input e l'output? E.g .: per ogni frame Input = posizione + velocità (per step) di tutti gli oggetti Output = quali coppie di oggetti sono entrati in collisione in quale punto nello spazio? E aggiorni manualmente le posizioni/velocità usando un metodo personalizzato? –

risposta

4

Sto scrivendo un'app IOS con il fucile che si sparano a vicenda sulla scena 3D. Uso fisica dei proiettili per il rilevamento delle collisioni Ho impostato il flighter come oggetto cinematico, la mia logica sposta il flighter e quindi aggiorna il mondo btMotionStateTransform dell'oggetto cinematico. anche io non ottengo alcun rilevazioni di collisione fino a quando ho modificare le seguenti due affermazioni (impostare la mascheratura e di gruppo per la stessa sia per il giocatore e nemici)

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

allora posso vedere il

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

Il valore numManifold diventa 1 quando l'oggetto collide.

1

È possibile controllare le informazioni di contatto come spiegato here:

Recapiti

Il modo migliore per determinare se le collisioni accaduto tra esistente oggetti nel mondo, è quello di iterare su tutti i contatti molteplice. Questo deve essere eseguito durante una richiamata di simulazione (substep), poiché i contatti possono essere aggiunti e rimossi durante diversi sottofasi di una chiamata Step-Simulation .Un collettore di contatti è una cache che contiene tutti i punti di contatto tra coppie di oggetti di collisione. Un buon modo è quello di iterare su tutte le coppie di oggetti in tutto il mondo/dinamiche collisione:

//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; 
      } 
     } 
    } 

Potreste essere interessati a btGhostObject che tiene traccia dei propri coppie sovrapposte.

0

Minimal esempio eseguibile

Una sfera cade e colpisce il suolo.

Le collisioni vengono rilevate e stampate su stdout.

Gnuplot visualizzazione:

La linea "collisione" va a 1 ogni volta che la sfera tocca il suolo.

E per coefficienti di restituzione piccole (0.5 e 0.5):

Qui la pallina si ferma saltando completamente e tocca il terreno continuamente.

Codice:

#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(); 
} 

Sulla base: http://www.bulletphysics.org/mediawiki-1.5.8/index.php

versione di questo focalizzata sulla distinzione quale oggetto toccato quale oggetto: https://gamedev.stackexchange.com/a/120881/25171

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

provata su proiettile 2,83 , Ubuntu 15.10.

Problemi correlati