como obter retorno de chamada de colisão de dois objetos específicos usando a física de bala?


7

Ocorreu um problema ao implementar o retorno de chamada de colisão no meu projeto. Eu gostaria de ter detecção entre dois objetos específicos. Tenho colisão normal, mas quero que um objeto pare ou mude de cor ou qualquer outra coisa quando colidir com outro. Eu escrevi o código do bullet wiki:

int numManifolds = dynamicsWorld->getDispatcher()->getNumManifolds();
    for (int i=0;i<numManifolds;i++)
    {
        btPersistentManifold* contactManifold =  dynamicsWorld->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;
                bool x = (ContactProcessedCallback)(pt,fallRigidBody,earthRigidBody);
                if(x)
                    printf("collision\n");
            }
        }
    }

onde fallRigidBody é um corpo dinâmico - uma esfera e earthRigiBody é um corpo estático - StaticPlaneShape e sphere não tocam em earthRigidBody o tempo todo. Também tenho outros objetos que estão colidindo com a esfera e funciona bem. Mas o programa detecta colisão o tempo todo. Não importa se os objetos estão ou não colidindo.

Eu também adicionei após declarações de corpo rígido:

    earthRigidBody->setCollisionFlags(earthRigidBody->getCollisionFlags() | btCollisionObject::CF_CUSTOM_MATERIAL_CALLBACK);
fallRigidBody->setCollisionFlags(fallRigidBody->getCollisionFlags() | btCollisionObject::CF_CUSTOM_MATERIAL_CALLBACK);

Então, alguém pode me dizer o que estou fazendo de errado? Talvez seja algo simples?


Respostas:


3

Eu estou familiarizado com o JBullet, mas como é a porta java do original, o conceito deve ser o mesmo.

Pareceu-me que sempre há uma colisão com seu plano estático e outros objetos. Mas você precisa fazer algo apenas na colisão de objetos específicos. O que você precisa usar é o ponteiro do usuário da forma de colisão. Aqui está um pedaço de código do meu próprio mecanismo em java. Aqui GameEntity é a classe que envolvo meus objetos e CollisionListener é outra classe minha que eu uso para implementar o que a colisão faz. Também passo posições e colisões normais ao ouvinte para obter efeitos mais sofisticados.

            if (pt.getDistance() < 0.f) {
                Vector3f ptA = pt.getPositionWorldOnA(new Vector3f());
                Vector3f ptB = pt.getPositionWorldOnB(new Vector3f());
                Vector3f normalOnB = pt.normalWorldOnB;
                GameEntity thisObject = (GameEntity) (obA.getUserPointer());
                GameEntity thatObject = (GameEntity) (obB.getUserPointer());
                if(thisObject == null){
                    return;
                }
                if (thisObject.getCollisionListener() != null) {
                    thisObject.getCollisionListener().collided(
                            ptA, ptB, normalOnB, thatObject);
                }
                if (thatObject.getCollisionListener() != null) {
                    thatObject.getCollisionListener().collided(
                            ptB, ptA, normalOnB, thisObject);
                }
            }

0

Exemplo mínimo executável

Duas esferas caindo de diferentes alturas em função do tempo.

Desde que eles começam em alturas diferentes, eles atingirão o chão em momentos diferentes.

Detectamos o impacto no solo e imprimimos em qual esfera o solo tocou quando.

Visualização Gnuplot do stdout:

Veja como:

  • Quando uma esfera atinge o chão, o indicador de colisão quadrado rosa passa para 1
  • quando a outra esfera atinge o chão, o verde Xpassa para1

Código:

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

#include <btBulletDynamicsCommon.h>

#define PRINTF_FLOAT "%7.3f"

constexpr float gravity = -10.0f;
constexpr float timeStep = 1.0f / 60.0f;
constexpr float groundRestitution = 0.9f;
constexpr float objectRestitution = 0.9f;
constexpr int maxNPoints = 500;
constexpr float initialXs[] = {  0.0f, 5.0f };
constexpr float initialYs[] = { 10.0f, 5.0f };
constexpr size_t nObjects = sizeof(initialYs) / sizeof(*initialYs);

std::map<const btCollisionObject*,std::vector<btManifoldPoint*>> objectsCollisions;
void myTickCallback(btDynamicsWorld *dynamicsWorld, btScalar timeStep) {
    objectsCollisions.clear();
    int numManifolds = dynamicsWorld->getDispatcher()->getNumManifolds();
    for (int i = 0; i < numManifolds; i++) {
        btPersistentManifold *contactManifold = dynamicsWorld->getDispatcher()->getManifoldByIndexInternal(i);
        auto *objA = contactManifold->getBody0();
        auto *objB = contactManifold->getBody1();
        auto& collisionsA = objectsCollisions[objA];
        auto& collisionsB = objectsCollisions[objB];
        int numContacts = contactManifold->getNumContacts();
        for (int j = 0; j < numContacts; j++) {
            btManifoldPoint& pt = contactManifold->getContactPoint(j);
            collisionsA.push_back(&pt);
            collisionsB.push_back(&pt);
        }
    }
}

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;
        groundShape = new btStaticPlaneShape(btVector3(0, 1, 0), -1);
        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);
    }

    // Objects.
    for (size_t i = 0; i < nObjects; ++i) {
        btCollisionShape *colShape;
        colShape = new btSphereShape(btScalar(1.0));
        collisionShapes.push_back(colShape);
        btTransform startTransform;
        startTransform.setIdentity();
        startTransform.setOrigin(btVector3(initialXs[i], initialYs[i], 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(objectRestitution);
        dynamicsWorld->addRigidBody(body);
    }

    // Main loop.
    std::printf("step body x y z collision2 collision1\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, origin.getX(), origin.getY(), origin.getZ());
            auto& manifoldPoints = objectsCollisions[body];
            if (manifoldPoints.empty()) {
                std::printf("0");
            } else {
                std::printf("1");
            }
            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();
}

Testado no Bullet 2.83, Ubuntu 15.10.

GitHub upstream: https://github.com/cirosantilli/cpp-cheat/blob/d7b70153b8f86b5864c22fbc6f7005049a93491f/bullet/which_collision.cpp


@Downvoters, por favor explique para que eu possa aprender e melhorar ;-)
Ciro Santilli冠状病毒审查六四事件法轮功
Ao utilizar nosso site, você reconhece que leu e compreendeu nossa Política de Cookies e nossa Política de Privacidade.
Licensed under cc by-sa 3.0 with attribution required.