Determining direction of 2D vector with 8 discrete directions (horizontal, vertical and diagonals)
-
05-03-2021 - |
Pregunta
I have the input of a gamepad thumbstick that's a vector. The range of values of the vector defines a circle within a rectangle that spans -1, -1 to +1, +1.
I want to know which of the 8 possible directions the vector is closes to. Don't worry about the case where the vector is (0, 0), I'll deal with this separately. How can I do this?
Note: This is for a game, so this function will be ran very frequently. Anything to speed up the function would help, for example I know I could use the dot product and trig to work out the angle between my vector and each direction vector, however I wonder if something better could be done given I don't need the exact angle.
Solución
Imagine a square inside the -1..1 square, where the stick position (X) is on one of the sides:
+-----------------+ +-----------------+
| | | |
| | | |
| X | | +-----X-+ |
| | | | | |
| O | | | O | |
| | | | | |
| | | +-------+ |
| | | |
| | | |
+-----------------+ +-----------------+
You just have to find out which side the coordinates are on, and check whether it's closer to the center of the side, or a corner. If the inner square is too small, you can consider the stick to be centered.
Something like:
Public enum Direction {
None,
LeftUp, Up, RightUp, Right, RightDown, Down, LeftDown, Left
}
public Direction GetDirection(double x, double y) {
double absX = Math.Abs(x);
double absY = Math.Abs(y);
if (absX < 0.1 && absY < 0.1) {
// close to center
return Direction.None;
}
if (absX > absY) {
// vertical side
double half = absX * 0.4142;
if (x > 0) {
// left side
if (y > half) return Direction.LeftDown;
if (y < -half) return Diretion.LeftUp;
return Direction.Left;
} else {
// right side
if (y > half) return Direction.RightDown;
if (y < -half) return Direction.RightUp;
return Direction.Right;
}
} else {
// horisontal side
double half = absY * 0.4142;
if (y > 0) {
// bottom
if (x > half) return Direction.RightDown;
if (x < -half) return Direction.LeftDown;
return Direction.Down;
} else {
// top
if (x > half) return Direction.RightUp;
if (x < -half) return Direction.LeftUp;
return Direction.Up;
}
}
}
No trigonometry, just simple comparisons, so it should be pretty fast. :)
(Although I used trigonometry to calculate the point 0.4142, which is the tan(22.5), or the position on the side where the angle is 45/2.)
Otros consejos
sin a / cos a = tan a,
and you have
Y = sin a
X = cos a
So, apply the reverse tangent function to Y/X and you'll get the angle.
EDIT: In a full circle, there are two angles with the same tangent value (a
and a
+ pi
). Use the sign of X and Y to determine which one is the valid one.
You could use a pre-built approximation table of tan^-1. Should be very fast to calculate an angle based on the size of your input vector, and you probably won't need a very large resolution in the table (in fact, if you only want 8 discrete directions, 8 entries in your table will suffice).