1
\$\begingroup\$

I am making a basic 2D platformer. I want to get the relative position of one rectangle to another.

This is how I do it right now:

const float offset = 5;
if (x1 <= x2 && x1 + w1 >= x2)
            {
                if (Math.Abs(y1 + h1 - y2) <= offset)
                    return CollisionType.Top;
                else if (Math.Abs(y1 - h2 - y2) <= offset)
                    return CollisionType.Bottom;
                else if ((y1 <= y2 && y1 + h1 >= y2) || (y1 >= y2 && y1 <= y2 + h2))
                    return CollisionType.Left;
            }
            if (x2 <= x1 && x2 + w2 >= x1)
            {
                if (Math.Abs(y1 + h1 - y2) <= offset)
                    return CollisionType.Top;
                else if (Math.Abs(y1 - h2 - y2) <= offset)
                    return CollisionType.Bottom;
                else if ((y1 <= y2 && y1 + h1 >= y2) || (y1 >= y2 && y1 <= y2 + h2))
                    return CollisionType.Right;
            }

            return CollisionType.NoCollision;

Here how this function works (or at least how I suppose it to work): enter image description here

Using this function, I often have collision bugs, but I cannot understand a global rule of when it happens. I don't think that a bunch of if blocks would solve the problem.

I need a collision detection function that receives two arguments of type Entity (The class can contain any fields or functions, but it must have an X, a Y, a Width and a Height) The function needs to return the relative position of the first rectangle to the second.

What improvements would you suggest to my current function or is there a better system for collision detection in 2D games where one needs to get a relative position instead of a simple true false?

\$\endgroup\$
  • 1
    \$\begingroup\$ There is no actual answer to this question because you have not really defined what exactly it means for one rectangle to be on top of another rectangle. Once you define that, then how to implement the function to check for that should be clear. We can't decide for you what the best definition of "a being on top of b" is because we don't know what is most appropriate for your application. \$\endgroup\$ – Garrett Gutierrez Oct 3 '18 at 13:06
  • \$\begingroup\$ @GarrettGutierrez If I would have known how to define it, I wouldn't ask this question. I have a hero (52 x 20) who walks on (16x16) blocks, and I want him to walk when he is on top of those blocks. Be blocked when he has blocks to his right and so on \$\endgroup\$ – Philippe Oct 3 '18 at 13:10
  • \$\begingroup\$ One idea might be to take rectangle B and draw lines through its two diagonals and let these lines divide the world space into 4 quadrants, one on top, bottom, left, and right. Rectangle A can be considered "on top" of B if most of its area is within the top quadrant. A heuristic way of checking for this might be to check what quadrant A's center lies within. You could check this by getting a diagonal and using a line sideness algorithm. \$\endgroup\$ – Garrett Gutierrez Oct 3 '18 at 13:11
  • \$\begingroup\$ You might be approaching your problem the wrong way then. Collision detection should be resolved independently of what direction your hero is approaching the object he is colliding from. If, while travelling from point A (in the previous frame) to point B (this frame) he collides with an object, that collision detection should be resolved such that he is moved back to a point between A and B and is touching but not colliding with the object. This is true regardless of if he approaches said object from above or the side. \$\endgroup\$ – Garrett Gutierrez Oct 3 '18 at 13:21
  • \$\begingroup\$ @GarrettGutierrez So, how should it work in a 2D platformer? I am just very new to game development and collision detection has been my biggest problem lately \$\endgroup\$ – Philippe Oct 3 '18 at 17:37
1
\$\begingroup\$

The simplest way is not to check if there is a collision but rather to check if its impossible for there to be a collision.

To do this you need to verify if the two sides that would overlap in a collision are too far away form each other for that to be impossible:

if(Obj1.Right < Obj2.Left
   || Obj1.Bottom < Obj2.Top
   || Obj1.Left > Obj2.Right
   || Obj1.Top > Obj2.Bottom){
    //There is a collision
}

Using this technique you save on a fair bit of computation when there is no collision because once one of the tests fail it doesn't bother doing the rest since there is no point. Do note though that this version counts barely touching as a collision, so you may want to switch out some > and < with >= and <= to have tighter collision checking if that is what you require

\$\endgroup\$
  • \$\begingroup\$ I have fixed my problem by making the FPS higher and blocks bigger. But your solution also works well, you get my upvote, thanks. \$\endgroup\$ – Philippe Nov 4 '18 at 8:07
  • \$\begingroup\$ Ahhh then your problem was to do with things passing through other things, you could also look into raycasting to solve that as well if ever you need to bring those parameters back down \$\endgroup\$ – TurtleKwitty Nov 4 '18 at 18:02
  • \$\begingroup\$ That code can also go through when there is not a collision \$\endgroup\$ – Klemmensen Nov 10 '18 at 10:28
-2
\$\begingroup\$

You should compare the distance between the opposite sides of both squares and the side which has the smallest distance should be returned. I have written some pseudo code u can get inspired by. The long method/function names are just to make it very clear what they do.

CollisionType GetCollisionTypeFraSquare2ToSquare1(x1,y1,x2,y2)
{
    distTop = Math.abs( y2 + h2- y1 )
    distBottom = Math.abs( y1 + h1 - y2 )
    distRight = Math.abs( x1 + w1 - x2  )
    distLeft = Math.abs(x2 + w2 - x1 )

    if( IsFirstNumberSmallest(distTop,distBottom,distRight,distLeft) )
        return CollisionType.Top;

    if( IsFirstNumberSmallest(distBottom,distTop,distRight,distLeft) )
        return CollisionType.Bottom;

    if( IsFirstNumberSmallest(distRight,distTop,distBottom,distLeft) )
        return CollisionType.Right;

    if( IsFirstNumberSmallest(distLeft,distTop,distBottom,distRight) )
        return CollisionType.Left;

    //If nothing has been returned yet it means there are 2 distances which are the same.
}

bool IsFirstNumberSmallest(n1, n2, n3 ,n4)
{
    return n1 < n2 && n1 < n3 && n1 < n4;
}

enter image description here

\$\endgroup\$
  • \$\begingroup\$ this doesn't work... \$\endgroup\$ – Philippe Oct 3 '18 at 12:51
  • 1
    \$\begingroup\$ Thats an incredible amount of useless computation \$\endgroup\$ – TurtleKwitty Nov 4 '18 at 0:13
  • \$\begingroup\$ @TurtleKwitty i dont know if you are trolling.... xd. But your answer is legit the same as mine except your answer is more abstract. And it only checks for collision while not checking for the relative position between the two rectangles. \$\endgroup\$ – Klemmensen Nov 9 '18 at 12:56
  • \$\begingroup\$ Not quite; yes the highlevel algorithm is the same but your implementation does a whole lot of useless computation. Does many more checks, calls on functions therefore needsto build up and tear down the stack each time it checks anything. So no, our answers are not the same apart from high level concepts \$\endgroup\$ – TurtleKwitty Nov 9 '18 at 13:55
  • \$\begingroup\$ Pls specify. Show me a better implementation which returns the relative position. \$\endgroup\$ – Klemmensen Nov 9 '18 at 14:43

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.