2012-06-24 46 views
7

Tôi đang cố gắng sử dụng Bullet Physics để chỉ phát hiện va chạm. Tôi không cần nó để di chuyển bất kỳ đối tượng cho tôi hoặc xử lý rendering với callbacks. Tôi chỉ muốn cập nhật vị trí đối tượng mỗi khung hình và sử dụng nó để cho tôi biết khi nào tôi có xung đột. Để có được ví dụ đơn giản nhất, tôi đang cố gắng tìm va chạm giữa các đối tượng với btBoxShape làm hình dạng của chúng. Mọi thứ đều chạy tốt mà không có sự cố hoặc rò rỉ bộ nhớ rõ ràng, nhưng tôi không bị va chạm vì vậy tôi phải phạm một số sai lầm ở đâu đó. Tôi sẽ cố gắng giữ nó ngắn gọn như tôi có thể mà không để lại bất cứ điều gì quan trọng.Đạn Vật lý Ví dụ Va chạm Đơn giản

Dưới đây là chức năng thiết lập thế giới của tôi:

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

Ngay bây giờ tôi có người chơi và kẻ thù đối tượng của loại btCollisionObject *. Tôi đang thiết lập chúng như sau:

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

Tôi thực chất là điều tương tự với đối tượng kẻ thù của mình.

Sau đó mỗi khung để cập nhật tất cả các đối tượng của tôi với một cái gì đó như thế này:

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 
} 

Và cuối cùng, đây là cấu trúc xác định kết quả callback của tôi:

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

tôi đã xem xét rất nhiều các bản trình diễn, nhưng dường như họ rời khỏi phong trào lên Bullet, và vì tôi đang di chuyển các nhân vật với tốc độ cài đặt mà không có bất kỳ vật lý đặc biệt nào khi chúng va chạm, tôi gặp rắc rối khi thích ứng với các ví dụ trong ứng dụng của mình. Kết quả gọi lại thực sự đến từ bài đăng này trên diễn đàn: http://bulletphysics.org/Bullet/phpBB3/viewtopic.php?t=6816 Đó là về việc sử dụng lưới hình tam giác, nhưng dường như gần nhất với những gì tôi đã cố gắng triển khai.

Dù sao, nếu bạn đọc bài này đến nay, cảm ơn bạn !! Bất kỳ lời khuyên hoặc liên kết bạn có thể phụ tùng sẽ được rất nhiều đánh giá cao.

+3

"Tôi chỉ muốn cập nhật vị trí đối tượng mỗi khung hình và sử dụng nó để cho tôi biết khi nào tôi có xung đột." Điều đó nói chung là không hợp lý về cách một hệ thống * vật lý * hoạt động. Bạn nên cố gắng làm việc với công cụ vật lý của mình, chứ không phải * chống lại nó. Nếu bạn có các ký tự di chuyển với tốc độ đã đặt, thì bạn nên thực sự để hệ thống vật lý của bạn di chuyển chúng. Nó có thể làm điều đó tốt. –

+0

vâng, tôi ước tôi có thể gắn thẻ câu trả lời này là câu trả lời. Sau khi đọc điều này và nhìn vào nó một số chi tiết, tôi nhận ra rằng tôi chỉ có thể tự mình phát hiện va chạm bằng cách sử dụng khối lượng giới hạn và một số phép toán. Cảm ơn! – Aztal

+0

Đầu vào và đầu ra là gì? Ví dụ: cho mỗi khung Input = position + speed (mỗi bước) của tất cả các đối tượng Output = cặp đối tượng nào va chạm vào điểm nào trong không gian? Và bạn cập nhật vị trí/tốc độ theo cách thủ công bằng phương pháp tùy chỉnh? –

Trả lời

4

Tôi đang viết một ứng dụng IOS với hình ảnh chụp liên tục trên cảnh 3D. Tôi sử dụng vật lý đạn để phát hiện va chạm Tôi đặt máy bay sáng như đối tượng động học, logic của tôi sẽ di chuyển bộ lọc và sau đó cập nhật btMotionState worldTransform của đối tượng động học. tôi cũng không nhận được bất kỳ phát hiện va chạm cho đến khi tôi thay đổi hai câu sau đây (thiết lập mặt nạ và nhóm giống nhau cho cả người chơi và kẻ thù)

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

sau đó tôi có thể nhìn thấy

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

Giá trị numManifolds trở thành 1 khi đối tượng va chạm.

1

Bạn có thể kiểm tra các thông tin liên lạc như đã giải thích here:

Liên hệ Thông tin

Cách tốt nhất để xác định nếu va chạm xảy ra giữa đối tượng hiện có trên thế giới, là để lặp qua tất cả các liên lạc đa tạp. Bạn cần thực hiện này trong khi gọi lại đánh dấu mô phỏng (substep), vì liên hệ có thể được thêm vào và xóa trong một số bước thay thế của một cuộc gọi gọi là Cuộc gọi kích thích.Danh sách liên hệ là bộ nhớ cache chứa tất cả các điểm tiếp xúc giữa các cặp đối tượng va chạm. Một cách tốt là để lặp qua tất cả các cặp của các đối tượng trong toàn bộ va chạm/động thế giới:

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

Bạn có thể quan tâm đến btGhostObject mà theo dõi những cặp chồng chéo của riêng mình.

0

Minimal dụ Runnable

Một quả cầu rơi xuống và chạm mặt đất.

Các va chạm được phát hiện và được in thành thiết bị xuất chuẩn.

Gnuplot trực quan:

Các "va chạm" đường đi đến bất cứ khi nào 1 lĩnh vực chạm mặt đất.

Và đối với hệ số bồi thường nhỏ hơn (0.50.5):

đây bóng ngừng nhảy hoàn toàn và chạm mặt đất liên tục.

Code:

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

Dựa trên: http://www.bulletphysics.org/mediawiki-1.5.8/index.php

Phiên bản này tập trung vào việc phân biệt mà đối tượng xúc động mà đối tượng: https://gamedev.stackexchange.com/a/120881/25171

GitHub thượng nguồn: https://github.com/cirosantilli/cpp-cheat/blob/503a3b6487ccb75334798839b5ed912270446d14/bullet/ground_ball.cpp

Thử nghiệm trên Bullet 2.83 , Ubuntu 15.10.