关于题目这3个掩码的意思,看官方文档肯定会蒙圈。尤其对于初学者更是不知所云。但是,这个概念在游戏开发又非常重要,所以必须搞明白。我自认为自己的表达还算清晰,所以标题用了“最易懂”,希望各路大神看到不要喷我。
首先,弄清楚2个概念,一是碰撞;二是接触后产生回调。碰撞是真实的物理效果,即两个物体碰撞后会相互弹开,这个非常好理解。那么,接触后产生回调,这里面的“接触”,可以代表碰撞,也可以是交叉错开。当这种情况发生的时候会产生消息,即我们的回调函数会被执行,你可以在里面写你想要的的动作。这3个掩码的作用,就是控制哪些物体可以相互碰撞,哪些物体接触后会产生消息。
setCategoryBitmask和setCollisionBitmask,与运算(&)的结果用来控制是否碰撞。
setCategoryBitmask和setContactTestBitmask,与运算(&)的结果用来控制是否接触后产生消息。
分开来举例讲解(假设有两个物体s1和s2):
1.碰撞
发生碰撞的条件:s1的CategoryBitmask与(&)上s2的CollisionBitmask不为0,并且s2的CategoryBitmask与(&)上s1的CollisionBitmask不为0。即与后的结果都不为0,会发生碰撞;只要有一个与的结果为0,则不发生碰撞。
比如有3个精灵,按照下面的bitMask进行设定:
// 精灵1
setCategoryBitmask(0x01); // 0001
setCollisionBitmask(0x03); // 0011
// 精灵2
setCategoryBitmask(0x01); // 0001
setCollisionBitmask(0x03); // 0011
// 精灵3
setCategoryBitmask(0x04); // 0100
setCollisionBitmask(0x06); // 0110
精灵1和精灵2可以相互碰撞,精灵3和谁也不会碰撞。
演示代码如下:
bool HelloWorld::init()
{
if (!Scene::initWithPhysics()){ return false; }
auto visibleSize = Director::getInstance()->getVisibleSize();
auto origin = Director::getInstance()->getVisibleOrigin();
auto sprite1 = Sprite::create("s1.png");
sprite1->setPosition(Vec2(50, 400));
auto physicsBody1 = PhysicsBody::createBox(sprite1->getContentSize(), PhysicsMaterial(0.1f, 1.0f, 0.0f));// 密度,修复,摩擦
physicsBody1->setVelocity(Vec2(400, 200));
physicsBody1->setCategoryBitmask(0x01); // 0001
physicsBody1->setCollisionBitmask(0x03); // 0011
sprite1->setPhysicsBody(physicsBody1);
this->addChild(sprite1);
auto sprite2 = Sprite::create("s2.png");
sprite2->setPosition(Vec2(950, 400));
auto physicsBody2 = PhysicsBody::createBox(sprite2->getContentSize(), PhysicsMaterial(0.1f, 1.0f, 0.0f));// 密度,修复,摩擦
physicsBody2->setVelocity(Vec2(-400, 200));
physicsBody2->setCategoryBitmask(0x01); // 0001
physicsBody2->setCollisionBitmask(0x03); // 0011
sprite2->setPhysicsBody(physicsBody2);
this->addChild(sprite2);
auto sprite3 = Sprite::create("s3.png");
sprite3->setPosition(Vec2(50, 200));
auto physicsBody3 = PhysicsBody::createBox(sprite3->getContentSize(), PhysicsMaterial(0.1f, 1.0f, 0.0f));// 密度,修复,摩擦
physicsBody3->setVelocity(Vec2(-400, 200));
physicsBody3->setCategoryBitmask(0x04); // 0100
physicsBody3->setCollisionBitmask(0x06); // 0110
sprite3->setPhysicsBody(physicsBody3);
this->addChild(sprite3);
// 制作一个外围墙
auto wall = Node::create();
wall->addComponent(PhysicsBody::createEdgeBox(visibleSize, PhysicsMaterial(0.1f, 1, 0.0f)));
wall->setPosition(origin.x + visibleSize.width / 2, origin.y + visibleSize.height / 2);
this->addChild(wall);
return true;
}
效果如下:

