Search code examples
c#unity-game-enginemathmappingjoystick

Mapping values to a direction


been trying to figure this out for ages now. Was trying to figure out a way to have my joystick when moved in any direction represent the numbers shown on the image in that direction. I essentially I want my players movement speed to be that of the value in the direction of the stick.

What I am aiming for

I just want some guidance in the right direction.


Solution

  • If I understand correctly what you have as input are

    • the value 3 in "forward/up" (Y) direction (will call it maxValue below)
    • the value 1 in "backward /down" (-Y) direction (will call it minValue below)
    • A 2D vector user input in XY

    So what you could use is a Vector2.Dot product between the input and Vector2.up (= fully Y direction)

    var input = new Vector2(userInputX, userInputY).normalized;
    var dot = Vector2.Dot(input, Vector2.up);
    

    This will be

    • 1 if the input is fully aligned with the up vector
    • -1 if the input is fully contrary to the up vector => fully aligned with down
    • 0 if it is exactly 90° to up to aligned with left or right
    • and any values in-between according to the degree of alignment

    This information you can then further use as a factor in Mathf.Lerp which interpolates between two values given a factor

    • 0 fully take first value
    • 1 fully take second value
    • anything in-between linear interpolates between first and second value

    you just need to map the given range from the dot which is within -1 and 1 onto an interpolation factor range between 0 and 1 like e.g.

    // Remap the range -1 to 1 into the factor 0 to 1
    var factor = (dot + 1f) / 2f;
    var result = Mathf.Lerp(minValue, maxValue, factor);
    

    In case also the left/right value is a given value - assuming this because your values in the graphic wouldn't match as the left/right value for 3 and 1.5 would actually be 2.25 - you can basically use the same dot calculation from above but then have a check in which half of the circle you are and split the interpolation into the two according parts:

    float result;
    if(dot >= 0) // dot is directly a factor 0 to 1 between left/right and up
    {
        result = Mathf.Lerp(middleValue, maxValue, dot);
    }
    else // dot is directly a negative factor -1 to 0 between left/right and down
    {
        result = Mathf.Lerp(middleValue, minValue, -dot);
    }
    

    As in the comments, indeed Dot is basically based on sinus so there might occur some sort of smoothing around the end values.

    Alternatively you could simply go by angle and do e.g.

    var angle = Vector2.Angle(input, Vector2.up);
    float result;
    if(angle <= 90)
    {
        var factor = angle / 90f;
        result = Mathf.Lerp(maxValue, middleValue, factor);
    }
    else
    {
        var factor = (angle - 90) / 90f;
        result = Mathf.Lerp(middleValue, minValue, factor);
    }