How to Calculate Bounce Angle

How to calculate bounce angle?

I think an easier way to do this is to use the velocity of the missile instead of calculating angles. Say you have a missile that has xVelocity and yVelocity to represent its movement horizontally and vertically. Those velocities can be positive or negative to represent left, right, up, or down.

  • If a missile hits a top or bottom border reverse the sign of the yVelocity.
  • If a missile hits a left or right border reverse the sign of the xVelocity.

This will keep the movement in the opposite axis the same.

Borrowing the image from ChrisF's answer, let's say the missile starts out at position I.

Angle of Reflection

With the xVelocity and yVelocity both being positive (in 2D graphics right and down are typically positive) the missile will travel in the direction indicated. Let's just assign values of

xVelocity = 3
yVelocity = 4

When the missile hits the wall at position C, its xVelocity shouldn't change, but its yVelocity should be reversed to -4 so that it travels back in the up direction, but keeps going to the right.

The benefit to this method is that you only need to keep track of a missile's xPosition, yPosition, xVelocity, and yVelocity. Using just these four components and your game's update rate, the missile will always get redrawn at the correct position. Once you get into more complicated obstacles that are not at straight angles or are moving, it will be a lot easier to work with X and Y velocities than with angles.

Calculate the bouncing angle for a ball/point

The "bounced velocity vector" v' is obtained from the original velocity v and the surface normal unit vector n with 2(n . v)n + v where . stands for the vector dot product. This is usually called a reflection; the velocity vector is reflected across the surface normal.

In case you're not familiar with the terminology, the surface normal is a vector that is perpendicular (at 90-degree angle) to the surface. A unit vector is a vector with length 1.

Sample Image

I assume you already have a class to represent vectors, called Vec, with methods to multiply a vector with a scalar and to add two vectors. You could write the bounce operation as:

static Vec bounce(Vec n, Vec v) {
Vec tmp = Vec.scalarMultiply(-2*Vec.dot(n,v), n);
return Vec.add(tmp, v);
}

static double dot(Vec a, Vec b) {
return a.x*b.x + a.y*b.y; // + a.z*b.z if you're in 3D
}

As for how to get the surface normal, that will depend on if you're in 2D or 3D. Assuming 2D, it's simple: if (x,y) is the vector from P1 to P2, then (-y,x) is perpendicular to it, and one unit normal would be:

n = (-y/sqrt(x*x+y*y), x/sqrt(x*x+y*y))

The other possible unit normal is -n. You would use one or the other depending on which side of the surface you are.

You should store the normal vector with the geometry of the scene so you don't have to calculate it every time.

Calculate angle change after hitting a tilted wall

It is not wise to use angle for moving ball and calculate Cos/Sin again and again. Instead use unit velocity direction vector with components vx, vy like this:

new_x = old_x + vx * Velocity_Magnitude * Time_Interval

Note that vx = Cos(angle), vy = Sin(angle), but with direction approach you seldom need to use trigonometric functions.

Tilted wall with angle Fi has normal

nx = -Sin(Fi)
ny = Cos(Fi)

To find reflection , you need to calculate dot product of velocity and normal

dot = vx * nx + vy * ny

Velocity after reflection transforms:

vnewx = v.x - 2 * dot * n.x 
vnewy = v.y - 2 * dot * n.y

Use these values for further moving

(note that you can use both internal and external normal direction, because direction flip changes both components, and sign of 2 * dot * n.x remains the same)

Examples:

horizontal moving right
vx=1, vy=0
30 degrees wall has normal
nx=-1/2, ny=Sqrt(3)/2
dot = -1/2
vnewx = 1 - 2 * (-1/2) * (-1/2) = 1/2
vnewy = 0 - 2 * (-1/2) * Sqrt(3)/2 = Sqrt(3)/2
(velocity direction angle becomes 60 degrees)

horizontal moving left
vx=-1, vy=0
330 degrees wall (left bottom corner) has normal
nx=1/2, ny=Sqrt(3)/2
dot = -1/2
vnewx = -1 - 2 * (-1/2) * (1/2) = -1/2
vnewy = 0 - 2 * (-1/2) * (Sqrt(3)/2) = Sqrt(3)/2
(velocity direction angle becomes 120 degrees)

Calculate random bounce angles

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

Sample Image

Sample Image

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.

Sample Image

Well, that's about it.

Good luck with building the rest of your program!

Angle Reflexion for bouncing ball in a circle

I recommend do calculate the reflection vector to the incident vector on the circular surface.

In the following formula N is the normal vector of the circle, I is the incident vector (the current direction vector of the bouncing ball) and R is the reflection vector (outgoing direction vector of the bouncing ball):

R = I - 2.0 * dot(N, I) * N.

Use the pygame.math.Vector2.

To calculate the normal vector, you' ve to know the "hit" point (dvx, dvy) and the center point of the circle (cptx, cpty):

circN = (pygame.math.Vector2(cptx - px, cpty - py)).normalize()

Calculate the reflection:

vecR = vecI - 2 * circN.dot(vecI) * circN

The new angle can be calculated by math.atan2(y, x):

self.angle  = math.atan2(vecR[1], vecR[0])

Code listing:

import math
import pygame
px = [...] # x coordinate of the "hit" point on the circle
py = [...] # y coordinate of the "hit" point on the circle

cptx = [...] # x coordinate of the center point of the circle
cpty = [...] # y coordinate of the center point of the circle

circN = (pygame.math.Vector2(cptx - px, cpty - py)).normalize()
vecI = pygame.math.Vector2(math.cos(self.angle), math.sin(self.angle))
vecR = vecI - 2 * circN.dot(vecI) * circN

self.angle = math.pi + math.atan2(vecR[1], vecR[0])


Related Topics



Leave a reply



Submit