Ben Mc
Ben Mc

Reputation: 2078

Calculate angle of moving ball after collision with angled or sloped wall that is a 2D line segment

If you have a "ball" inside a 2D polygon, made up of say, 4 line segments that act as bounding walls, how do you calculate the angle of the ball after the collision with the irregularly sloped wall?

I know how to make the ball bounce if the wall is horizontal, vertical, or at a 45 degree angle. I also have my code setup to detect a collision with the wall.

I've read about dot products and normals, but I cannot figure out how to implement these in Java / Android. I'm completely stumped and feel like I've looked up everything 10 pages deep in Google 10 times now. I'm burned out trying to figure this out, I hope someone can help.

Upvotes: 1

Views: 5617

Answers (2)

Will Kru
Will Kru

Reputation: 5212

This might come in handy for you http://www.tonypa.pri.ee/vectors/tut07.html

Upvotes: 0

Tommy
Tommy

Reputation: 100602

Apologies in advance: I don't know the correct Android types. I'm assuming you have a vector type with properties 'x' and 'y'.

If the wall were horizontal and the current velocity were 'vector' then it'd be as easy as:

vector.y = -vector.y;

And you'd leave the x component alone. So you need to do something analogous, but more general.

You do that by substituting the idea of the line normal (a vector perpendicular to the line) for hard coding for the y axis (which is perpendicular to the horizontal).

Since the normal is orthogonal to the line, it can be found by rotating the line by 90 degrees. In 2d, the vector (a, b) can be rotated by 90 degrees by converting it to (-b, a). Hence if you have a line from (x1, y1) to (x2, y2) then you can get the normal with:

vectorAlongLine.x = x2 - x1;
vectorAlongLine.y = y2 - y1;

normal.x = -vectorAlongLine.y;
normal.y = vectorAlongLine.x;

You don't actually care how long the original line was (and it'll affect computations later when you don't want it to), so you want to make the normal be of length 1 irrespective of its current length. You can do that by dividing it by its current length. So, e.g.

lengthOfNormal = Math.sqrt(normal.x*normal.x + normal.y*normal.y);
normal.x /= lengthOfNormal;
normal.y /= lengthOfNormal;

Using the Pythagorean theorem there to get the length.

With the horizontal line, flipping on the y axis was the same as (i) working out what the extent of the vector extends along the y axis; and (ii) subtracting that amount twice — once to get the velocity to be 0 in that direction, again to make it the negative version of the original. That is, it's the same as:

distanceAlongNormal = vector.y;
vector.y -= 2.0 * distanceAlongNormal;

The dot product is used in the general case is to work how far the vector extends along the normal. So it does the same as taking vector.y does for the horizontal line. This is where you possibly have to take a bit of a leap of faith. It's a property of the dot product and you can persuade yourself by inspecting a right-angled triangle. But for now, if you had a horizontal line, you'd have ended up with the normal (0, 1). Since the dot product would be:

vector.x * normal.x + vector.y * normal.y

You'd compute:

distanceAlongNormal = vector.x * 0.0 + vector.y * 1.0;

Which is obviously the same thing as just taking the y component.

Having worked out the distance along the normal, you actually want to then subtract that amount times the normal times two. The only additional step here is multiplying by the normal to get a 2d quantity to subtract. That's because you're looking to subtract in the order of the normal. So complete code, based on a normal computed earlier, is:

distanceAlongNormal = vector.x * normal.x + vector.y * normal.y;
vector.x -= 2.0 * distanceAlongNormal * normal.x;
vector.y -= 2.0 * distanceAlongNormal * normal.y;

If you hadn't made normal of length 1, then you'd need to divide by the length here, since the dot product would scale the distanceAlongNormal value by that amount.

Upvotes: 8

Related Questions