Bullet物理最简单的碰撞示例

时间:2012-06-24 07:21:05

标签: c++ game-physics bulletphysics

我正在尝试使用Bullet Physics进行碰撞检测。我不需要它为我移动任何对象或使用回调处理渲染。我只想每帧更新对象位置,并用它来告诉我何时发生碰撞。为了得到最简单的例子,我试图找到以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;
 }
}

我看了很多演示,但他们似乎主要是将动作留给了子弹,因为我在碰撞时没有任何特殊物理的设定速度移动角色,我无法适应我的应用程序中的示例。结果回调实际上来自论坛上的这篇文章: http://bulletphysics.org/Bullet/phpBB3/viewtopic.php?t=6816 这是关于使用三角形网格,但它似乎最接近我试图实现的。

无论如何,如果你读到这里,谢谢!任何您可以提供的建议或链接都​​将非常感激。

3 个答案:

答案 0 :(得分:4)

我正在编写一个IOS应用程序,在3D场景中互相射击。 我使用子弹物理进行碰撞检测 我将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 :(得分:1)

您可以按照here

中的说明查看联系信息

联系信息

  

确定现有冲突是否发生的最佳方法   世界上的物体,是迭代所有接触流形。这个   应该在模拟tick(子步)回调期间完成,因为   可以在a的几个子步骤中添加和删除联系人   单步模拟调用。接触歧管是缓存   包含碰撞对象对之间的所有接触点。一个好的   方法是遍历整个对象中的所有对象   碰撞/动力学世界:

//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感兴趣。

答案 2 :(得分: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。