user3871
user3871

Reputation: 12708

Calculate random bounce angles

I'd like to make a ball bounce angle change each time it hits a wall.

It will change based on how near the middle of the wall it hits...

Right now, I'm hard coding the change in X and Y when it hits a surface... My goal is to get the degrees from current X and Y, apply a change to the degrees (Right now I'm add a random number to the degrees), then calculate the new incrementing values for X and Y. I know how to get the newX and newY, but not how to get the incrementing values.

The green is the starting x y of (5,5)... the blue is the next frame of (4,4).

enter image description here

currX (5) - wallX (0) = distX (5)

currY (5) - wallY (0) = distY (5)

Take the cosine of my angle + random increment, we'll say 55 degrees, * distX

cos(55 degrees) = .5735... .5735 x distX (5) = 2.86

And sin of my angle * distY

sin(55 degrees) = .8191... .8191 x distY (5) = 4.09

newX = cos result (2.86) + originX (5) = 7.86

newY = sin result (4.09) + originY (5) = 9.09

newX, newY = (7.86, 9.09)

Okay... so I have my new coordinates...

But those don't equate to what my new incrementing value of x and y should be based on my angle in incidence.

Code snippet: You can see that I'm hard coding the x,y increments (dragger.x += 2; )

        function tick() {
            var rand = Math.floor((Math.random()*10)+1);

            console.log("ticking..." + rand);
            if (dragger.x >= 400-20) {
                dragger.xDir = "right";         
            }
            if (dragger.x < 20) {
                dragger.xDir = "left";      
            }       
            if (dragger.y >= 150-20) {
                dragger.yDir = "up";
            }
            if (dragger.y < 20) {
                dragger.yDir = "down";
            }

            var oldX = dragger.y;
            var oldY = dragger.x;

            if (dragger.xDir == "left") {
                dragger.x += 2; 
            }
            else {
                dragger.x -= 2;
            }
            if (dragger.yDir == "up") {
                dragger.y -= 2;
            }
            else {
                dragger.y += 2;
            }
            //post update...
            var newX = dragger.y;
            var newY = dragger.x;                   

            var angle = getAngle(newX, oldX, newY, oldY)
            angle+=rand;

            $('#getAngle').empty();
            $('#getAngle').append("bounce angle (degrees): " + angle);


            //console.log(xDir);
            // update the stage:
            stage.update();
        }

        function getAngle(x2, x1, y2, y1) {             
            var deltaX = Math.abs(x2-x1);
            var deltaY = Math.abs(y2-y1);
            var radians = Math.atan2(deltaX, deltaY);
            var degrees = radians * (180/Math.PI);
            return degrees;
        }

Upvotes: 4

Views: 3275

Answers (3)

Jean-Paul
Jean-Paul

Reputation: 21150

This is a pretty interesting problem due to it's specificity.

Making a ball bounce in a programming language can be done quite easily. Like this example.

But clearly, your question is not about 'making it work'; you want explicit control over the coordinates and the angles such that you can alter them for whatever purpose you had in mind.

Because I am quite vulnerable to nerd sniping, I dusted off my geometric skills and came up with the following scrap of pseudocode (I made this from scratch to make sure I have total control):

Intuition

enter image description here

enter image description here

