上篇




碰撞

你是否经历过车祸?是否跟什么物体相撞过?就像车一样,物理刚体对象可以互相接触。当它们接触的时候,就发生了碰撞。当碰撞发生时,它可以被完全忽略,也可以引起一系列事件。

碰撞筛选

碰撞筛选允许你启用或者阻止形状之间碰撞的发生。物理引擎支持使用类型、组位掩码来筛选碰撞。

Cocos2d-x中支持32种碰撞类型。对于每个形状都可以指定其所属的类型。还可以指定有哪些类型可以与这个形状进行碰撞。这些是通过掩码来完成的。例如

auto

 sprite1 = addSpriteAtPosition(Vec2(s_centre.x - 150,s_centre.y));

sprite1->getPhysicsBody()->setCategoryBitmask(0x02);// 0010

sprite1->getPhysicsBody()->setCollisionBitmask(0x01);  // 0001

 

sprite1

 = addSpriteAtPosition(Vec2(s_centre.x - 150,s_centre.y + 100));

sprite1->getPhysicsBody()->setCategoryBitmask(0x02);// 0010

sprite1->getPhysicsBody()->setCollisionBitmask(0x01);  //

 0001

 

auto

 sprite2 = addSpriteAtPosition(Vec2(s_centre.x + 150,s_centre.y),1);

sprite2->getPhysicsBody()->setCategoryBitmask(0x01);// 0001

sprite2->getPhysicsBody()->setCollisionBitmask(0x02);  // 0010

 

auto

 sprite3 = addSpriteAtPosition(Vec2(s_centre.x + 150,s_centre.y + 100),2);

sprite3->getPhysicsBody()->setCategoryBitmask(0x03);// 0011

sprite3->getPhysicsBody()->setCollisionBitmask(0x03);  // 0011

可以通过检测、类型比较和碰撞掩码,来确定碰撞的发生:

if((shapeA->getCategoryBitmask() & shapeB->getCollisionBitmask()) == 0 || (shapeB->getCategoryBitmask() & shapeA->getCollisionBitmask()) == 0)

{

   //

 shapes can't collide

   ret

 = false;

}


碰撞组允许你指定一个综合组的索引。你可以让具有同一组之索引的形状全都一直碰撞(正索引)或者永不碰撞(负索引或零索引)。组指数不同的形状间进行的碰撞,可以根据类型和掩码来进行筛选。换句话说,组筛选比类型筛选的优先级更高。

连接/关节

还记得之前的术语吗?关节是一种把接触点联结在一起的方式。没错,你可以把它类比为自己身体上的关节。每一个关节都有一个从PhysicsJoint对象获得的定义。在两个不同的刚体之间,所有的关节都是联结在一起的。刚体可以是静态的。你可以使用joint->setCollisionEnable(false)来避免相关联的刚体相互碰撞。很多关节的定义需要你提供一些几何数据。很多情况下,关节由锚点来定义。其余的关节定义数据取决于关节的类型。

  • PhysicsJointFixed:固定关节在一个特定的点上,将两个刚体结合在了一起。如果要创建一些以后会断裂的复杂形状,固定关节是非常有用的。

  • PhysicsJointLimit:一种限制关节,它利用了两个刚体间最大距离,就如同两个刚体被绳子连在一起一样。

  • PhysicsJointPin:针式关节可以让两个刚体独立地围绕锚点进行旋转,就如同它们被钉在一起了一样。

  • PhysicsJointDistance:设定两个刚体间的固定距离。

  • PhysicsJointSpring:用弹簧来联结两个物理刚体

  • PhysicsJointGroove:将一个刚体连到线上,另一个连到点上。

  • PhysicsJointRotarySpring:与弹簧关节相似,但是增加了自旋

  • PhysicsJointRotaryLimit:与限制关节相似,但是增加了自旋

  • PhysicsJointRatchet:与套筒扳手的工作类似

  • PhysicsJointGear:使一对刚体的角速度比率保持是一个常数。

  • PhysicsJointMotor:使一对刚体的相对角速度保持是一个常数。 


碰撞检测

碰撞(Contacts)是一种由物理引擎创建的用以管理两个形状间碰撞的对象。Contact对象会自动创建,而非由用户创建。这里有几个与之相关联的术语。

  • contact point:contact point是两个形状相接触的那个点。

  • contact normal:contact normal指的是从一个形状指向另一个形状的单位向量。

你可以从一个碰撞中获取PhysicsShape。从中,你可以获取刚体。

boolonContactBegin(PhysicsContact& contact)

{

    auto

 bodyA = contact.getShapeA()->getBody();

    auto

 bodyB = contact.getShapeB()->getBody();

    returntrue;

}

你可以通过使用contact listener来访问碰撞。contact listener支持多种事件:begin,pre-solve,post-solve,以及separate

  • begin:在这一步骤中,两个形状刚刚开始接触。从回调函数中返回true,可以使碰撞正常发生,若返回false,则物理引擎会将碰撞整个忽略掉。如果返回false值,preSolve()postSolve()回调函数会被禁止运行,不过当两个形状停止重叠时,你仍然可以收到一个单独的事件。

  • pre-solve:在这一步骤中,两个形状接触在一起。如果在回调函数中返回false值,则物理引擎会忽略掉此次碰撞;若返回true,碰撞则会正常进行。此外,你可以使用setRestitution()setSurfaceVelocity()函数来忽略碰撞值,这样就可以提供自定义的恢复系数、摩擦系数和表面速度值。

  • post-solve:两个形状相接触,而它们之间的碰撞已被处理。

  • separate:在这一步骤中,两个形状刚刚停止接触。

