This is not done with angles.
This is done with vectors. Well, actually you could do it with angles...
In any case, the ball has a velocity vector at the instant of collision, and will have a new velocity vector from the collision resolution. There are a few ways we can go about getting that new velocity vector:
- You can use vector projection to extract the components, and use them to construct the new velocity.
- You can express the orientation as a transformation matrix, the flipping as another transformation matrix, compose them... and that is a bounce transformation matrix.
- You can throw trigonometry at it.
I suggest the first path. It is the one that matches intuition more closely. Also optimizes well. Although, I will not be doing that in this answer.
What intuition?
Consider the simple case. If the ball were bouncing with a vertical wall, all you would do is flip the x axis of the velocity vector. If it were bouncing to an horizontal floor, all you would do is flip the y axis of the velocity vector. That is you flip the component that is normal to collision. We still need to do that, but with arbitrary orientations.
Basically, I'm saying that the collision flips the velocity along the normal of the surface, but not tangentially to the surface. The component that was going towards the obstacles is flipped so it goes away instead. In order words... it bounces.
Thus, the new velocity vector, will be the original velocity vector with the component normal to the collision flipped. Perhaps multiplied by some elasticity factor. But that is beyond the scope of the question.
As I was saying, you need to break the velocity vector into components. One component goes along the normal at the point of collision, the other goes tangent. Then you flip the normal component sign, compose it with the tangent component and that gives you the new velocity vector.
In case it is not clear, the components of the vector add up to the vector.
We have the velocity vector v
, and the normal vector n
. Then the velocity component along the normal is v⊥n
(v
projected on n
) and the tangent component is v - v⊥n
. Because the components add up to the vector.
Knowing that, we get new velocity vector by flipping v⊥n
, which gives us -v⊥n
and adding the other component (v - v⊥n
). That is, v' = -v⊥n + v - v⊥n = v - v⊥n - v⊥n = v - 2*v⊥n
.
Of course, to actually implement this, we a projection function. Assuming you do not have a library that offers such function, you can implement it like this:
v⊥a = (a/|a|)*(v·a)/|a|
Where |a|
is the length of a
, and ·
denotes dot product.
You can find this formula in any vector algebra or analytic geometry book. I will not explain how to derive it here.
As you probably know, |a|
would be just Pythagoras, and ·
is the sum of the products of the components:
|a| = sqrt(a.x * a.x + a.y * a.y /*+ a.z * a.z*/)
a·b = a.x * b.x + a.y * b.y /*+ a.z * b.z*/
That is enough information to write some code:
//This code has not been optimized whatsoever
Vector Add(Vector a, Vector b)
{
return Vector(a.x + b.x, a.y + b.y/*, a.z + b.z*/);
}
Vector Mult(Vector a, float s)
{
return Vector(a.x * s, a.y * s/*, a.z * s*/);
}
float Dot(Vector a, Vector b)
{
return a.x * b.x + a.y * b.y /*+ a.z * b.z*/;
}
float Norm(Vector a)
{
return sqrt(a.x * a.x + a.y * a.y /*+ a.z * a.z*/);
}
Vector Normalize(Vector a)
{
return Mult(a, 1/Norm(a));
}
Vector Project(Vector v, Vector a)
{
return Mult(Normalize(a), Dot(v, a)/Norm(a));
}
Vector Bounce(Vector v, Vector n)
{
return Add(v, Mult(Project(v, n), -2));
}
What angles?