Pseudocode

 theta    = starting angle
 a        = current x-coordinate of ball
 b        = current y-coordinate of ball
 quadrant = quadrant-direction to which ball is moving

 /> Determine number between 1 and 360: theta
 /> Calculate quadrant

   .> 0-90 :   quadrant 1:    horizontal: 90-a   vertical: b      alpha: 90 - theta
   .> 90-180:  quadrant 4:    horizontal: 90-a   vertical: 30-b   alpha: theta - 90
   .> 180-270: quadrant 3:    horizontal: a      vertical: 30-b   alpha: 270 - theta
   .> 270-360: quadrant 2:    horizontal: a      vertical: b      alpha: theta - 270

 /> Calculate distance to side       |
 /> Calculate distance to top/bottom |

   .> to side:       n(alpha) = horizontal/cos(alpha)
   .> to top/bottom: m(alpha) = vertical  /sin(alpha)

 /> Determine where ball is going to hit (n = side, m = top/bottom)

   .> n >= m  : bounces at top/bottom
   .> m >= n  : bounces at side

      .> switch (quadrant)
         .> 1 : n = right side     m = top      
         .> 2 : n = left side      m = top
         .> 3 : n = left side      m = bottom
         .> 4 : n = right side     m = bottom

 /> Calculate coordinates of hit

    /> Define new angle

      // Normally, angle of impact = angle of reflection
      // Let's define the angle of impact with respect to the origin (0,0)

   .> switch (quadrant)
         .> 1 : 
                 .> n >= m (at top/bottom) : x = a + vertical*tan(alpha)   y = 0                         theta = 180-theta
                 .> m >= n (at side)       : x = 90                        y = b - horizontal*tan(alpha) theta = 270+alpha
         .> 2 : 
                 .> n >= m (at top/bottom) : x = a - vertical/tan(alpha)   y = 0                         theta = 270-alpha
                 .> m >= n (at side)       : x = 0                         y = b - horizontal*tan(alpha) theta = 90-alpha
         .> 3 : 
                 .> n >= m (at top/bottom) : x = a - vertical/tan(alpha)   y = 30                        theta = 270+alpha
                 .> m >= n (at side)       : x = 0                         y = b + horizontal*tan(alpha) theta = 90+alpha
         .> 4 : 
                 .> n >= m (at top/bottom) : x = a + vertical/tan(alpha)   y = 30                        theta = 90-alpha
                 .> m >= n (at side)       : x = 90                        y = b + horizontal*tan(alpha) theta = 270-alpha

 /> Define new coordinates (for reusage of function)

    .> a = x
    .> b = y

   .> (optional) if you would like the angles to differ, enter extra term here:

         .> extra = ...
         .> theta = theta + extra

Implementing this code will allow you to work with the easiness of degrees and still be able to determine the coordinates.

It works as follows:

  • First determine the initial position of the ball (a,b) and it's initial direction (theta)

  • Now the program will calculate:

    • Where the ball is going to hit
    • What the coordinates of the ball at impact are
    • What the new angle of reflection is (this is the part you want to change)

And then it starts over again to calculate the new hit.

In JavaScript, the code would look like this:

Code

var width = 500;
var height = 200;
var extra = 0;
var a;
var b;
var x;
var y;
var angle;
var n;
var m;
var quadrant;
var horizontal;
var vertical;
var alpha;
var side;
var topbottom;
var sides;
var i = 1;

  var txt=document.getElementById("info");
  txt.innerHTML="x: "+a+"<br>y: "+b+"<br>angle: "+angle+"<br>quadrant: "+quadrant;

function buttonClick()
{
  if (i == 1)
  {
    a = 75;
    b = 75;
    //determine first angle randonmly
    angle = Math.floor((Math.random()*360)+1);;
  } else
  {
    a = xcoord();
    b = ycoord();
  }
  var oldAngle = angle;  
  angle = findNewCoordinate(a, b, angle);

  sides = hitWhere();

  var txt=document.getElementById("info");
    txt.innerHTML="x: "+a+"<br>y: "+b+"<br>horizontal: "+horizontal+"<br>vertical: "+vertical+"<br>n: "+n+"<br>m: "+m+"<br>angle: "+oldAngle+"<br>alpha: "+alpha+"<br>quadrant: "+quadrant+"<br>side: "+topbottom+side+"<br>"+sides+"<br>"+i;
    i++;
}

function findNewCoordinate(a, b, angle)
{
    if (angle >= 0 && angle < 90) { quadrant = 1; horizontal = width-a; vertical = b; alpha = (90 - angle); }
    else if (angle >= 90 && angle < 180) { quadrant = 4; horizontal = width-a; vertical = height-b; alpha = (angle-90);  }
    else if (angle >= 180 && angle < 270) { quadrant = 3; horizontal = a; vertical = height-b; alpha = (270-angle);  }
    else if (angle >= 270 && angle <= 360) { quadrant = 2; horizontal = a; vertical = b; alpha = (angle-270);  }


       var cosa = Math.cos(alpha * Math.PI / 180);
       var sina = Math.sin(alpha * Math.PI / 180);
       var tana = Math.tan(alpha * Math.PI / 180);

       var tant = Math.tan(angle * Math.PI / 180);

       n = horizontal/cosa;
       m = vertical/sina;


    switch (quadrant)
    {
        case 1:  
            if (m >= n) //hit at side
            {
                y = b - horizontal*tana; 
                x = width;               
                angle = 270+alpha;       
            } else
            {
                y = 0;                  
                x = a + vertical*tant;   
                angle = 180-angle;       
            } 
            side = "right side"; topbottom = "top";
            break;
        case 2:
            if (m >= n)  //hit at side
            {
                y = b-horizontal*tana;   
                x = 0;                   
                angle = 90-alpha;        
            } else
            {
                y = 0;                   
                x = a - vertical/tana;   
                angle = 270-alpha;       
            } 
            side = "left side"; topbottom = "top";
            break;
        case 3: side = "left side"; topbottom = "bottom";
            if (m >= n)  //hit at side
            {
                x = 0;                   
                y = b + tana*horizontal; 
                angle = 90+alpha;        
            } else
            {
                y = height;              
                x = a - vertical/tana;   
                angle = 270+alpha;       
            } break;
        case 4: side = "right side"; topbottom = "bottom";
            if (m >= n)  //hit at side
            {
                y = b+horizontal*tana; 
                x = width;             
                angle = 270-alpha;     
            } else
            {
                y = height;            
                x = a + vertical/tana; 
                angle = 90-alpha;      
            } break;
    }

    //add extra degrees to the angle (optional)
    angle += extra;

    context.beginPath();
    context.arc(a, b, 5, 0, Math.PI*2, true); 
    context.stroke();
    context.closePath();
    context.fill();

    drawLine(a,b,x,y);

    return angle;
}

