I'm attempting to check for specific collisions between sprites with Physics bodies. The problem is that I don't know how to assign either a tag to each node physics body or set the collision bitmask properly for each sprite. I am successfully checking for collisions but it's only a true or false for all objects.
I'm following a guide in the cocos2d-x documentation that I've linked below. In it, it explains how to set masking bits and masking categories.
Collision filtering allows you to prevent collision between shapes. Cocos2d-x >supports collision filtering using category and groups bitmasks.
Cocos2d-x supports 32 collision categories. For each shape you can specify which category it belongs to. You also specify what other categories this shape can collide with. This is done with masking bits
The problem is that when I set:
sprite1->getPhysicsBody()->setCategoryBitmask(0x02); // 0010
sprite1->getPhysicsBody()->setCollisionBitmask(0x01); // 0001
There isn't collision detection for those sprites. But when I set:
invaderPhysicsBody->setContactTestBitmask(true);
There is a collision detection.
Does anybody know how to successfully set the collision bitmask or category bitmask?
Objects with possible collisions:
This is my onContactBegin function that will remove the Node once they collide. This is where I need to test which objects collided.
bool Gameplay::onContactBegin(PhysicsContact &contact) {
std::cout << "onContactBegin -------> " << std::endl;
player_score += 10;
auto nodeA = contact.getShapeA()->getBody()->getNode();
auto nodeB = contact.getShapeB()->getBody()->getNode();
std::cout << contact.getShapeA()->getTag() << std::endl;
if ((contact.getShapeA()->getCategoryBitmask() & contact.getShapeB()->getCollisionBitmask()) == 0
|| (contact.getShapeB()->getCategoryBitmask() & contact.getShapeA()->getCollisionBitmask()) == 0)
{
std::cout << "Overlap!" << std::endl;
}
nodeA->removeFromParent();
nodeB->removeFromParent();
return true;
}
Here is my collision listener:
// Enable collision listener.
auto contactListener = EventListenerPhysicsContact::create();
contactListener->onContactBegin = CC_CALLBACK_1(Gameplay::onContactBegin, this);
this->getEventDispatcher()->addEventListenerWithSceneGraphPriority(contactListener, this);
This is how I currently create and set the Physics bodies for my sprites:
int spacing = 0;
// Four Player Shields
for (int i = 0; i < 4; i++) {
auto shield = Sprite::create("player_shield.png");
auto shield_x = shield->getContentSize();
auto shield_y = shield->getContentSize();
auto shieldPhysicsBody = PhysicsBody::createBox(Size(shield_x.width,shield_y.height), PHYSICSBODY_MATERIAL_DEFAULT);
shieldPhysicsBody->setContactTestBitmask(true);
shieldPhysicsBody->setDynamic(true);
shieldPhysicsBody->setTag(0);
shield->setPosition(Vec2(200 + spacing, 150));
shield->addComponent(shieldPhysicsBody);
this->addChild(shield);
spacing += 200;
}
There is a big mistake in your code. setContactTestBitmask
accept a int
rather than bool
. When you call shieldPhysicsBody->setContactTestBitmask(true)
, it's actually shieldPhysicsBody->setContactTestBitmask(0x0000001)
. And 0x000001 & 0x000010 = 0
, so no contact event will be raised. The &
result of spriteA's categoryBitmask and spriteB's ContactTestBitmask determines whether a contact event will be raised. BTW, generally speaking, you don't need check contaction manually, ie:
if ((contact.getShapeA()->getCategoryBitmask() & contact.getShapeB()->getCollisionBitmask()) == 0
|| (contact.getShapeB()->getCategoryBitmask() & contact.getShapeA()->getCollisionBitmask()) == 0)
{
std::cout << "Overlap!" << std::endl;
}