cocos2d-x3.0 物理引擎之详解
我在编写游戏的时候遇到了这个问题, 物理引擎其他的内容还好理解, 就这三个函数就是没找到有人详细的解释一下。
找了半天终于找到合理的解释,写下来好理解。
先说物理引擎:
想用这个物理引擎,首先必须 创建一个物理引擎世界,普通的场景就不行了,如下:
Scene* MyScene::createScene()
{
// 'scene' is an autorelease object
auto scene = Scene::createWithPhysics();
//显示物理引擎调试界面 --- 有红框
scene->getPhysicsWorld()->setDebugDrawMask(PhysicsWorld::DEBUGDRAW_ALL);
// 'layer' is an autorelease object
auto layer = MyScene::create();
// add layer as a child to scene
scene->addChild(layer);
return scene;
}
然后需要一个全屏碰撞盒子,意思就是在这个范围内碰撞。
Size visibleSize = Director::getInstance()->getVisibleSize();
//创建一个物理世界, 大小和屏幕的尺寸相同, 使用默认材质, debug框的宽度为3个像素
auto body = PhysicsBody::createEdgeBox(visibleSize, PHYSICSBODY_MATERIAL_DEFAULT, 3);
//创建一个碰撞图形
auto edgeShape = Node::create();
//将图形和刚刚创建的世界绑定
edgeShape->setPhysicsBody(body);
//设置图形的位置在屏幕正中间
edgeShape->setPosition(visibleSize.width / 2, visibleSize.height / 2);
//添加进图层
this->addChild(edgeShape);
接下来注册物理碰撞
//注册物理碰撞
auto contactListener = EventListenerPhysicsContact::create();
contactListener->onContactBegin = CC_CALLBACK_1(HelloWorld::onContactBegin, this);
_eventDispatcher->addEventListenerWithSceneGraphPriority(contactListener, this);
做完这些,物理世界以及监听都好了,需要做的就是创建碰撞对象了,也就是刚体,我的做法很简单,就是创建了二个精灵而已,然后在绑上2个刚体,然后让二个精灵来回碰撞。
//第一个对象
auto sprite1 = Sprite::create("jinbitubiao.png");
sprite1->setPosition(Vec2(200, 100));
this->addChild(sprite1);
//动作
auto moveBy1 = MoveBy::create(2,Vec2(500,0));
auto moveBy2 = MoveBy::create(2, Vec2(-500, 0));
sprite1->runAction(RepeatForever::create(Sequence::create(moveBy1, moveBy2,NULL)));
//刚体
auto boxbody1 = PhysicsBody::createBox(sprite1->getContentSize());
boxbody1->setCategoryBitmask(8);//种类
boxbody1->setCollisionBitmask(20);//碰撞
boxbody1->setContactTestBitmask(20);//接触
sprite1->setPhysicsBody(boxbody1);
boxbody1->setTag(100);
//第二个对象
auto sprite2 = Sprite::create("jinbitubiao.png");
sprite2->setPosition(Vec2(1000, 100));
this->addChild(sprite2);
//动作
auto MoveBy1 = MoveBy::create(2, Vec2(-500, 0));
auto MoveBy2 = MoveBy::create(2, Vec2(500, 0));
sprite2->runAction(RepeatForever::create(Sequence::create(MoveBy1, MoveBy2, NULL)));
//刚体
auto boxbody2 = PhysicsBody::createBox(sprite2->getContentSize());
boxbody2->setCategoryBitmask(16);//种类
boxbody2->setCollisionBitmask(40);//碰撞
boxbody2->setContactTestBitmask(40);//接触
sprite2->setPhysicsBody(boxbody2);
boxbody2->setTag(200);
监听函数实现
bool HelloWorld::onContactBegin(cocos2d::PhysicsContact& contact)
{
static int num = 0;
CCLOG("onContactBegin%d",num);
num = num + 1;
auto who1 = contact.getShapeA()->getBody()->getTag();
auto who2 = contact.getShapeB()->getBody()->getTag();
CCLOG("onContactBegin----Tag---%d%d", who1, who2);
return false;
}
这样程序跑起来之后你就会发现,碰撞到一下就会打印日志:onContactBegin----Tag---100200
到了这里才刚刚开始,虽然碰撞成功了,但是对于下面三个函数:
setCategoryBitmask、setCollisionBitmask、setContactTestBitmask
完全不知啥意思,只是象征性的写一个数字上去。
后来经过探索,总结了一下自己的领悟:
setCategoryBitmask
定义了这个物理刚体是属于哪个类别的掩码。在一个场景中的每个物理刚体可以分配给达到 32 不同的类别(参数int bitmask是int类型4个字节32位),每个对应有32位中的1位掩码。您的游戏中您定义使用的掩码值。联同的 collisionBitMask 和 contactTestBitMask 的属性,定义哪些物理刚体彼此之间进行交互和何时你接收到这些交互作用的通知。默认值为的 0xFFFFFFFF (所有的位都被设置)。
setCollisionBitmask
一个掩码,它定义了哪些类别的刚体与此物理刚体产生交集(相互作用)的通知。当两个刚体共有同一个空间时,通过执行逻辑与运算每个刚体的类别掩码被检测测试反对其他的刚体的接触掩码。如果任一比较结果在一个非零值,一个 PhysicsContact 对象被创建并传递到物理世界的委托。为获得最佳性能,仅设置您感兴趣的互动相互作用的接触掩码位。默认值为 0x00000000 (所有位均被清除)。
setContactTestBitmask
一个掩码,它定义了哪些类别的物理刚体可以与这物理刚体发生碰撞。当两个物理刚体互相接触时,可能会发生冲突。这个刚体的碰撞掩码被相比其他刚体的类别掩码通过执行按位逻辑与运算。如果结果是一个非零值,则这一刚体被受碰撞。每个刚体独立地选择是否愿意受其他刚体的影响。例如,您可能会使用这来避免碰撞计算使对刚体的速度的变化可以忽略。默认值为的 0xFFFFFFFF (所有的位设置)。
说了这么多是不是还不懂,嗯,我就是一脸懵。下面在详细说一下:
box1,box2,box3三个刚体
box1->getPhysicsBody()->setCategoryBitmask(0x01); // 0001
box1->getPhysicsBody()->setContactTestBitmask(0x04); // 0100
box1->getPhysicsBody()->setCollisionBitmask(0x03); // 0011
box2->getPhysicsBody()->setCategoryBitmask(0x02); // 0010
box2->getPhysicsBody()->setContactTestBitmask(0x08); // 1000
box2->getPhysicsBody()->setCollisionBitmask(0x01); // 0001
box3->getPhysicsBody()->setCategoryBitmask(0x04); // 0100
box3->getPhysicsBody()->setContactTestBitmask(0x01); // 0001
box3->getPhysicsBody()->setCollisionBitmask(0x06); // 0110
结果:
box1 和 box2 发生碰撞
box1 box3 不会
box2 box3 也不会
为什么呢? 解释如下:
box1的类别掩码 0001
可接到通知 0100
允许撞我 0011
box2的类别掩码 0010
可接到通知 1000
允许撞我 0001
box3的类别掩码 0100
可接到通知 0001
允许撞我 0110
现在做运算呗:
box1的允许撞我 0011 与box2的类别掩码做按位与运算
box2的类别掩码 0010
结果为: 0010 不为0, box1会被box2撞到。
同理:
box2的允许撞我 0001 与box1的类别掩码做按位与运算
box1的类别掩码 0001
结果为: 0001 不为0, box2会被box1撞到。
他们会相互受到撞击的。
box2的允许撞我与box3的类别掩码 按位与运算为0;
box3的允许撞我与box2的类别掩码 按位与运算为0;
box1的允许撞我与box3的类别掩码 按位与运算为0;
box3的允许撞我与box1的类别掩码 按位与运算为0;
所以:
box1 and box2 发生碰撞
but the box1 box3 不会
the box2 box3 也不会
这样在游戏中,我们如果不想让二个不同类别的刚体碰撞得话,就可以设置这些掩码来实现,比如捕鱼游戏中的鱼和鱼就不需要碰撞检测,只需要子弹和鱼进行碰撞检测。