Analog stick input traces a square

Hi guys,

I have been using xbox controllers for input, and have found that the values from the analog sticks correlate to a square rather than a circle.

If I draw the vector created from these values and trace it around the character, I get a slightly rounded square as shown:

I am simply making a vector from the axis, multiplying it and adding it to the character location:

The value of an axis when the stick is exactly diagonal is about 0.885, when it should be 0.707 for a circle.

Why is this happening and how can I create a circle from it instead? (I can’t normalize the vector length because I need that for movement speed)

I could clamp the vector length to a maximum of 1 however that would still result in incorrect values when the stick is halfway to the side.

Thanks

Am I the only one with this issue? It might be worth adding that I am using xbox one gamepads.

bumping this

A Perfect Square is means it is working perfectly, anything less means there is an issue with your controller.

It looks like your controller is not returning full input range… you should get a perfect square with a perfect controller.
X and Y Axis values are separate from each other ranging from -1 to 1 (0 = centred)

The axis only return the current position on it. so .885 means it is physical design/limitation of the controller(circle border instead or Square) preventing it from reaching max extents in X/Y axis when positioned diagonally.

The controller from each axis is not circle based, all calculations are per axis between -1 and 1

If you want it to be circle based… then it potentially gets math heavy.

I am not saying you have a defective controller, i am saying that the Xbox controller itself may have a design flaw.
Players want smooth input, so they gave them a circle for the stick to slide against instead of an accurate but not smooth 90 degree corner.

This limitation of the controller sticks has been causing me some grief too. Simply plugging in the axis values to control player speed and rate of turn results in some crazy pressure applied to the sticks when on the diagonals as the user tries correct for what is lost when the stick is in the corners. Around 1/4 of the axes range is lost in the corners due to controller design - which makes me hate game controllers more than ever.

The more sophisticated solution is probably to map or project the available circular or spherical control range to a plane - like removing lens distortion from a photo. But since quick and dirty methods can be useful - give this node a try for both X and Y thumbstick axis values. Essentially we are trying to describe the largest rectangle that will fit inside the thumbstick x,y output grid. This way we get good predictable corner values and while gaining a bit of nearly unnoticeable deadzone on top, bottom, left and right.

It is possible that the values I’ve selected for the ‘In Range’ won’t be optimal for other controllers and that it might be necessary to offer a calibration screen in the game.

Yeah, I suppose the math is the problem - it looks pretty tricky. You could try to stretch the corners to a complete square, or squash them to a circle, but it could be an issue if different controller types behaved in different ways.

At the moment I am clamping the vector length between 0 and 1, which will result in a circle when the stick is fully pressed to any direction, but the diagonal value range when not fully pressed would still be different to the horizontal or vertical value range - basically because the value is being hard clamped.

The map range idea is interesting - but I am afraid it would suffer from a similar problem which is that you lose the sensitivity from the axis values 0.73 to 1 if the stick direction is not diagonal.

Probably going to have to do some heavier math to weight the values differently if the stick is facing diagonally.

Check this out, the atan2 degrees function especially.

This might be a pointless post at this point, but I’ll chime in.

First, there is nothing wrong with Xbox controller input, and second, square movement does not mean it is working well. The thumbstick is bound in a circular plastic ring, and the input when done without any extra bounding would result in a circular shape. Why it’s making a square is because a maximum range has been set for each axis(what 1 is- normal XInput range is ~32737) and anything over that is reduced to one. This maximum is set below the limit the plastic bounds create(to guarantee that the stick will output 100% throw), but that means that the stick can lean beyond that maximum(36000 or so in XInput).

The blue line is the full, circular range the stick can access if let register until the plastic ring, and the red box is the bounding area set as default(not sure if you can change that). Since the stick can lean over the red line for quite a bit of its circumference, those values are reduced to 1 on the axes, which create the sides of the square. The corners are rounded because the outer limit of the plastic ring goes within the red bound and limits input first.

The green circle is what you want to set up. A circular bounding area within the area set by the engine.

Doing that isn’t too hard mathematically, but it will take a few steps. It’s a little more wonky with visual scripting, but it’s straightforward enough.

For this to work fully, you might need to change the default deadzones in the axis config(below the axis events in input settings). The deadzones are set up by axis, creating a square deadzone which can affect full circular movement since it zeros a lot of the axis input. Reduce the deadzone to zero and you can create them in the code.

DanaFo mentioned the Atan2(degrees) which is the main thing you’ll want to use( Atan gives the reference angle, so avoid that for this). By using Atan2(stickY, stickX) you can get the angle.

To make this somewhat short, we basically want:

magnitude = sqrt[ pow((stickX - 0), 2) + pow((stickY - 0), 2) ]; if(magnitude>1){ magnitude = 1;}

This finds the distance from the center of the stick. Since the stick can overlean the normal max, setting it to 1 will normalize it.

activeRange = (magnitude - deadzone)/(1 - deadzone); if(activeRange<0){activeRange = 0;).

Rescales values so a linear 0-1 range of values occur after the deadzone. If it goes below 0, motion will be negative. Exactly like the default settings, but this creates a circular deadzone.

angle = Atan( stickY/stickX );

Finds the angle. Important for the next step.

newStickX = cos(angle);
newStickY = sin(angle);

Doing this both ensures that the stick output will point in the correct direction the stick is facing, but also normalizes the vector distance to make a perfect circle.

newStickX = newstickXactiveRange;
newStickY = newStickY
activeRange;

This will let the stick move with circular motion and give you a single circular deadzone and 0-1 range to work with. This should meet your goal.

It’s a little messy, but you can see it applied here. I was setting up an aiming system so you can ignore the accelCurve and sensitivity stuff. The newStick values should work just the way you expect at this point.

2 Likes

Hi, I´ve had some problems with this method because it was treating my joystick input kind of like a button for some reason (it was only null or full joystick), but I discovered that if I replace the InputAxis event with the Gamepad Left Thumbstick Y-Axis event (I´ve only tested it with that axis) it detects when the joystick is halfway (and puts the respective value), like it should be.
I´m putting this here in case someone else runs into the same issue.

I’ve tried applying this solution but am still getting non-1.0 values. Do you know why that would be?