我正在尝试使用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 这是关于使用三角形网格,但它似乎最接近我试图实现的。
无论如何,如果你读到这里,谢谢!任何您可以提供的建议或链接都将非常感激。
答案 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.5
和0.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
测试子弹2.83,Ubuntu 15.10。