Important

Note that there are many more ways to make a bouncing program. But, because I tackled the question geometrically and without 'shortcuts', the unique characteristics of my program make it very easy for you to alter it to your likings:

  • You can give an extra angle to the bounce angle easily (use var extra).
  • You can change the movement of the ball at any time (at bounce, after bounce etc.)
  • You have explicit access to the coordinates of the ball
  • All units are conventional (in degrees and coordinates; hence easy to understand and intuitive).

Also note that I did not make the program very concise because this simply wasn't my goal. I wanted to create a bouncing ball program that, although lenghty, is an exact realisation of the geometric intuition behind it.

Demo

You can find a demo of my program in this JSFiddle. Note that the beginning angle is determined randomly. Hence restarting the program will give a different angle.

enter image description here

Well, that's about it.

Good luck with building the rest of your program!

Upvotes: 6

6a6179
6a6179

Reputation: 280

depends on the angle it came in at.. so basically for making the ball bounce off the wall, just inverse the angle it came in at, e.g. if using velocity, if it was 3, then make it -3 when it collides with the wall, therefore the ball will bounce off the wall at the same angle as it was before it collided with the wall...

I hope this helps... Good luck

Upvotes: 0

Jean-Paul
Jean-Paul

Reputation: 21150

We know that

distance = average velocity x time //if acceleration is constant

Hence

time = distance / average velocity

Applying this knowledge to a two dimensional field (distance) means we have to do two things:

  • Apply Pythagoras theorem to find distance to new coordinates
  • Calculate the 'new' velocity

Before we apply the Pythagoras theorem, we have to know the direction of the move:

enter image description here

Now to find the distance to the new coordinates, we apply pythagoras theorem:

Pseudocode

//Change in coordinates
dx = Math.abs(newX - oldX);
dy = Math.abs(newY - oldY);

//Distance to travel
distance = Math.sqrt( Math.pow(dx, 2) + Math.pow(dy,2) );

//Units per increase
// time = distance / average velocity

velocity = ?;
time = distance / velocity;

//Now to find x+= .. and y+= .. we apply our knowledge of direction
//Together with our knowledge of the time it takes

case north east:        x += (dx / time);           y += (dy / time);

case south east:        x += (dx / time);           y -= (dy / time);

case north west:        x -= (dx / time);           y -= (dy / time);

case south west:        x -= (dx / time);           y += (dy / time);

Now note that the x and y represent the coordinates of the moving ball. This means that we must repeat x += .. and y += .. value of time times to reach the new coordinate.

Hence you can do something like:

for (int i = 0; i < time; i ++)
{
    switch (direction)
    {
           case "north east":  x += (dx / time);  y += (dy / time); break;

           case "south east":  x += (dx / time);  y -= (dy / time); break;

           case "north west":  x -= (dx / time);  y -= (dy / time); break;

           case "south west":  x -= (dx / time);  y += (dy / time); break;
    }
}

Also note that velocity = ? is yet to be specified by you. You can let it have a constant velocity (friction = 0), or you can implement some kind of model to mimick friction.

I hope this answers your question.

PS. This answer is actually a derivative of my other answer as I already specify direction and pixel distance in my other answer hence the step to x += .. and y += .. is actually pretty small/ straightforward.

Upvotes: 1

Related Questions