4
\$\begingroup\$

I wanted to ask, how do I resolve collision between 2 cubes - in my case I want to push a moving / dynamic cube (used for the player) out of another static cube (used for the floor or objects in the scene)?

I tried various things, but in the end I don't know how to make the dynamic cube stay smoothly on top of the floor or not able to go through other objects (everything AABB vs AABB for now).

Below are the most important snippets to understand my problem better.

From PhysicsEngine.cpp

IntersectData intersectData =
                m_objects[i]->getCollider().intersect(
                    m_objects[j]->getCollider());

                // if they intersect, reverse movement to have solid box collisions.
                if(intersectData.getDoesIntersect())
                {
                    // revert movement (later push?) collision response.
                    glm::vec3 diff = m_objects[i]->getPosition() - m_objects[j]->getPosition();
                    glm::vec3 sep = glm::normalize(intersectData.getDirection()) * diff;
                    // std::cout << "Separation between: " << m_objects[i]->

                    // if testCollisionResponse - push back.
                    if (m_objects[i]->testCollisionResponse)
                    {
                        m_objects[i]->setPosition(m_objects[i]->getPosition() + sep);
                        // m_objects[i]->setVelocity(glm::vec3(0.0));
                        // m_objects[i]->testGravity = false;
                        // m_objects[i]->testGravity = false;
                    }
                    else if (m_objects[j]->testCollisionResponse)
                    {
                        m_objects[j]->setPosition(m_objects[j]->getPosition() - sep);
                        // m_objects[j]->setVelocity(glm::vec3(0.0));
                        // m_objects[j]->testGravity = false;
                    }
                }

AABB.cpp

uGE::IntersectData uGE::AABB::intersectAABB(const AABB& other) const

{ glm::vec3 distances1 = other.getMinExtents() - m_maxExtents;

// test the other way around.
glm::vec3 distances2 = m_minExtents - other.getMaxExtents();

// pick the biggest values between the vectors on each axis.
glm::vec3 distancesOnEachAxis = glm::max(distances1, distances2);

float maxDistance = distancesOnEachAxis[0];
for (int i = 0; i < 3; i++)
{
    if (distancesOnEachAxis[i] > maxDistance)
    {
        maxDistance = distancesOnEachAxis[i];
    }
}

return uGE::IntersectData(maxDistance < 0, distancesOnEachAxis);

}

IntersectData.hpp

class IntersectData

{ public: IntersectData(const bool doesIntersect, const glm::vec3 direction) : m_doesIntersect(doesIntersect), m_direction(direction) {}

inline bool getDoesIntersect() const { return m_doesIntersect; }
inline float getDistance() const { return glm::length(m_direction); }
inline const glm::vec3 getDirection() const { return m_direction; }

private: const bool m_doesIntersect; const glm::vec3 m_direction; };

These are the project files (if you want to see more): https://github.com/BigThinker/CPPGame/blob/master/uGE/uGE/Physics/PhysicsEngine.cpp

All the best, Aldo

\$\endgroup\$

2 Answers 2

3
\$\begingroup\$

I think you may need to look further than simple collision detection to make this happen. Due to the discrete nature of a simulation such as this, it's hard to achieve a smooth "flooring" of one object onto a surface as it will constantly collide, then be adjusted, then collide, then be adjusted again.

One approach in projects I've used in the past is to manage this via a set of different phyics "states" which an object could be in. Examples might be: Grounded, Airbourne etc.

In this way, you run a different set of phyics rules depending on the state your object is currently in, and use hitbox detection differently in each, and to help transition between the two.

For example, imagine the case of a player character who appears in midair, above the floor. THe inital state is set to Airbourne and the physics system applies gravity to the character to start them falling.

Eventually, the AABB hitbox detection system detects a collision between the player physics hitbox and the floor hitbox. At this point the physics state transitions to Grounded and the position is adjusted to ensure there is no collision (as you discuss).

Now, the physics system knows the player is grounded, so can use different techniques to track the floor, rather than relying on the hitbox. Depending on your game, you might have a defined "floor" plane which you can query the height of at the current location. You might be able to ray-cast downwards (a short distance) to ensure you are still near the ground, and then transition back to 'Airbourne' when not.

There are many approaches to this, but the principal is that simple hitboxes are probably not sufficient for "smoothly" adhering one object to another where movement is possible. Use them to detect collisions, and use that information to inform transitions in your physics system to control character movement.

\$\endgroup\$
2
  • \$\begingroup\$ Thanks a ton for your input. I think that this approach would work well in my case, and I'm going to try and implement this. \$\endgroup\$
    – BigThinker
    Feb 28, 2015 at 3:27
  • \$\begingroup\$ Very glad if my answer was useful to your situation! \$\endgroup\$
    – xan
    Mar 2, 2015 at 9:23
0
\$\begingroup\$

Optional approach: I read through Game Physics Engine Development by Ian Millington to find out that before he resolves the impulse (velocity) and interpenetration, he removes the change in velocity by gravity like so:

// resting contact implementation.
    // what is the velocity due to acceleration only?
    glm::vec3 velocityDueAcc = particle[0]->getAcceleration();

    // separation velocity due to acceleration (i.e. gravity)
    // contact normal is the normalized vector between 2 objects's positions.
    // duration is the frame duration (delta time).    
    float sepVelocityDueAcc = glm::dot(velocityDueAcc, contactNormal) * duration;

        // if there's separation velocity from acceleration,
        // remove it from the new separation velocity.
        // (turn back the movement from y acceleration before resolving v and p).
        if (sepVelocityDueAcc < 0)
        {
            newSepVelocity += restitution * sepVelocityDueAcc;

            // make sure there wasn't removed more than there
            // was to remove.
            if (newSepVelocity < 0) newSepVelocity = 0;
        }
    // ...
    // use newSepVelocity to resolve velocity.

Kind of an ad hoc approach to solving this problem, but it's working well so far.

\$\endgroup\$

You must log in to answer this question.

Not the answer you're looking for? Browse other questions tagged .