也可以使用EventListenerPhysicsContactWithBodies,EventListenerPhysicsContactWithShapes,EventListenerPhysicsContactWithGroup来监听你感兴趣的刚体、形状和组的一些事件。除此之外,你还需要设定与物理接触相关的掩码,因为就算你创建了相关的EventListener,碰撞事件还是不会在默认状态下被接收。

例如:



boolinit()

{

    //create

 a static PhysicsBody

    auto

 sprite = addSpriteAtPosition(s_centre,1);

    sprite->setTag(10);

    sprite->getPhysicsBody()->setContactTestBitmask(0xFFFFFFFF);

    sprite->getPhysicsBody()->setDynamic(false);

 

    //adds

 contact event listener

    auto

 contactListener = EventListenerPhysicsContact::create();

    contactListener->onContactBegin

 = CC_CALLBACK_1(PhysicsDemoCollisionProcessing::onContactBegin, this);

    _eventDispatcher->addEventListenerWithSceneGraphPriority(contactListener,this);

 

        schedule(CC_SCHEDULE_SELECTOR(PhysicsDemoCollisionProcessing::tick),

 0.3f);

    returntrue;

 

returnfalse;

}

 

voidtick(floatdt)

{

    auto

 sprite1 = addSpriteAtPosition(Vec2(s_centre.x + cocos2d::random(-300,300),

  s_centre.y

 + cocos2d::random(-300,300)));

    auto

 physicsBody = sprite1->getPhysicsBody();

    physicsBody->setVelocity(Vec2(cocos2d::random(-500,500),cocos2d::random(-500,500)));

    physicsBody->setContactTestBitmask(0xFFFFFFFF);

}

 

boolonContactBegin(PhysicsContact& contact)

{

    auto

 nodeA = contact.getShapeA()->getBody()->getNode();

    auto

 nodeB = contact.getShapeB()->getBody()->getNode();

 

    if(nodeA && nodeB)

    {

        if(nodeA->getTag() == 10)

        {

            nodeB->removeFromParentAndCleanup(true);

        }

        elseif (nodeB->getTag() == 10)

        {

            nodeA->removeFromParentAndCleanup(true);

        }

    }

 

    //bodies

 can collide

    returntrue;

}

查询

你有没有过站在一个地方往四周看的经历?你能看到离你近的东西,也能看到离你远的东西。你能判断出它们离你有多远。物理引擎提供类似的空间查询功能。PhysicsWorld对象目前支持的查询包括点查询、射线查询和矩形查询。

点查询

当你碰到什么东西,比如说你的桌子的时候,你可以将此认为是一个点查询的例子。这使你能够检查在一个点周围的一定距离内是否有形状存在。对于鼠标拾取和简单的传感器来说,点查询是非常有用的。你还可以找到在一个形状上离某定点最近的点,或者找到离某个点最近的形状。

射线查询

当你四处看的时候,在你视线内的某些物体肯定会引起你的注意。像这样的时候,你基本上就算是执行了一次射线查询。你不停地扫描,直到有什么有趣的东西让你停下来。你可以使用对某个形状使用射线查询来获取第一个交叉点。例如:

void tick(float dt)

{

    Vec2

 d(300 * cosf(_angle), 300 * sinf(_angle));

    Vec2

 point2 = s_centre + d;

    if(_drawNode)

    {

        removeChild(_drawNode);

    }

    _drawNode

 = DrawNode::create();

 

    Vec2

 points[5];

    intnum = 0;

    auto

 func = [&points, &num](PhysicsWorld& world,

    constPhysicsRayCastInfo& info, void*

 data)->bool

    {

        if(num < 5)

        {

            points[num++]

 = info.contact;

        }

        returntrue;

    };

 

    s_currScene->getPhysicsWorld()->rayCast(func,

 s_centre, point2, nullptr);

 

    _drawNode->drawSegment(s_centre,

 point2, 1, Color4F::RED);

    for(inti = 0; i < num; ++i)

    {

        _drawNode->drawDot(points[i],

 3, Color4F(1.0f, 1.0f, 1.0f, 1.0f));

    }

    addChild(_drawNode);

 

    _angle

 += 1.5f * (float)M_PI

 / 180.0f;

}

矩形查询

矩形查询提供了一个大致检查区域中存在的形状的一种快捷方式。它非常容易实现:

auto

 func = [](PhysicsWorld& world, PhysicsShape& shape, void*

 userData)->bool

{

    //Return

 true from the callback to continue rect queries

    returntrue;

}

 

scene->getPhysicsWorld()->queryRect(func,

 Rect(0,0,200,200), nullptr);

A few examples of using a Rect query while doing a logo smash:

这里是在制作撞击logo时使用矩形查询的几个例子:  

禁用物理引擎

使用内置的物理引擎是个不错的想法。它又稳定又强大。然而,有时候你会想要使用一些其他的物理引擎。这时候你只需要在base/ccConfig.h中把CC_USE_PHYSICS禁用就好了。



上篇