使用box2d cocos2d
,当b2body
触及另一个时,我需要播放一个点击声。
我知道如何检测碰撞。问题是,在碰撞之后,它们有时仍会相互碰触,我不想再次播放这种声音(就像在现实世界中,如果2个物体仅以高速碰撞会发出声音)
所以,只有当它们达到一定的速度/加速度时才需要播放声音。
碰撞后能否获得身体速度?
//some code to meet the standards of the site :
int speed=body->getInertzia;
if(speed>10)
sound!
答案 0 :(得分:1)
要随时获得身体的速度,您可以执行以下操作:
b2Vec2 velocity = body->GetLinearVelocity();
float speedSquared = velocity.LengthSquared(); // Use squared to save on square root calc.
if(speedSquared > thresholdSquared)
{
// Play sound
}
你仍然需要找到哪些机构在上次播放声音时发生碰撞和跟踪,这样你就不会太快再次播放。
Box2d会在你的侦听器第一次碰撞并分别打破碰撞时给你一个beginContact和一个endContact回调。所以你只需要根据beginContact调用来定位你的碰撞声。
但是,您还需要过滤调用,因为在解决冲突的过程中您将获得对beginContact / endContact的多次调用。
如果物体相互反弹很多(多个碰撞对象),最好跟踪上次为某个特定实体发出碰撞声而不是让它再次制作一个实体。
这是我使用的碰撞过滤器:
我使用类(下面)进行冲突处理,该类过滤掉重复冲突并通过邮件系统向“实体”对象发送通知。您可以使用它来排列应该播放的声音效果。
注意:这是更大代码库的一部分;如果您需要澄清,请随时提出任何问题,因为代码不在此处。
class EntityContactListener : public ContactListener
{
private:
GameWorld* _gameWorld;
EntityContactListener() {}
typedef struct
{
Entity* entA;
Entity* entB;
} CONTACT_PAIR_T;
vector<CONTACT_PAIR_T> _contactPairs;
public:
virtual ~EntityContactListener() {}
EntityContactListener(GameWorld* gameWorld) :
_gameWorld(gameWorld)
{
_contactPairs.reserve(128);
}
void NotifyCollisions()
{
Message* msg;
MessageManager& mm = GameManager::Instance().GetMessageMgr();
for(uint32 idx = 0; idx < _contactPairs.size(); idx++)
{
Entity* entA = _contactPairs[idx].entA;
Entity* entB = _contactPairs[idx].entB;
//DebugLogCPP("Contact Notification %s<->%s",entA->ToString().c_str(),entB->ToString().c_str());
msg = mm.CreateMessage();
msg->Init(entA->GetID(), entB->GetID(), Message::MESSAGE_COLLISION);
mm.EnqueueMessge(msg, 0);
msg = mm.CreateMessage();
msg->Init(entB->GetID(), entA->GetID(), Message::MESSAGE_COLLISION);
mm.EnqueueMessge(msg, 0);
}
_contactPairs.clear();
}
void PreSolve(b2Contact* contact, const b2Manifold* oldManifold)
{
}
// BEWARE: You may get multiple calls for the same event.
void BeginContact(b2Contact* contact)
{
Entity* entA = (Entity*)contact->GetFixtureA()->GetBody()->GetUserData();
Entity* entB = (Entity*)contact->GetFixtureB()->GetBody()->GetUserData();
//DebugLogCPP("Begin Contact %s->%s",entA->ToString().c_str(),entB->ToString().c_str());
if(entA->GetGroupID() == entB->GetGroupID())
{ // Can't collide if they are in the same group.
return;
}
assert(entA != NULL);
assert(entB != NULL);
for(uint32 idx = 0; idx < _contactPairs.size(); idx++)
{
if(_contactPairs[idx].entA == entA && _contactPairs[idx].entB == entB)
return;
// Not sure if this is needed...
if(_contactPairs[idx].entA == entB && _contactPairs[idx].entA == entB)
return;
}
CONTACT_PAIR_T pair;
pair.entA = entA;
pair.entB = entB;
_contactPairs.push_back(pair);
}
// BEWARE: You may get multiple calls for the same event.
void EndContact(b2Contact* contact)
{
/*
Entity* entA = (Entity*)contact->GetFixtureA()->GetBody()->GetUserData();
Entity* entB = (Entity*)contact->GetFixtureB()->GetBody()->GetUserData();
DebugLogCPP("End Contact %s->%s",entA->ToString().c_str(),entB->ToString().c_str());
*/
}
};
答案 1 :(得分:0)
有多种方法可以做到这一点。
最简单的方法是使用每个物体的GetLinearVelocity,在物体击中时使用物体的速度差异。
您可以使用GetLinearVelocityFromWorldPoint比较实际碰撞位置的速度,以获得更准确的值。
考虑到物体的质量,你可以看一下物理引擎用来推动身体分开的脉冲的大小。这可以在碰撞监听器的PostSolve函数中完成。请注意,PostSolve事件将在两个实体接触时连续发生,因此对于控制较长的声音(如刮擦)而非初始影响更为有用。
在每种情况下,您可能希望在播放声音之前检查该值是否高于某个阈值。当身体彼此滑动时,身体可能会有微小的分离,但是会导致很多BeginContact / EndContact事件。
根据我的经验,这是一个非常棘手的事情。您可能还想检查在过去的n毫秒内是否还没有为该联系人播放声音,以避免口吃,尤其是在使用PostSolve方法时,发生的重碰撞超过了多个时间步长的阈值。