# How can I map thumbsticks to cardinal directions?

I've got a tile based game that has notions of moving up, down, left, right and the diagonals (by alternating horizontal/vertical movement).

I've found that if I use the default ThumbStick<Direction> buttons that XNA provides I'm often moving diagonally instead of in a straight line. E.g. moving the thumbstick left, often just triggers 'down' too (but only just).

How can I map moving a thumbstick to the 8 discrete directions so this accidental diagonal movement doesn't occur?

I'm starting to think about favouring up/down/left/right over diagonals (as you more frequently move straight in my game), however, I'm not sure how do put this into code.

• that's because you're probably taking any value != 0 as a "direction trigger" for example, if the thumbstick is 99% to the left and 1% to the bottom, it will trigger both. Try to use a 15% threshold. (or some other value that you feel good with) – Gustavo Maciel Jan 23 '12 at 21:40
• The XNA does use a threshold, although I've added my own (higher) threshold on top and it seems to be doing the trick. If you post as an answer I'll accept. – George Duckett Jan 23 '12 at 21:58
• -1: For accepting the wrong answer. That'll teach you to wait more than a few minutes before clicking the "accept" tick. – Nicol Bolas Jan 24 '12 at 1:02
• @NicolBolas: I don't see anything wrong with accepting an answer that is posted that solves my problem. I can always change that if I feel a subsequent answer is better. – George Duckett Jan 24 '12 at 7:55
• @NicolBolas: But Gtoknu's answer is more useful to me. Possibly next time I might wait a little longer, but I'd probably go for the same answer as although detailed, Jonathan's one in my case would be overkill. – George Duckett Jan 24 '12 at 8:34

That's because you're probably taking any value != 0 as a "direction trigger". For example, if the thumbstick is 99% to the left and 1% to the bottom, it will trigger both.

Try to use a 15% (or some other value that you feel good with) as a threshold.

## 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 DiagonalAvoidance = 0.2f;

{
// Get the length and prevent something from happening
// if it's in our deadzone.
return Direction.None;

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)
{
return Direction.Right;
else
return Direction.Left;
}
else
{
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 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;

{
// Get the length and prevent something from happening
// if it's in our deadzone.
return Direction.None;

// Find the angle that the stick is at. see: http://en.wikipedia.org/wiki/File:Atan2_60.svg
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.

• Thanks for going into more detail and posting after an accepted answer. In my case the first one is simple and works, so I'll keep that accepted. +1 – George Duckett Jan 24 '12 at 7:56
• @GeorgeDuckett that's fine, just sticky this answer for when you get bug reports because of the missing dead angle. – Jonathan Dickinson Jan 24 '12 at 10:29
• @JonathanDickinson Using your angle-based method. I have noticed that when I press up on the left thumbstick, it returns Direction.Down and when I press down, it returns Direction.Up. This is an easy fix, I just want to let you know so you can make the changes yourself. – Kyle Uithoven Aug 17 '12 at 3:22
• @KyleUithoven oops, I will make a fix ASAP. Thanks for the feedback ;). – Jonathan Dickinson Aug 17 '12 at 14:59