Question

Using box2d cocos2d, I need to play a hit sound when a b2body touches another one. I know how to detect the collision. The problem is that after they collide, they sometimes still touch one another, and I DON'T want to play that sound again (like in real world ,a sound happen if 2 bodies collide at high speed only)

So, I need to play the sound only if they hit in a certain speed/acceleration.

Can i get the body speed when collide?

//some code to meet the standards of the site :
int speed=body->getInertzia;
if(speed>10)
sound!
Was it helpful?

Solution 2

There are a variety of ways you can do this.

The simplest would be to use the difference in velocity of the objects at the time they hit, using GetLinearVelocity for each body.

You could use GetLinearVelocityFromWorldPoint to compare the velocities at the actual collision location, for a more accurate value.

To take into account the mass of the bodies as well, you could look at the magnitude of the impulses that the physics engine applied to push the bodies apart. This can be done in the PostSolve function of the collision listener. Note that the PostSolve event will occur continuously while the two bodies are in contact, so it is more useful for controlling longer sounds like scraping, rather than the initial impact.

In each of these cases you would probably want to check that the value is above a certain threshold before playing a sound. Bodies can have tiny separations as they slide across each other that you can barely see, but will cause many BeginContact/EndContact events.

In my experience this is a pretty tricky thing to manage. You may also want to check that a sound has not already been played for the contact in the last n milliseconds, to avoid stuttering, especially when using the PostSolve method and a heavy collision occurs which exceeds the threshold value for multiple timesteps.

See this for more details: http://www.iforce2d.net/b2dtut/collision-anatomy

OTHER TIPS

To get the speed of the bodies at any time, you can do something like the following:

b2Vec2 velocity = body->GetLinearVelocity();
float speedSquared = velocity.LengthSquared(); // Use squared to save on square root calc.
if(speedSquared > thresholdSquared)
{
   // Play sound
}

You will still have to find which bodies collided and keep track of when the last time they played a sound so you don't play it again too soon.

Box2d will give you a beginContact and an endContact callback on your listener when they first collide and break the collision respectively. So you just need to cue up your collision sound based on the beginContact call.

However, you also need to filter the calls because you will get multiple calls to beginContact/endContact over the course of the solving of the collision.

If bodies are bouncing into each other a lot (multiple colliding objects), it is probably better to keep track of the last time a collision sound was made for a particular entity and not let it make one too soon again.

This is the collision filter I used:

I do my collision processing using a class (below) that filters out duplicate collisions and sends notifications to the "Entity" objects via a messaging system. You can use it to enqueue sound effects that should be played.

NOTE: This is part of a larger code base; feel free to ask any questions if you need clarification because the code is not here.

   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());
           */
       }
    };
Licensed under: CC-BY-SA with attribution
Not affiliated with StackOverflow
scroll top