简体   繁体   中英

How do I resolve a collision's position properly in 2D collision detection?

My current implementation looks like this:

if (shapesCollide) {
    if (velocity.y > 0) entity.position.y = other.position.y - entity.size.y;
    else entity.position.y = other.position.y + other.size.y;

    velocity.y = 0;

    if (velocity.x > 0) entity.position.x = other.position.x - entity.size.x;
    else entity.position.x = other.position.x + other.size.x;

    velocity.x = 0;
}

However, this leads to weird handling when movement is happening on both axes - for example, having entity moving downward to the left of object , and then moving it to collide with object , will correctly resolve the horizontal collision, but will break the vertical movement.

I previously simply went

if (shapesCollide) {
    position = oldPosition;
    velocity = { 0, 0 };
}

But this lead to another multi-axis issue: if I have my entity resting atop the object , it will be unable to move, as the gravity-induced movement will constantly cancel out both velocities. I also tried considering both axes separately, but this lead to issues whenever the collision only occurs when both velocities are taken into account.

What is the best solution to resolving collision on two axes?

I assume that the entities can be considered to be more or less round and that size is the radius of the entities?

We probably need a little vector math to resolve this. (I don't know the square-root function in c++, so be aware at sqrt.) Try replacing your code inside if(shapesCollide) with this and see how it works for you.

float rEntity = sqrt(entity.size.x * entity.size.x + entity.size.y * entity.size.y);
float rOther = sqrt(other.size.x * other.size.x + other.size.y * other.size.y);

float midX = (entity.position.x + other.position.x) / 2.0;
float midY = (entity.position.y + other.position.y) / 2.0;

float dx = entity.position.x - midX;
float dy = entity.position.y - midY;
float D = sqrt(dx * dx + dy * dy);

rEntity and rOther are the radii of the objects, and midX and midY are their center coordinates. dx and dy are the distances to the center from the entity.

Then do:

entity.position.x = midX + dx * rEntity / D;
entity.position.y = midY + dy * rEntity / D;

other.position.x = midX - dx * rOther / D;
other.position.y = midY - dy * rOther / D;

You should probably check that D is not 0, and if it is, just set dx = 1, dy = 0, D = 1 or something like that.

You should also still do:

velocity.x = 0;
velocity.y = 0;

if you want the entities to stop.

For more accurate modelling, you could also try the following:

float rEntity = sqrt(entity.size.x * entity.size.x + entity.size.y * entity.size.y);
float rOther = sqrt(other.size.x * other.size.x + other.size.y * other.size.y);

float midX = (entity.position.x * rOther + other.position.x * rEntity) / (rEntity + rOther);
float midY = (entity.position.y * rOther + other.position.y * rEntity) / (rEntity + rOther);

float dxEntity = entity.position.x - midX;
float dyEntity = entity.position.y - midY;
float dEntity = sqrt(dxEntity * dxEntity + dyEntity * dyEntity);

float dxOther = other.position.x - midX;
float dyOther = other.position.y - midY;
float dOther = sqrt(dxOther * dxOther + dyOther * dyOther);

entity.position.x = midX + dxEntity * rEntity / dEntity;
entity.position.y = midY + dyEntity * rEntity / dEntity;

other.position.x = midX + dxOther * rOther / dOther;
other.position.y = midY + dyOther * rOther / dOther;

which finds the midpoints when the radii are taken into account. But I won't guarantee that that works. Also, the signs on the last additions are important.

I hope this helps (and works). Let me know if something is unclear.

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM