Take the 2-minute tour ×
Game Development Stack Exchange is a question and answer site for professional and independent game developers. It's 100% free, no registration required.

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.

share|improve this question
    
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
1  
@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

2 Answers 2

up vote 3 down vote accepted

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.

share|improve this answer

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.

share|improve this answer
    
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

Your Answer

 
discard

By posting your answer, you agree to the privacy policy and terms of service.

Not the answer you're looking for? Browse other questions tagged or ask your own question.