Wednesday 3 February 2016

Using a joystick (part 19)

Ok, it's time for another little sidetrack here:)

I bought myself a little USB 4 axis gamepad and decided to have a look at the cross platform joystick input functions that are part of GLFW.

First off, for those people using Mac, Windows guys have a one up on us. Joystick support seems to be a lot better on Windows. You can use XBox one controllers right off the bat, many bluetooth controllers work, etc.
On Mac it seems a bit more of a secret art. There are many 3rd party drivers you can install to make things work. Interestingly enough it's more that a lot of it seems a bit hidden. I was glad to find out the gamepad I bought worked without a problem with GLFW even though Mac OS X itself seemed oblivious to it being connected.

The gamepad I got is a $20 wired Logitech F310. Important here is that it has two modes that may exist on other gamepads as well. There is a switch on the back label X <=> D.
X stands for something like XInput and is the default and seems to be directly related to DirectX support, needless to say it does not work on the Mac.
D stands for DirectInput, it needs to be selected before connecting the device to the Mac and then GLFW will pick it up straight away.

Now when you look at my checked in source code you'll see that I've written a wrapper for the GLFW joystick code but in this particular case, that really is extreme overkill. Again my reasoning for this is that I want to be able to use a different framework for platforms that GLFW doesn't support.

In this case it is absolute overkill and I'll just discuss the GLFW calls here.
GLFW has defined a number of constants from GLFW_JOYSTICK_1 to GLFW_JOYSTICK_LAST to index each joystick. Basically the first joystick you connect to your system will be GLFW_JOYSTICK_1, the second will be GLFW_JOYSTICK_2, up until the number of joysticks you've got connected. Once numbered a joystick will remain accessibly through that constant so even if I disconnect joystick #1, joystick #2 will remain joystick #2.

To find out if a joystick is connected you simply call:
if (glfwJoystickPresent(GLFW_JOYSTICK_1) == GL_TRUE) {
  // process our joystick info

Now an interesting difference with the joysticks is that unlike the keyboard or mouse, you don't get any events that the user made any input. Instead you poll the joystick state. That may sound wasteful but it is not, a user may be holding a control stick firmly in a certain direction and you'll need to react to that, it is far more likely you will need to react to the joystick in every frame.

This is something you would add for each joystick in your update function (engineUpdate in our example). Good practice is to query your input devices, do whatever changing to positions of object you need to do, etc. in an update function, then call your render function to render the result.

For this also note that we send pSecondsPassed to our update function. This is a fairly precise timer and if you keep it's value from last frame you can obtain a delta of time elapsed. It's often a good idea to use this delta in your adjustments so that on slower machines the users input will seem just as responsive as on faster machines. I'm not doing this in our current example however.

On a quick side note purely for informational purpose you can call glfwGetJoystickName if you wish, it will return a device name for your joystick.

The important things on the joystick are the buttons and axes. Now here is where things get a little tricky because every devices has a different layout. You may thus need to react differently depending on the number of axes and buttons are available and maybe offer the user with the ability to map them to certain actions.
We can generally be certain that most joysticks have 2 axes and 2 buttons that are the primary controls and that further controls are logically added.

GLFW allows you to get the current state for all axes with the function glfwGetJoystickAxes. It returns a pointer to a buffer of floats, each entry referring to an axis with its value between -1.0 and 1.0.
The state of our buttons can be queried by calling glfwGetJoystickButtons. This returns a pointer to a buffer of ints, one entry per button with the value 0 or 1, one being the button is pressed.

For example you could do:
if (glfwJoystickPresent(GLFW_JOYSTICK_1) == GL_TRUE) {
  int axesCount, buttonCount;
  float * axes = glfwGetJoystickAxes(GLFW_JOYSTICK_1, &axesCount);
  int * buttons = glfwGetJoystickButtons(GLFW_JOYSTICK_1, &buttonCount);
  
  // process our joystick info
  ...
};
It's no more difficult then that.

Our sample application now allows for both joystick and keyboard input to rotate our Tie-bomber.

Download the source here

What's next

So I've gotten a bit sidetracked on a new project that I hope to be able to tell more about in the near future so that's put building my platformer game on the backburner a bit.

Not to worry however, this side project is using GLFW and using my tutorial as a base and I'm planning to feed back lessons learned into this series.

More to come soon!

No comments:

Post a Comment