Search Unity

Bug Controllers' Sticks can't return full ranges of values in diagonals (only normalized values)

Discussion in 'Input System' started by Max_Bol, Jan 30, 2022.

  1. Max_Bol

    Max_Bol

    Joined:
    May 12, 2014
    Posts:
    168
    In my current main project, I'm trying to access the regular (not the normalized) value of for 2 axis.
    Those 2 values determines multiple variables used in the project such as movements, floor & wall detection and quite a bit more.

    Basically, I built a custom-made collision & movement system which is simpler and 4x to 20x lighter to use than a character controller (even more from a Rigid Body) as the physics are only calculated "if needed" based on what's happen in-game and what's done by the player.

    One key element of that system, to check for wall collision, is what input is applied by the player. The movement is calculated by mouse position as well as either AWSD / Arrows / [Num]4862 keys on a keyboard which are all set to "Digital" mode, hence returning 0.0f-1.0f on 2 axis in values or Left Stick on a controller.

    Basically, if a player is not "falling" and press a key or the stick to move, all the physics done to detect where the player should move is done with 3 or 4 raycasts which, in the ultimately in the end, return the frame's next position for the player. This simulates not only collisions, but also allows me to calculate easy-to-handle descent, climbing, stepping, rolling and other movements-related action with no additional physics requirement.

    Out of those 3 or 4 raycasts, 1 or 2 requires that I get a particular value out of the previously mentioned movement input value. With the keyboard, the value are properly returning full range of 0.0f and 1.0f on both X and Y axis even when I press both at the same time (as the Vector2 is not normalized).

    On the other hand, the controller seems to fails at returning the same range of values.
    At first, I though it would be a fault in my controller, but I got the exact same values out of the following controllers:
    - Xbox One: Official Microsoft 1708 model (comes with the Xbox One S)
    - Xbox One: PowerA 1516953-01 (wired)
    - Xbox 360: Official Microsoft "White Xbox 360 Wired controller" model
    - Original Xbox Controller (with a Xbox-PC adaptator)
    - PS3 Controller (USB plugged)
    - EVO VR model Mi-VG010-101 (via Bluetooth)
    - Xbox 360: Red version of a Chinese cheap no-name brand. (Wired)

    To put it simple, when you press the most "perfect" diagonal on the controller, the highest value you'll get out of both axis at once is approx. 0.70f. That value is the one you get if you use the Normalized Digital mode on keyboard.

    When I look at the Input Actions window, there's no way to turn off that normalized value to get the (raw?) full range of the stick input. (There's no mode to be selected and the Processor only have "Stick Deadzone" set in.)

    This bug only appears with the Input System. The old Input seems to properly return full range values (1.0, 1.0) on the least sensible of my controllers while, with the Input System, they all are stuck at returning (0.70, 0.70) at most if stick is pressed at 45 degree clockwise. For example, the cheap no-name brand Xbox 360 controller has such a cheap analogue stick that pressing between 35 and 55 degrees return fulls diagonal (1.0, 1.0) values easily with the old Input method and returns (0.7, 0.7) with the Input System.

    This problem, as minor as it seems, makes the player able to get 30% closer to any wall than other input methods when using a controller. The only fix I could come up with is to boost the controller's stick value by a variable that goes from 1.0 to 1.428f based on formula that analyses both values at once. It works to allow the controller to return (1.0, 1.0) when pressing diagonal (45, 135, 225 and 315 degrees) on the stick, but it doesn't feel good since it's as if the sensitivity raises when pressing close to the diagonals.
     
  2. This is not a bug. It's all about movement. Just think about it.
    If you push the controller forward, you move with 1 "unit" speed max on the Z axis, right?
    If you push the controller to the right, you move with 1 "unit" speed on the X axis, right?
    If you push the controller to 45 degrees to the top-right: you shouldn't move by 1 "unit" on the Z and 1 "unit" on the X axis. That would mean you're moving diagonally faster than on the four cardinal directions. (the same problem happens when you are using square grid instead of hexagonal grid in tactical games)

    You need to adapt your custom collision system to take this into consideration.

    And if you want unrealistic movement, then you can set your Vector2 binding settings from DigitalNormalized to Digital.
     
    Last edited by a moderator: Jan 30, 2022
  3. Max_Bol

    Max_Bol

    Joined:
    May 12, 2014
    Posts:
    168
    Your solution gave me an hint and there's a solution, but It's is a bug and I'll explain a bit more details.

    The main mistake you make is thinking that the stick is for movement. That's 100% wrong. The stick is to return 2 values of -1f to 1f values and that's it. The usage of those values and whatever you might do with it or how to limit them is something that has to be done after the values are read from the input and only as an option.

    In case you don't know, controllers' "sticks" (actually called "thumbsticks" as it's usually the thumbs that rest and pushes them) are mechanical devices which uses 2 electrical signal out of 2 separate gyros gears to calculate the inputs on 2 axis. The signal usually goes from 0 (no signal) to 1 (full signal) and is converted to -1 to 1. When rested (middle), the thumbstick returns, if in proper condition, a constant half-signal from both gyros gears. Pressing a perfect 45 degree on it returns a full signal on both axis while pressing a perfect 225 degrees returns a null signal on both axis.

    What you describe as "moving diagonally faster" is only true if you're handling both axis as a single speed parameter by adding both axis as-is, which is not how I use the value. That's why you might normalize the values (which is when it return 0.7f on both axis if at 45 degrees for example) if you want to use the direction as a speed value. If you want the real orientation of the input, you need the value in its raw state and not normalized.

    Here's an example (which is part of how I use the data):
    Let's say you cast a physics' ray in a direction based on the input and you want the ray to always reach up to 1.0f in distance, but there's no need to detect that far if you're not pressing full-tilt on the thumbstick. (For keyboard, the value always returns 0 or 1 anyway, if you use the Digital non-normalized mode.) Well, if you press it up, down, left or right, you'll get a raycast up to 1.0f away. If you press Up-Right, Up-Left, Down-Left or Down-Right, you'll never send a raycast further than 0.7f in diagonal because of the normalized value (with the problem).

    Another example where the current binding fails is if you implement diagonal animation movements. If you apply the input as blending animations values, you'll never be able to have a perfect 1.0f strength in the diagonal movement animation as it will always be stuck to a 0.7f in each axis.

    Currently, the solution (found from your hint) is to set a binding as "2D Vector Composite" instead of a regular "Binding" and that is the only way of having the option to set its mode to Digital as regular "Binding" doesn't have that particular option. It seems that regular Binding is automatically set to DigitalNormalized internally with no way of having the actual real value of the inputs. Once a binding as "2D Vector Composite" is created, it's possible to bind the composite Up, Down, Left and Right with "Stick Up", "Stick Down", "Stick Left" and "Stick Left".

    Natively, a thumbstick is supposed by default (mechanically speaking) to reach 1.0f whenever it reaches the furthest of each cardinal direction (like a squared zone) and, currently, Unity's Input System basically breaks it if you use a regular Binding. Using 2D Vector Composite is basically the equivalent of trying to modify the original input signal data for interpretation while what I wanted is use it in its raw state which is what the regular "Binding" is supposed to do. So, in other words, it's like if you were to prepare the input signal for being manipulated (as raw signal) so it's converted into a composite signal (like when you convert 4 keyboard keys into 2 vectors), then tell then engine "reverse back" to the raw input signal because you don't need it to be converted while the regular "Binding" is exactly like having the raw signal, then applying Vector2.Normalized on it before feeding it as values.

    I know that you might not think of a thumbstick as a "square" because most common controllers have round movement zones, but that's just a mecanical gimmick since the round zone thumbstick is more popular than, for example, the octagon zone (such as the one you had on the Nintendo 64 and the GameCube controller), but the mechanism inside is the same, with a few adjustments managed by the pressure setting set in the controller's chips. This is why cheap or defect controller (like my Xbox 360 red no-brand controller) can have issues like a "sticking" 45 degrees zone on each diagonal as their pressure setting is just too bad or even none-existent.

    In fact, if the regular "binding" of a stick was properly working (without having its automatic normalized), it would still be possible to normalize the value by adding a "Normalize Vector 2" processor to the binding. The inverse is not possible because, once normalized, it's basically a stupid waste to try to de-normalize a Vector2 back to its original state.

    The reason why I know so much about controllers is because I'm also building my own custom ones with a raspberry pi and I have fun implementing their compatibility within Unity. Some might have read it around, but every project I'm currently working on will have some form of compatibility with a raspberry pi for unique features like custom controllers.

    The solution to this problem is quite simple: Make the regular "Binding" type of binding to return unmodified input values instead of normalized on and, for those who need it absolutely normalized, they can add a processor to the binding or do it even within the script.
     
    Alex_Galvez likes this.
  4. Thanks for the TL;DR condescending wall of text. It is still not a bug.
    Yes, you should read more carefully:
     
    Last edited by a moderator: Jan 31, 2022
  5. liambilly

    liambilly

    Joined:
    May 13, 2022
    Posts:
    154
    it is not possiple to change the binding settings of stick controllers