Distance-Based
You will basically need to take two things into consideration: a threshold (inversely a deadzone) and the dominant vector. Given an enum
like the following:
public enum Direction { None, Up, Down, Left, Right }
You would find the dominant direction (if the deadzone isn't in play) and use that.
// You could make this user-configurable.
const float Deadzone = 0.8f;
const float DiagonalAvoidance = 0.2f;
public static Direction GetDirection(Vector2 gamepadThumbStick)
{
// Get the length and prevent something from happening
// if it's in our deadzone.
var length = gamepadThumbStick.Length();
if (length < Deadzone)
return Direction.None;
var absX = Math.Abs(gamepadThumbStick.X);
var absY = Math.Abs(gamepadThumbStick.Y);
var absDiff = Math.Abs(absX - absY);
// We don't like values that are too close to each other
// i.e. borderline diagonal.
if (absDiff < length * DiagonalAvoidance)
return Direction.None;
if (absX > absY)
{
if (gamepadThumbStick.X > 0)
return Direction.Right;
else
return Direction.Left;
}
else
{
if (gamepadThumbStick.Y > 0)
return Direction.Down;
else
return Direction.Up;
}
}
Angle-Based
With this one you basically ensure that, once again, we don't fall within a deadzone - but also that the angle is within certain ranges.
const float PI = (float)Math.PI;
// You could make this user-configurable.
const float Deadzone = 0.8f;
const float AliveAngle = PI / 4 - (PI / 16);
// Remember PI = 180 degrees
const float AliveAngle1Start = PI * 0;
const float AliveAngle1End = PI * 0.5f - AliveAngle;
const float AliveAngle2Start = PI * 0.5f + AliveAngle;
const float AliveAngle2End = PI * 1f - AliveAngle;
const float AliveAngle3Start = PI * 1f + AliveAngle;
const float AliveAngle3End = PI * 1.5f - AliveAngle;
const float AliveAngle4Start = PI * 1.5f + AliveAngle;
const float AliveAngle4End = PI * 2f - AliveAngle;
public static Direction GetDirection(Vector2 gamepadThumbStick)
{
// Get the length and prevent something from happening
// if it's in our deadzone.
var length = gamepadThumbStick.Length();
if (length < Deadzone)
return Direction.None;
// Find the angle that the stick is at. see: http://en.wikipedia.org/wiki/File:Atan2_60.svg
var angle = (float)Math.Atan2(gamepadThumbStick.Y, gamepadThumbStick.X);
if (angle < 0)
angle += PI * 2; // Simpify our checks.
if (angle > AliveAngle4End)
return Direction.Right;
if (angle > AliveAngle4Start)
return Direction.None;
if (angle > AliveAngle3End)
return Direction.Up;
if (angle > AliveAngle3Start)
return Direction.None;
if (angle > AliveAngle2End)
return Direction.Left;
if (angle > AliveAngle2Start)
return Direction.None;
if (angle > AliveAngle1End)
return Direction.Down;
if (angle > AliveAngle1Start)
return Direction.None;
return Direction.Right;
}
// Inlined in release so don't worry about it.
private static bool IsBetween(float value, float start, float end)
{
return value > start && value < end;
}
Remarks
The way I did the distance-based one would likely emulate the (possibly more expensive) angle-based one - this is because of the absDiff < length * DiagonalAvoidance
. Either way experiment with them and see which you like best.