2.接触产生回调消息
发生条件:s1的CategoryBitmask与(&)上s2的ContactTestBitmask不为0,并且s2的CategoryBitmask与(&)上s1的ContactTestBitmask不为0。即与后的结果都不为零,会产生消息;只要有一个与的结果为0,则不产生消息。
// 精灵1
setCategoryBitmask(0x01); // 0001
setContactTestBitmask(0x04); // 0100
// 精灵2
setCategoryBitmask(0x01); // 0001
setContactTestBitmask(0x04); // 0100
// 精灵3
setCategoryBitmask(0x04); // 0100
setContactTestBitmask(0x01); // 0001
上面的例子,精灵1和精灵2接触不产生消息,精灵3和精灵1,2接触都会产生消息。
我们将演示代码修改如下:
bool HelloWorld::init()
{
if (!Scene::initWithPhysics()){ return false; }
auto visibleSize = Director::getInstance()->getVisibleSize();
auto origin = Director::getInstance()->getVisibleOrigin();
auto sprite1 = Sprite::create("s1.png");
sprite1->setPosition(Vec2(50, 400));
auto physicsBody1 = PhysicsBody::createBox(sprite1->getContentSize(), PhysicsMaterial(0.1f, 1.0f, 0.0f));// 密度,修复,摩擦
physicsBody1->setVelocity(Vec2(400, 200));
physicsBody1->setCategoryBitmask(0x01); // 0001
physicsBody1->setContactTestBitmask(0x04); // 0100
physicsBody1->setCollisionBitmask(0x03); // 0011
sprite1->setPhysicsBody(physicsBody1);
sprite1->setTag(10);
this->addChild(sprite1);
auto sprite2 = Sprite::create("s2.png");
sprite2->setPosition(Vec2(950, 400));
auto physicsBody2 = PhysicsBody::createBox(sprite2->getContentSize(), PhysicsMaterial(0.1f, 1.0f, 0.0f));// 密度,修复,摩擦
physicsBody2->setVelocity(Vec2(-400, 200));
physicsBody2->setCategoryBitmask(0x01); // 0001
physicsBody2->setContactTestBitmask(0x04); // 0100
physicsBody2->setCollisionBitmask(0x03); // 0011
sprite2->setPhysicsBody(physicsBody2);
sprite2->setTag(20);
this->addChild(sprite2);
auto sprite3 = Sprite::create("s3.png");
sprite3->setPosition(Vec2(50, 200));
auto physicsBody3 = PhysicsBody::createBox(sprite3->getContentSize(), PhysicsMaterial(0.1f, 1.0f, 0.0f));// 密度,修复,摩擦
physicsBody3->setVelocity(Vec2(-400, 200));
physicsBody3->setCategoryBitmask(0x04); // 0100
physicsBody3->setContactTestBitmask(0x01); // 0001
physicsBody3->setCollisionBitmask(0x06); // 0110
sprite3->setPhysicsBody(physicsBody3);
sprite3->setTag(30);
this->addChild(sprite3);
// 制作一个外围墙
auto wall = Node::create();
wall->addComponent(PhysicsBody::createEdgeBox(visibleSize, PhysicsMaterial(0.1f, 1, 0.0f)));
wall->setPosition(origin.x + visibleSize.width / 2, origin.y + visibleSize.height / 2);
this->addChild(wall);
auto contactListener = EventListenerPhysicsContact::create();
contactListener->onContactBegin = CC_CALLBACK_1(HelloWorld::onContactBegin, this);
_eventDispatcher->addEventListenerWithSceneGraphPriority(contactListener, this);
return true;
}
bool HelloWorld::onContactBegin(PhysicsContact& contact)
{
auto nodeA = contact.getShapeA()->getBody()->getNode();
auto nodeB = contact.getShapeB()->getBody()->getNode();
if (nodeA && nodeB)
{
int tagA = nodeA->getTag();
int tagB = nodeB->getTag();
log("onContact!! tagA = %d, tagB = %d", tagA, tagB);
}
return true;
}
为了区分它们谁是谁,我用setTag()分别给精灵附上标签10,20,30。然后在消息的回调函数里,把触发消息的两个精灵的Tag给打印出来。执行的效果和上面动图是一样的,我们看一下输出的log。可以看到,只有10和30,20和30的输出,也就证明了预想结果是正确的。
onContact!! tagA = 20, tagB = 30
onContact!! tagA = 10, tagB = 30
onContact!! tagA = 30, tagB = 10
onContact!! tagA = 30, tagB = 20
onContact!! tagA = 10, tagB = 30
onContact!! tagA = 30, tagB = 10
onContact!! tagA = 30, tagB = 10
onContact!! tagA = 10, tagB = 30
onContact!! tagA = 20, tagB = 30
setCategoryBitmask、setCollisionBitmask和setContactTestBitmask,它们3个可以一起使用,也可以两两独立使用互不影响。就看你的需求是什么。
3.物理刚体的动态属性setDynamic
这个physicsBody有一个动态属性,是由setDynamic()来设定,默认是true。动态属性是指这个物体是不是模拟真实的物体,即有没有重量、速度、摩擦等属性。如果设定为false,就是静态的,即使设了真实物体的那些属性,也是没有用的。
如果你想产生碰撞效果,则必须physicsBody->setDynamic(true)(或使用默认值,不写也可以)。
而接触产生消息,则对动态属性没有要求。也就是你的刚体动态属性全是true,或者全是false,或者两者都有,只要满足消息的产生条件,都可以产生消息。
4.最后在倒逼一下setGroup
使用setGroup(Index)可以给你物体分组,这样也可以控制物体是否可以碰撞。它比位掩码具有更高的优先级。指定了group,位掩码的设定就失效了。
这个Index(索引)的意思:正数:碰撞; 负数:不碰撞。
比如还是上面的例子,
设定3个精灵的索引都为1,则3个精灵间都会碰撞;
设定3个精灵的索引分别为1,2,1,则精灵1和3碰撞,精灵2和他们不碰撞;
设定3个精灵的索引分别为1,2,3,则按照位掩码判断是否碰撞(因为他们根本不在一个组里);
设定3个精灵的索引都为-1,则3个精灵间不会碰撞;
其他就不举例了。
代码也不举例了,可以自己试验一下,比较简单。
本文详细解析cocos2d-x中setCategoryBitmask、setCollisionBitmask和setContactTestBitmask的含义,通过实例说明如何控制物体间的碰撞及接触产生回调。动态属性setDynamic对碰撞效果至关重要,而setGroup则提供了一种通过分组控制碰撞的高级方式。
物理碰撞--setCategoryBitmask、setCollisionBitmask和setContactTestBitmask最易懂的解释&spm=1001.2101.3001.5002&articleId=105402009&d=1&t=3&u=ef2728187db9497c8aa33538075a1679)
1724

被折叠的 条评论
为什么被折叠?



