上篇
碰撞
你是否经历过车祸?是否跟什么物体相撞过?就像车一样,物理刚体对象可以互相接触。当它们接触的时候,就发生了碰撞。当碰撞发生时,它可以被完全忽略,也可以引起一系列事件。
碰撞筛选
碰撞筛选允许你启用或者阻止形状之间碰撞的发生。物理引擎支持使用类型、组位掩码来筛选碰撞。
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禁用就好了。
上篇