Detect collision side for balls and rectangles

I am creating a “physics simulation” with objects that are affected by gravity, friction and wind. The main object is a ball that bounces around the screen, in realistic ways according to the physics modifiers the user can set. I thought it would be cool if users could add boxes for the ball to collide with, however I have found myself stuck trying to make this collision work. This is much harder than regular circle-rectangle collision because I need to know the side(s) the ball hit in order to accurately change the velocity. The best I can do is collision on one axis (for example the y axis, so the ball can bounce correctly off the top and bottom of the rectangle), but whenever I try to add the second axis to the mix it doesn’t work out due to some flaw in my code. This has been really difficult for me, especially since I’m not good with trigonometry (and therefore am avoiding it). Some help would be very appreciated :slight_smile:

1 Like

Hi @turke1034 ,

Check this out…

Hope it helps.

Cheers
— mnse

@mnse Yeah, a short while after posting this I stumbled onto that article. It’s great, and I have my ball able to bounce as expected on all 4 sides. However when colliding near the edges, the ball acts strangely.

1 Like

please show your code segment with the collision - or even a run-able Sketch

Check:

String intersects(Ball ball, Block block)
    {
        String side = "none";

        float testX = ball.vector.x;
        float testY = ball.vector.y;

        if (ball.vector.x < block.vector.x)
        {
            testX = block.vector.x;
            side = "left";
        }
        else if (ball.vector.x > block.vector.x + block.width)
        {
            testX = block.vector.x + block.width;
            side = "right";
        }
        if (ball.vector.y < block.vector.y)
        {
            testY = block.vector.y;
            side = "top";
        }
        else if (ball.vector.y > block.vector.y + block.height)
        {
            testY = block.vector.y + block.height;
            side = "bottom";
        }

        float distX = ball.vector.x - testX;
        float distY = ball.vector.y - testY;
        float distance = sqrt((distX * distX) + (distY * distY));

        if (distance <= ball.radius)
        {
            return side;
        }
        return "none";
    }

Apply:

void checkCollision(Ball ball, Block block)
    {
        String side = intersects(ball, block);

        switch (side)
        {
            case "top" ->
            {
                ball.velocity.y *= -ball.restitution; // restitution is just the bounciness
                ball.vector.y = block.vector.y - ball.radius;
            }
            case "left" ->
            {
                ball.velocity.x *= -ball.restitution;
                ball.vector.x = block.vector.x - ball.radius;
            }
            case "bottom" ->
            {
                ball.velocity.y *= -ball.restitution;
                ball.vector.y = (block.vector.y + block.height) + ball.radius;
            }
            case "right" ->
            {
                ball.velocity.x *= -ball.restitution;
                ball.vector.x = (block.vector.x + block.width) + ball.radius;
            }
        }
    }

If you do collision on an uneven surface, you can identify the angle a surface has to the balls trajectory and then “rotate” to the flat surface one-dimentionsal case, solve this and then rotate back, if that makes sense.

For the collision detection I see two potential issues:
Detection misses the balls radius, except you include that somewhere else you should test for ball.vector.x + ball.radius

(block.vector.x + block.width) + ball.radius

For the calculation of the position after collision you need to add 2 x radius not just 1 x radius.

Poorly formated page with very good information:

1 Like

Not sure what you mean by adding radius * 2 instead of just radius. I tried this, and it just teleports the ball the correct distance + the length of the radius away from the block, whereas with just the radius it just teleports the ball to the edge (where the edge of the ball touches the edge of the box). With the testing lacking the ball’s radius as a consideration, well really the if statement at the end of circleRect() takes care of that (testing if the distance is less than or equal to the balls radius.

It might be that you have a different approach.

When I did collision with a wall, then I did calculate the new location of the particle (after applying velocity) and then tried to check if it is out of bounds. If it is, I calculate the final position of the particle, effectively mirroring the particles trajectory on the plane of the wall. This gives me the position the particle would have traveled to complete the movement.

if (location.x+size >= boxsize/2) {
      velocity.x = -velocity.x;
      location.x = boxsize - location.x-2*size;

(this is inside a box, therefore boxsize/2 = wall limit).

I thought, that could be transferred to your problem, but not sure anymore.

1 Like

The switch statement

case something :

break; after each section

Yeah, I tried splitting the y and x axes to have their own checking in hope that they would act independently (including the switch statements) but it did absolutely nothing. The issue is definitely the corners and I’ve seen examples for how to handle that so I might implement it and see how it goes. Basically, it checks if the ball’s position is within an imaginary circle positioned on the edge of the rectangle, and I can just make it flip the velocity depending on the corner it hit.

1 Like

It worked. I might do some more tests, but I’ve tested a lot of angles and velocities and I think its safe to say so. If I should post the code, I will but just keep in mind that it probably isn’t the most refined.

Just to let people know who might be having the same problem, if you take the circle on edges approach you will need to manually determine if the velocity on certain axes is flipped or not. Without it, for example the top left corner of a rectangle may unnaturally flip a ball going upward and rightward because even though it barely scrapes the edge, the y velocity will be flipped causing it to jolt downward. So you can check what direction on the y axis the ball is moving, and if it is moving against the rectangle (coming from the opposite side of that axis) you don’t reverse y velocity. Just saying :slight_smile:

2 Likes