2012-06-24 70 views
7

我正在嘗試將Bullet物理用於碰撞檢測。我不需要它爲我移動任何對象或使用回調來處理渲染。我只想更新每一幀的對象位置,並使用它來告訴我何時發生碰撞。爲了得到最簡單的例子,我試圖找出btBoxShape作爲其形狀的對象之間的碰撞。一切運行良好,沒有崩潰或明顯的內存泄漏,但我沒有碰撞,所以我必須在某處犯一些錯誤。我會盡可能簡短地保持這一點,而不會留下任何重要的東西。子彈物理最簡單的碰撞示例

這裏是我的世界設定功能:

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

現在我有型btCollisionObject *的玩家和敵人的對象。我設置他們是這樣的:

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

我對我的敵人物體做基本相同的事情。

然後每幀更新我的是這樣的所有對象:

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 
} 

最後,這裏是定義我的結果回調結構:

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

我已經看了很多的演示,但他們似乎大部分都是離開運動到Bullet,並且由於我在設定的速度下移動角色時沒有任何特殊的物理碰撞,所以我無法將這些例子調整到我的應用程序中。結果回調實際上是從這個職位上的論壇傳來: http://bulletphysics.org/Bullet/phpBB3/viewtopic.php?t=6816 這是關於使用三角形網格,但它似乎最接近我試圖實現。

無論如何,如果你讀到這裏,謝謝!任何建議或鏈接,你可以免費將非常感激。

+3

「我只是想每次更新對象的位置,並用它來告訴我什麼時候發生了碰撞。」這通常與*物理*系統的工作原理是相反的。你應該嘗試使用你的物理引擎,而不是*反對*它。如果你的角色以設定的速度移動,那麼你應該真的讓你的物理系統移動它們。它可以做到這一點。 –

+0

是的,我希望我能把這個標記爲答案。在閱讀完這些內容並進一步研究之後,我意識到我可以使用邊界卷和一些數學方法自己進行碰撞檢測。謝謝! – Aztal

+0

什麼是輸入和輸出?例如:對於每一幀輸入=所有對象的位置+速度(每步)輸出=哪對對象在空間中的哪個點碰撞?並且您使用自定義方法手動更新位置/速度? –

回答

4

我寫與flighter互相射擊的3D場景的iOS應用。 我使用Bullet物理碰撞檢測 我設置flighter爲運動對象,我的邏輯移動flighter,然後更新btMotionState worldTransform運動的對象。 我還沒有得到任何碰撞檢測,直到我改變了下面的兩個語句(設置掩蔽和組相同的兩個玩家和敵人)

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

的話,我可以看到

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

當對象發生碰撞時numManifolds的值變爲1。

1

您可以查看聯繫人信息,解釋here

聯繫信息

,以確定是否衝突在世界上現有 對象之間發生的最好辦法,就是要遍歷所有接觸歧管。這 應該在仿真時間(分步)回調過程中完成的,因爲 接觸可能會在一個 單stepSimulation調用若干子被添加和刪除。接觸歧管是包含碰撞對象對之間的所有接觸點的緩存。一個好的 辦法是遍歷所有對象對整個 碰撞/動態的世界:

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

您可能感興趣的btGhostObject保持跟蹤自己的重疊對。

0

最小可運行例如

球體落下並撞擊地面。

檢測到碰撞並將其打印到標準輸出。

gnuplot的可視化:

的 「碰撞」 行去1每當球接觸地面。

而對於較小的恢復係數(0.50.5):

這球完全停止跳動,不斷接觸地面。

代碼:

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

基於:http://www.bulletphysics.org/mediawiki-1.5.8/index.php

的這個版本集中於區分哪個對象觸摸哪個對象:https://gamedev.stackexchange.com/a/120881/25171

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

測試在子彈2.83 ,Ubuntu 15.10。