0
\$\begingroup\$

I'm having a hard time trying to resolve the reflexion angles when the ball collides with one of the brick's corners (square vertices) in my brick breaker game.

The collision detection system is working fine, and I've already managed to resolve the collisions when the ball hits the brick from its sides. The reflection angles are working fine when the ball hits either the walls or the brick's sides, and I've even managed to add slight angle increments to these angles to prevent the ball to bounce indefinitely from one side to the other.

But when it comes to the brick's corners, things get messy. The collision resolution works fine to differentiate the sides from the corners. In general, I'm picking two coordinates x,y (new and previous) to determine the ball positions and using Math.atan2(newY-prevY, newX-prevX) function to get the angles to calculate cos and sin for spdX and spdY for the collision resolution on the walls and the brick's sides. For the corners part, I'm picking the newX,newY ball coordinate (center point) when it collides with one of the bricks corners and picking the x,y coordinate from this corner to determine the normal line, by using Math.atan2(newY-cornerY, newX-cornerX). Note that I've made the adjustments for each corner x,y to get the correct values (adding width to x for the right corners and height to y for the bottom corners). Then, I calculate the angle of incidence incAngle = Math.abs(Math.atan2(prevY-newY, prevX-newX) - normal) and the reflection angle refAngle = normal - incAngle. Lastly I calculate spdX = Math.cos(refAngle) and spdY = Math.sin(refAngle). It seems the mess happens when I calculate incAngle and refAngle. I've already inverted signs on both, used Math.abs(), and in all cases some of the reflection angles show fine, some of them even perfectly, but in certain positions, depending of the inciding angle, the reflection angle shows incorrectly. Definetly the angles sign issue still confuses me a lot.

Where am I doing wrong? Is there something missing? I'm developing my game from scratch using Java and Eclipse and am trying to make the code as simple as I can, since I don't understand much of those quite complicated complex Math equations (I had made some research about them, but did not understand a thing), and don't use external physics/collision engines in it.

Best regards.

\$\endgroup\$
4
  • 1
    \$\begingroup\$ Does the incorrect calculation appear in some specific cases, such as top-to-bottom collisions are fine, but bottom-to-top collisions are not correct? Maybe you can post the function to calculate the reflection angle here. \$\endgroup\$
    – Mangata
    Jun 24, 2022 at 6:41
  • 1
    \$\begingroup\$ Can you show us your exact code? Also, when you say "ellipse", do you mean your ball can have a distorted aspect ratio? If not, it's better to use the words "circle" or "disc" — math with arbitrary ellipses can get significantly more complicated, complexity you can skip if you don't need it. \$\endgroup\$
    – DMGregory
    Jun 24, 2022 at 11:20
  • \$\begingroup\$ You probably noticed that code isn't very legible in a comment. When asked to provide more information, the correct response is to edit your question so it contains all the relevant information in one place, with proper formatting. Then we can delete the comments since they've served their function. \$\endgroup\$
    – DMGregory
    Jul 1, 2022 at 2:16
  • \$\begingroup\$ Thanks for clarifying me. I finally fixed the issues that were going unnoticed and posted the solution as an answer. I can now delete my previous comments. \$\endgroup\$
    – Andre
    Jul 1, 2022 at 2:29

1 Answer 1

0
\$\begingroup\$

After a lot of testing, I discovered there were some unnoticed errors when calculating the normal vector angle and when repositioning the ball off the block. I'm going to take the upper-left corner to explain how I got to the solution.

The normal angle error happened when the ball collided with the block corner and its center point Y was below (greater than) the block corner Y or the center point X was greater than the block corner X. This caused the normal angle to get a positive value (ex. 180 degrees or more) and a value greater than -90 degrees, respectively. The correct normal angles for the upper-left block corner must range between -180 and -90 degrees.

To solve this, I made some adjustments in the conditions to differentiate the collisions on the sides from those on the corners, so that I can always get a normal angle for the upper-left corner from -180 to -90 degrees.

