Search code examples
game-enginegame-physicsbulletphysics

Good way to start playing sound when two object collide in Bullet Physics


I am trying to implement audio functionality for collisions in my engine and I struggle to find a good way to do it. I use Bullet Physics and I want to play a sound when two objects collide. I implemented a callback mechanism via gContactAddedCallback so I get callbacks every time two objects collide. The problem I am having is that the callback function can be called multiple times in each game loop and I am not sure how to play audio for collision.

I was thinking to keep some kind of a list that has all the current collisions for a certain object but with this way, I am again not sure as to when to clean the list. I tried to clean the list at each game loop but I am still receiving multiple callbacks for the collision of two objects.

Can anyone please explain or point to a resource that explains a proper way to make physics engine and audio work together?


Solution

  • Ok, so I found one way to do what I was looking for but it seems very unoptimized and kinda hacky. I am still wondering if there is an easy way to do this that I am not seeing. Here is what I did for now. I keep a list of persistent collisions and compare it to another list of current collisions. If current collisions have an entry that does not exist in persistent collision list, I add that to persistent ones and I play a sound. After that, I iterate over persistent collision list and remove all entries that are not contained in current collision list. Here is the code.

    // persistentCollisions map.
    std::map<int, std::vector<int>> persistentCollisions;
    
    // Main game loop
    while (!glfwWindowShouldClose(window.getWindow()))
    {
        //.
        //.
        //.
    
        physicsEngine->getDynamicsWorld()->stepSimulation(1.0f / 60.0f);
    
        // New collision map
        std::map<int, std::vector<int>> newCollisions;
    
        // Go over the collision manifold and extract all existing collisions
        int numManifolds = physicsEngine->getDynamicsWorld()->getDispatcher()->getNumManifolds();
        for (int i = 0; i < numManifolds; i++)
        {
            btPersistentManifold* contactManifold = physicsEngine->getDynamicsWorld()->getDispatcher()->getManifoldByIndexInternal(i);
            const btCollisionObject* obA = contactManifold->getBody0();
            const btCollisionObject* obB = contactManifold->getBody1();
    
            int numContacts = contactManifold->getNumContacts();
            for (int j = 0; j < numContacts; j++)
            {
                btManifoldPoint& pt = contactManifold->getContactPoint(j);
                if (pt.getDistance() < 0.f)
                {
                    // If it is a new collision, add to the newCollision list
                    if (std::find(newCollisions[obA->getUserIndex()].begin(), newCollisions[obA->getUserIndex()].end(), obB->getUserIndex()) == newCollisions[obA->getUserIndex()].end()) 
                    {
                        newCollisions[obA->getUserIndex()].emplace_back(obB->getUserIndex());
                    }
                }
            }
        }
    
    
        // Iterate over new collisions and add new collision to persistent collisions if it does not exist
        std::map<int, std::vector<int>>::iterator newCollisionIterator = newCollisions.begin();
        while (newCollisionIterator != newCollisions.end())
        {
            for (auto item : newCollisionIterator->second)
            {
                if (std::find(persistentCollisions[newCollisionIterator->first].begin(), persistentCollisions[newCollisionIterator->first].end(), item) == persistentCollisions[newCollisionIterator->first].end()) 
                {
                    std::cout << "New collision between " << newCollisionIterator->first << " And " << item << std::endl;
                    // We can play our collision audio here
                    persistentCollisions[newCollisionIterator->first].emplace_back(item);
                }
            }
    
            newCollisionIterator++;
        }
    
        // Iterate over persistent collisions and remove all collisions that did not exist in new collision
        std::map<int, std::vector<int>>::iterator persistentCollisionIterator = persistentCollisions.begin();
        while (persistentCollisionIterator != persistentCollisions.end())
        {
            std::vector<int>::iterator iter;
            for (iter = persistentCollisionIterator->second.begin(); iter != persistentCollisionIterator->second.end(); ) 
            {
                if (std::find(newCollisions[persistentCollisionIterator->first].begin(), newCollisions[persistentCollisionIterator->first].end(), *iter) != newCollisions[persistentCollisionIterator->first].end())
                {
                    ++iter;
                }
                else
                {
                    iter = persistentCollisionIterator->second.erase(iter);
                }
            }
    
            persistentCollisionIterator++;
        }
    }