Bullet物理引擎学习笔记4(碰撞过滤)

有时候我们会想让俩个物体不发生碰撞而是穿过对方,比如打砖块游戏里的Unstoppable形态的小球。通过查看Bullet的wiki我们可以看到几种方法,有在碰撞添加时防止的,也有碰撞时防止的。我个人最喜欢的是wiki中的第一种,通过位运算的实现的Filter。

#define BIT(x) (1<<(x))//一个宏
enum collisiontypes {
    COL_NOTHING = 0, //

const int groundCollidesWith = COL_AIRBOX;
const int airboxCollidesWith = COL_GROUND | COL_KINEMATICBOX;
const int kinematicboxCollidesWith = COL_AIRBOX;

定义好以上之后只需要在addrigidbody是加入2个参数就能实现碰撞过滤了。

m_dynamicsWorld->addRigidBody(boxBody, COL_AIRBOX, airboxCollidesWith);

要说明的是,在最新版本的Bullet中,只有当物体A可碰撞物体中有物体B,并且物体B可碰撞物体中也有物体A,AB才会发生碰撞。

Bullet物理引擎学习笔记2(MotionState)

上一次说道更新物体位置信息的时候,是通过遍历所有物体的方法来更新的。然后这种方法会做很多无用功,因为有些物体根本就没动。

为了解决这个问题,我们就需要利用MotionStae。我们可以看到,在上次的文章中,我们也使用了MotionState,只不过是bullet提供的默认的MotionState,现在我们自己创建一个来代替它。

#include "linearmath/btMotionState.h"
#include 

class MyMotionState : public btMotionState {
public:
MyMotionState(const btTransform &initialpos, Ogre::SceneNode *node) {//初始化的时候将初始位置和node设置好
mVisibleobj = node;
mPos1 = initialpos;
}

virtual ~MyMotionState() {
}

void setNode(Ogre::SceneNode *node) {
mVisibleobj = node;
}

virtual void getWorldTransform(btTransform &worldTrans) const {
worldTrans = mPos1;
}

virtual void setWorldTransform(const btTransform &worldTrans) {
if(NULL == mVisibleobj)
return; // silently return before we set a node
btQuaternion rot = worldTrans.getRotation();//更新物体在Ogre中的位置信息
mVisibleobj->setOrientation(rot.w(), rot.x(), rot.y(), rot.z());
btVector3 pos = worldTrans.getOrigin();
mVisibleobj->setPosition(pos.x(), pos.y(), pos.z());
}

protected:
Ogre::SceneNode *mVisibleobj;
btTransform mPos1;
};

这样在bullet每次调用setWorldTransform的时候,就会设置对应node的位置,那些不会动的物体就不会更新了。

Bullet物理引擎学习笔记3(碰撞检测)

现在我们的物体能够动起来了,并且通过Bullet的计算,我们可以观察到物体会像在真实世界中一样运动碰撞等等。一个自然的想法是,我们如何知道谁和谁进行了碰撞?

关于bullet的碰撞检测,google能搜到一些答案,都是正确的,不过看bullet自己的wiki比较全面。

比如我们可以通过一个回调函数来捕捉碰撞,从wiki中我们可以看到有一种回调函数的原型是:

typedef bool (*ContactProcessedCallback)(
    btManifoldPoint& cp,//重合点的意思吧,用不到可以无视它
    void* body0,void* body1);//发生碰撞的俩个物体

这样我们只需要写好一个符合原型的函数——myContactProcessedCallback,然后将其赋值给bullet全局变量gContactProcessedCallback就行了。

