Today I tried to figure out how to use joystick for input for Killing Horizon. Last weekend I talked with a friend of mine and he thought that, apart from a spinner, a joystick seems the most natural control interface for the game, what with a 360 rotation around a circle and all (compared to a simple left-right motion with the mouse – which works fine for me, but everyone else whose used the mouse to control has had issues).

So the first issue is a mostly solved issue which is you have to convert the values of a joystick to angles. You have two axes which go from 0 in the center to +1 at the highest value to -1 at the lowest. On the X axis, this means pushing the joystick all the way to the left gives you -1 and all the way right gives you +1, and then floating point values between them for where you are on that line (.9, .85, -.3, -.777, etc…), and the same goes for the Y axis – Up is positive, Down is negative.

In order to convert the values, you have to take the arctangent of y over x and use the radians to degrees function of Unity’s Mathf functions.

1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 |
void Update () { x = Input.GetAxis("Horizontal"); y = Input.GetAxis("Vertical"); if (x > 0 | y > 0 | x < 0 | y < 0){ float a = Mathf.Round(Mathf.Atan2(y, x) * Mathf.Rad2Deg); if (a < 0) { angle = 360 + a; } else angle = a; } else angle = 360; Debug.Log("Angle: " + angle); shipDirAndSpeed = (Mathf.Lerp(0, angle*mspeed, Time.time)); if (angle != 360){ transform.RotateAround (rotatePoint, Vector3.back, shipDirAndSpeed * Time.deltaTime); } } |

Sorry for the shitty code formatting, for some reason WordPress insists that their code tag surrounds text that has no spaces between the lines, and I’m not going to be arsed to mess with it.

Now, this code in itself is kinda screwy beyond just the weird if statements and such – the actual “rotatearound” code is still from my old mouse controls (left-right) so all that’s doing right now is moving the ship as fast as the given angle is (that is, if I have angle 1, the ship is moving at 1 unit per time, and if I have the stick at 359 degrees, it’s moving 359 units per time (this isn’t angle rotation, I don’t believe, though it might be… I think the deltaTime is making it slowdown so it’s not actually moving 359 degrees)

OK, so you see the a = mathf.round stuff. That’s what I talked about in the first part.

The problem lies in the fact that when do the conversion to angles, you end up with values that are negative for the bottom half of the circle. It goes like angle 0 is the furthest right on the x axis (and 0 on y axis), and furthest left (-1) on the x axis and 0 on the y axis (i.e. center vertical) gives 180. So far so good, but what happens if you push the stick down just a bit more? You would think it would go to 181, then to 182 and so on until you cycle back to 359 right below 0, but you would be wrong!

It goes from 180 to -179, and all the way to -1. Essentially it is the inverse of the top, a mirror image as it were. At first this stumped me until I thought it through. The first solution was to subtract the absolute value of the wrong numbers from 180 and then add 180 to it.

Example: |-179| = 179; 180-179 = 1; 180+1 = 181.

Well, subtracting the absolute value of a negative number is really just adding that number to the other value (180 + -179 = 180 -179). I wouldn’t need to do any absolute value crap, just add the value to 180 which would then give you the value you need to add to 180 again.

But then I realized I’m adding 180 twice, and so simplify it to 360+negative_angle_value and that is what you need to obtain the proper angle if you continue past 180. 360-179=181

It was really easy once I typed it out to figure out what to do. So what seemed complicated at first was really as simple as:

angle = 360+a — where a = Mathf.Round(Mathf.Atan2(y, x) * Mathf.Rad2Deg).

So I’m one step farther and it didn’t take that long once I put my mind to it.