Search code examples
c++collision-detectionraylib

C++ Raylib how to detect the side of a rectangle that a circle has collided with


I can use the function CheckCollisionCircleRec(Vector2{ x, y }, radius, paddleRect) to find out simply if my circle has collided with my rectangle, but I want to be able to find out what side of the rectangle my circle has collided with. How would I go about doing this? None of the algorithms I've made are working. Example of my most recent blunder:

if (x - radius <= 0 || x + radius >= screenWidth) {
            speedX *= -1;
        }
        else if (y - radius <= 0 || y + radius >= screenHeight) {
            speedY *= -1;
        }
        else if (CheckCollisionCircleRec(Vector2{ x, y }, radius, paddleRect)) {
            float paddleBottom = paddleRect.y + paddleRect.height;
            float paddleRight = paddleRect.x + paddleRect.width;

            if (range(paddleRect.x, paddleRect.x + speedX / 100, x + radius)) {
                x = paddleRect.x - radius;
                speedX *= -1;
            }
            if (range(paddleRight - speedX / 100, paddleRight, x - radius)) {
                x = paddleRight + radius;
                speedX *= -1;
            };

            if (range(paddleRect.y, paddleRect.y + speedY / 100, y + radius)) {
                y = paddleRect.y - radius;
                speedY *= -1;
            }
            if (range(paddleBottom - speedY / 100, paddleBottom, y - radius)) {
                y = paddleBottom + radius;
                speedY *= -1;
            };

EDIT: Here's the function I used to get the working end result:

// px and py are the ball's previous locations
// x and y are the ball's current locations

void checkCollision(Rectangle rectangle) {

    int left = rectangle.x;
    int right = rectangle.x + rectangle.width;
    int top = rectangle.y;
    int bottom = rectangle.y + rectangle.height;

    if (CheckCollisionCircleRec(Vector2{ x, y }, radius, rectangle)) {

        if (px < left) {
            speedX = negative(speedX);
            x = left - radius;
        }
        else if (px > right) {
            speedX = positive(speedX);
            x = right + radius;
        }
        else if (py < top) {
            speedY = negative(speedY);
            y = top - radius;
        }
        else if (py > bottom) {
            speedY = positive(speedY);
            y = bottom + radius;
        };
    };
};

Solution

  • A simply way is to use the PREVIOUS location of your circle. Not sure if you can in your program, but since you have an x and y handy, I'll assume you can have a prevX and prevY. I'll also assume these values represent the CENTER of the circle.

    Now if (prevX < paddleRect.x), then you likely collided with the left side (not guaranteed, but resolving ambiguities with complete accuracy requires recursively simulating your physics at smaller and smaller timesteps, which is likely unnecessary here). You can also constrain this more tightly with something like if (prevX < paddleRect.x && prevY > paddleRect.y && prevY < paddleRect.y + paddRect.height). There are various constraints you can add depending on how cleanly you want the side to be hit before detecting it. You can add corner hits, etc.

    The reason for using the previous location is that, if your circle is moving fast enough, then in a single frame it can jump straight into the middle of the rectangle. It's usually necessary to use the previous position to give more specific collision information in the current-location collision