        gContactProcessedCallback = myContactProcessedCallback;

最后需要提一下的是,俩个物体间的碰撞在某一次stepSimulation中会进入多次你的回调函数,所以不要第一次进去的时候就把body给删除,正确的做法是将要删除的指针放进一个set,在stepSimulation结束时删除。

Bullet物理引擎学习笔记1

折腾了两天,各种乱七八糟blog还有wiki什么的翻了个遍,从零开始学就是这样的,痛并快乐着。

我是用的是bullet-2.77,因为googlecode上这个版本有编译好的demo,我可以先下下来看,编译的过程很简单,vs打开工程生成一下就行了,不过这里有可能会报个小小的error,转到出错的位置后,只要将PostBuild那个target全注释掉就行了。

一开始我在用不用OgreBullet这个开源项目上纠结了半天,最后我放弃了它,现在想想还是正确的,OgreBullet是一个封装好的项目,实际上不利于像我这样从头开始学习Bullet的人。

由于项目的目的是要做一个躲避球的游戏,我初步的设想就是写一个能控制物体去躲避物体的demo,这里我选用了最简单的物体,也就是bullet里的基本Shape之一的盒子(box)。

要完成这个目标,我计划分步学习实现。

首先让bullet能和ogre结合起来,即能够通过物理引擎的计算将物体的正确位置显示到界面上,能够创建并发射一系列的盒子。

其次,能够检测出盒子的碰撞,并将于指定物体碰撞的盒子进行删除。

然后,能通过鼠标或键盘来控制指定物体进行躲避。

最后,能同过Kinect来控制物体。

其中,前3步不需要Kinect的辅助,可以先完成。做完这些后,就是对游戏的润化和调优,以后再说。

先说第一步吧,首先我们需要在ogre的世界和bullet的物理世界中创建物体,在这之前,我们需要对bullet的世界进行初始化。(不要问我这些是什么意思,我自己也似懂非懂的)

btDiscreteDynamicsWorld* m_dynamicsWorld;
btDefaultCollisionConfiguration* m_collisionConfiguration;
btCollisionDispatcher* m_dispatcher;
btBroadphaseInterface* m_overlappingPairCache;
btSequentialImpulseConstraintSolver* m_solver;
btAlignedObjectArray m_collisionShapes;

void initPhysics()
{ m_collisionConfiguration = new btDefaultCollisionConfiguration();
m_dispatcher = new btCollisionDispatcher(m_collisionConfiguration);
m_overlappingPairCache = new btDbvtBroadphase();
btSequentialImpulseConstraintSolver* sol = new btSequentialImpulseConstraintSolver;
m_solver = sol;
m_dynamicsWorld = new btDiscreteDynamicsWorld(m_dispatcher, m_overlappingPairCache, m_solver, m_collisionConfiguration);
m_dynamicsWorld->setGravity(btVector3(0, -10, 0));
}

接下来开始创建物体,NodeInfo是我自己设置的一个类,记录了Ogre中的SceneNode和一些其他信息。

//in physics
btCollisionShape* boxShape = new btBoxShape(btVector3(size.x,size.y,size.z));//创建Shape
float mass = 1.f;//质量,这里要说明的是,如果质量是0的话表示是一个静态物体或者Kinematic物体(不知道怎么翻译),区别可以看UserManual
btTransform startTransform;
startTransform.setIdentity();
startTransform.setOrigin(btVector3(0,5,0));//设置位置
bool isDynamic = (mass != 0.f);
btVector3 localInertia(0,0,0);
if (isDynamic) boxShape->calculateLocalInertia(mass,localInertia);//不知道干什么的
btDefaultMotionState* myMotionState = new btDefaultMotionState(startTransform);//创建一个MotionState以后会用到
btRigidBody::btRigidBodyConstructionInfo cInfo(mass,myMotionState,boxShape,localInertia);
btRigidBody* boxBody = new btRigidBody(cInfo);//创建它
NodeInfo *nodeInfo = new NodeInfo(node, "KinematicBodyBox", "man");
boxBody->setUserPointer((void*)nodeInfo);//通过bullet预留的一个指针,记录Ogre中对应node的信息,以便更新位置
m_dynamicsWorld->addRigidBody(boxBody);

bullet通过下面这句话进行模拟,后面的参数是模拟多少时间。

m_dynamicsWorld->stepSimulation(etime);

之后我们可以对node的位置等进行更新
btCollisionObjectArray bodies = m_dynamicsWorld->getCollisionObjectArray();
btRigidBody *TObject;
for (int i=0;i(TObject->getUserPointer());
    if (TObject->isStaticOrKinematicObject())
    {
        continue;
    }

// Set position
btVector3 Point = TObject->getCenterOfMassPosition();
nodeInfo->node->setPosition(Ogre::Vector3(Point.getX(), Point.getY(), Point.getZ()));

// Convert the bullet Quaternion to an Ogre quaternion
btQuaternion btq = TObject->getOrientation();
Ogre::Quaternion quart = Ogre::Quaternion(btq.w(),btq.x(),btq.y(),btq.z());

// use the quaternion with setOrientation
nodeInfo->node->setOrientation(quart);
}

你可以将一个物体的初始高度设为100,然后在每次stepSimulation后输出其高度,可以看到它会落入无底深渊。。。你也可以创建一个平面,阻止其下落。

//in physics
btCollisionShape* groundShape = new btStaticPlaneShape(btVector3(0,1,0),0);
btDefaultMotionState* groundMotionState = new btDefaultMotionState(btTransform(btQuaternion(0,0,0,1),btVector3(0,0,0)));
btRigidBody::btRigidBodyConstructionInfo groundRigidBodyCI(0,groundMotionState,groundShape,btVector3(0,0,0));
btRigidBody* groundRigidBody = new btRigidBody(groundRigidBodyCI);
m_dynamicsWorld->addRigidBody(groundRigidBody, COL_GROUND, groundCollidesWith);

最后你可以通过Ogre的FrameListener实现按一下键盘的某个键,就发射一个盒子。

Enjoy yourself in Bullet!

20120108新年首次活动小记

7人行本来准备打球的,无奈天公不作美,于是改变计划开始寻找桌游店。

普通桌游店一般营业结束都较晚,所以开门时间也就比较晚,我们在找了3家都无人应答正郁闷往哪走的绝望之时,睡眼朦胧的老板开门啦,谢天谢地啊。(店名是FF桌面游戏)

人数受限,很难开出一桌,纠结之下开了七大奇迹,(PS:老板此处不断确认我们是否要玩这个,什么意思。。。),实验局D哥蛋帝猛拼军事,事实证明是赢不了滴,教学局我和伟男同积57分并列第一,伟男紫卡(工会牌)至少拿了25分。。。,我科技拿了比较多的分,军事惨败,还好只输一点。第二局貌似大家都玩的比较熟练了,开始思考如何卡下家,hjj被我和D哥卡得造不出2级奇迹这样巴比伦空中花园威力大减,我得益于早造出了金字塔的三级奇迹,并且左右两边都不发展军事,靠2张军事卡偷得10+分,最后积52分一分险胜。伟男最后没卡好我,不过我第三时代一开始也失误了,应该拿6分的建筑,不应该造3军事的,毕竟后面还有机会。

午饭过后,开了几个欢乐游戏,(小小世界underground规则没看啊,怨念),一个貌似是店员的女生加入了游戏,不过第一个需要美国常识的游戏实在太乏味了(作为裁判的我名字我都不知道)。之后店员mm介绍了赌马的游戏,蛋帝的“一”马当先,赢了100元,小受3个注分别押对一二三名排名第二,我的“最后一搏”在重点线前一格输了,没排到名次,否则我就翻盘了,其实只要再轮到我我就能靠新摸到的牌将自己的马拉过终点的啊,不过还好没垫底。感觉结束的比较快,大家来不及下注,还是我们玩的有问题?

余下3局三国杀8人局就不表了。

 

20111231桌游活动小记(卡坦岛&只言片语)

31号终于考完了最后一门全是选择题的编译原理,终于能松口气了,恰逢D哥同学到交大来玩,加上wzh和竟仔还有东哥,一共6个人从21点玩到了第二天8点,爽是爽,不过回家路上就顶不住了。。。

由于都是新手,为了不让大家产生畏惧感,我只介绍了最简单的卡坦岛,还特意强调这是个人品游戏,规则很快介绍完后,由D哥(白色)、wzh(蓝色)、东哥(橙色)、D哥同学(红色)4人进行了第一局。整局游戏骰运很不平均,8和6总是扎堆出现,而东哥的11只出现过1次。。。前期wzh虽然拥有羊港,但是2和3的两块地盘限制了它的发展;D哥中心2城要不是他同学和东哥手软,早就要被限制到死;Dong哥骰运不佳;而D哥的同学潜力巨大。关键的转折点在于D哥成功突围,并且强行造出了木港,其实个人认为先造6、9、11那个点更好,其间D哥同学试图兵王路王速胜但是失败了,之后就进入了D哥疯狂拿资源建大城的情况,期间D哥一度有连造3路偷路王的机会,所有人都看出来了他自己还蒙在鼓里。。。不过不影响他最后的胜利,最后形势如下图。


第一局,教学局,D哥胜出

第二局由于人比较多,加入了5-6人扩展,看上去大大的版图,在12个村庄放下后就拥挤的可怕。我作为最后一个动的人,已经没什么位置给我选了,只能2连动选了砖港+6、11的砖地,这样稀缺资源能被换出来,也控制了不多的砖块,发展不会太受限。


第二局,加了扩展6人局,开始的样子

(蓝色:我,橙色:竟仔,红色:东哥,白色:D哥,绿色:wzh,棕色:D哥同学)/*色弱轻拍*/

开始后让我没想到的是,前面的人不停地扔7,都来抢我(如下图),我还没动,3个资源都被抢了,整一个郁闷了得。。。


一开始都把我的资源抢完了

之后wzh妄图和我换砖块造村庄,被我识破,并且很快我造出了这个村庄,让他2条路浪费了。我和D哥同学垄断砖块,抬高价码取得了快速的发展。


我抢到了关键的村庄

之后领跑者不断更换,竟仔,D哥同学,wzh先后成为被压制的对象,我则慢慢发展,但是不知怎么回事,大家觉得我要赢了又都来卡我,不和我换资源,我又点背2次自己在10资源的时候扔出7,排到了竟仔的后面,竟仔由于路王不可动摇加之大城起得早,资源出得多,而且没人能扔出7,而牢牢把握着第一的位置,最终获得胜利。


竟仔掏出一个胜利分,宣告胜利

之后玩了两局3V3,和一局十分纠结的双将国战。

最后开了只言片语,出乎我意料的是,大家竟然对只言片语十分喜爱,我一直以为女生会比较喜欢这个的。。。现在想想可能是只言片语简单的规则和轻松的氛围让它倍受青睐(毕竟后半夜还是蛮累的)。第一局D哥获胜,之后他就摆脱不了倒数的命运了。。。第二局我赢了,第三局D哥同学赢了。感觉利用某些人知识的盲区,或者说利用和某人的共同知识来出题能起到比较好的压制效果,总体来说还是个欢乐游戏,没必要太在意。

最后需要指出的是wzh同学,拿不到第一也不让别人赢的桌游思想我很不赞同。

星杯传说开箱照

星杯传说是国产桌游中口碑较好的一款,职业设计得精致,目标明确,试玩后感觉的确不错,一个普通的职业想要用好也很难。

 


盒子正面,大家应该知道画风了吧


说明书正面是天使


阵营卡,记录星杯和战绩


魔剑闪卡,这年头都爱送闪卡


内部结构,满拥挤的


12个骰子


5种魔法牌


6种攻击牌


星石和水晶


治疗卡


两种指示物,有些角色会用到


星杯卡


红蓝阵营

20111208桌游活动小记(两河流域&农场主)

编译和oo小作业搞完后,就兴致冲冲地跑到楼上准备开两河了,无奈wzh和pp都在忙,拖了好久才开战。


两河最后的战局

讲解完规则后,wzh拿了瓶子王朝,pp几经犹豫拿了公牛王朝,我则选了弓手。一上来,pp求战心切,就和wzh开战,而我在一边发展,很快wzh败下阵来开始转移阵地,pp则建起了第一座纪念碑,我苦于没有蓝色的农庄卡片拿不到蓝色得分,于是乎两家都针对pp。进入中期我赢了2次外部冲突,蓝色和绿色得分暴涨,黑色则拖了后腿,之后pp用语言诱导wzh来打我,很快pp起了第二座纪念碑,并且每回合稳定的有红绿分,我领袖皆毗邻2做神殿,手中神殿也不少,wzh用灾难卡片断路后进攻成效不大,反倒是让我拿足够了红分,期间我曾想和pp争夺一大片河流的左部区域,也因为wzh的骚扰而计划流产,不得不放弃手中4块蓝色卡片,换取神殿。之后我用灾难卡断开wzh的“一字长蛇阵”,通过内部冲突解决掉他的黑色领袖,并且最后成功造出2黑分,在自己的回合内结束游戏(最后形势如上图)。挡板移除,谜底揭晓,我和pp最低项和次低项都相等,倒数第三低的我赢了一分(如下图),而wzh分太低就没拍他的。大家都是第一次玩,感觉决定比较草率,下次开,估计某人就要一直陷入长考了(坏笑)。


pp得分9+9+9+10


我的得分9+9+10+好多

阅读更多...

  • Copyrights © 2011-2022 仙雾

请我喝杯咖啡吧~

支付宝
微信