cocos2d-x 4.0 学习之路(十六)物理碰撞--setCategoryBitmask、setCollisionBitmask和setContactTestBitmask最易懂的解释

本文详细解析cocos2d-x中setCategoryBitmask、setCollisionBitmask和setContactTestBitmask的含义,通过实例说明如何控制物体间的碰撞及接触产生回调。动态属性setDynamic对碰撞效果至关重要,而setGroup则提供了一种通过分组控制碰撞的高级方式。

关于题目这3个掩码的意思,看官方文档肯定会蒙圈。尤其对于初学者更是不知所云。但是,这个概念在游戏开发又非常重要,所以必须搞明白。我自认为自己的表达还算清晰,所以标题用了“最易懂”,希望各路大神看到不要喷我。

首先,弄清楚2个概念,一是碰撞;二是接触后产生回调。碰撞是真实的物理效果,即两个物体碰撞后会相互弹开,这个非常好理解。那么,接触后产生回调,这里面的“接触”,可以代表碰撞,也可以是交叉错开。当这种情况发生的时候会产生消息,即我们的回调函数会被执行,你可以在里面写你想要的的动作。这3个掩码的作用,就是控制哪些物体可以相互碰撞,哪些物体接触后会产生消息。

setCategoryBitmasksetCollisionBitmask,与运算(&)的结果用来控制是否碰撞。
setCategoryBitmasksetContactTestBitmask,与运算(&)的结果用来控制是否接触后产生消息。

分开来举例讲解(假设有两个物体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个精灵间不会碰撞;
其他就不举例了。
代码也不举例了,可以自己试验一下,比较简单。

评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值