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.
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.
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
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.
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
Different Like Behaviour Between My Application and the Access Query Wizard
How to Create an Expression Tree to Represent 'String.Contains("Term")' in C#
Using the C# Dispatcher in Wpf Applications
Possible to Calculate Md5 (Or Other) Hash with Buffered Reads
Running Msbuild Programmatically
Does C# Support Multiple Inheritance
Capturing Mouse/Keyboard Events Outside of Form (App Running in Background)
Metadata Were Not Loaded Using Metadatatype
How to Set the Wix Installer Version to the Current Build Version
How to Catch SQLserver Timeout Exceptions
Generic Way to Check If Entity Exists in Entity Framework
How to Get Urls of Open Pages from Chrome and Firefox
Md5 Hash with Salt for Keeping Password in Db in C#
How to Turn Off or Handle Camelcasing in JSON Response ASP.NET Core