As for the ball repositioning part, I needed to get the X,Y point coordinate on the ball circle corresponding to the normal+PI angle, and then subtract the X,Y values plus the ball radius from the current X,Y ball position values. (Because I wasn't figuring out how to do this before, the ball repositioning was inaccurate, thus causing "multi collisions" issue and wrong reflection angles.)

Below is the pseudo-code on how I got correct reflection angles for the collisions between the ball and the upper-left corner of the block. I will still make slight adjustments for the other corners, but the logic is the same.

//conditions for left side collisions
if(ballX+ballWidth >= blockX &&
ballY+(ballHeight/2) >= blockY &&
ballY+(ballHeight/2) <= blockY+blockHeight &&
Math.abs((ballY+ballHeight)-blockY) >= Math.abs(ballX+ballWidth)-blockX) && 
Math.abs((blockY+blockHeight)-ballY) >= Math.abs((ballX+ballWidth)-blockX)) 
{not relevant here}

//conditions for upper side collisions
else if(ballY+ballHeight >= blockY &&
ballX+(ballWidth/2) >= blockX &&
ballX+(ballWidth/2) <= blockX+blockWidth &&
Math.abs((ballX+ballWidth)-blockX) > Math.abs((ballY+ballHeight)-blockY) && Math.abs((blockX+blockWidth)-ballX) > Math.abs((ballY+ballHeight)-blockY)) 
{not relevant here}

//conditions for upper-left corner collisions
else if(ball.contains(blockX, blockY) && ballY+(ballHeight/2) < blockY && ballX+(ballWidth/2) < blockX) {code}

Code from upper-left corner collisions resolution made in Java:

    else if(e1Mask.contains(e2Mask.getX(), e2Mask.getY()) &&
        e1Mask.getY()+((e1Mask.getHeight()-1)/2) < e2Mask.getY() && 
        e1Mask.getX()+((e1Mask.getWidth()-1)/2) < e2Mask.getX()) {
      prevX = newX; //previous ball position x (center point)
      prevY = newY; //previous ball position y (center point)
      newX = getX()+getRadius()-0.5; //new ball position x (center point)
      newY = getY()+getRadius()-0.5; //new ball position y (center point)
      double normal = Math.atan2(newY-e2Mask.getY(), newX-e2Mask.getX()); //normal line angle
      double inX = (getRadius()-0.5)*Math.cos(normal+Math.PI); //x coordinate from circle point that is inside the block
      double inY = (getRadius()-0.5)*Math.sin(normal+Math.PI); //y coordinate from circle point that is inside the block
      x -= (getX()+getRadius()+inX)-e2Mask.getX()-1; //ball repositioning x. Ball radius was added in order to get the correct coordinate result.
      y -= (getY()+getRadius()+inY)-e2Mask.getY()-1; //ball repositioning y. Ball radius was added in order to get the correct coordinate result.
      newX = getX()+getRadius()-0.5; //updated newX to calculate ball incoming angle
      newY = getY()+getRadius()-0.5; //updated newY to calculate ball incoming angle
      //Alternative way to repositioning the ball
      /*double inX = (getRadius()-0.5)*Math.cos(normal+Math.PI);
      double inY = (getRadius()-0.5)*Math.sin(normal+Math.PI);
      x = Game.bloco.getX()-(getRadius()-0.5+inX)-1;
      y = Game.bloco.getY()-(getRadius()-0.5+inY)-1;
      newX = getX()+getRadius()-0.5;
      newY = getY()+getRadius()-0.5;*/
      //
      //Didn't use these variables, but they helped me understand the logic
      /*double slopeNormal = Math.tan(normal); 
      double slopeRefLine = -1/slopeNormal;
      double refLine = normal + (Math.PI/2);*/
      //
      double prevAngle = Math.atan2(prevY-newY, prevX-newX); //ball incoming angle
      double incAngle = prevAngle - normal; //inciding angle
      double refAngle = normal - incAngle; //reflection angle
      spdX = Math.cos(refAngle); //new speedX value
      spdY = Math.sin(refAngle); //new speedY value
    }

Certainly there are other ways to resolve collisions between circles and rectangles vertices, even better ones than mine but, at least, this one has worked for me, and I hope it can help other game developers who are facing the same challenges.

My best wishes to you all!

\$\endgroup\$

You must log in to answer this question.

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