Close

Gamepad.IO

A project log for HTML5 Retro Game Engine(s)

A Side project where I try and build various scriptable retro game engines in the browser.

timescaleTimescale 04/14/2020 at 15:280 Comments

For a while now, I wanted to do some experiments with gamepads. These controller devices are ubiquitous and I often considered them an easy and cheap way to interact with, well, just about anything that needs simple input. Surely I can use these devices in my work, but how to implement them?

I have a collection of input devices I play around with, but mostly I tinker with the more unique stuff like the Sony buzz quiz controllers or various Nintendo input devices. The generic gamepad not so much, but I do have a couple of them in my I/O hardware collection.

My classic Gravis gamepad is of no use as it is a 15 pin din device. I am building an arduino based converter for various types of I/O which I should actually be covering on hackaday.io, but perhaps later I'll do that write-up.

What I found in my bin was a logitech dual action USB gamepad. Just the thing! Nice and generic.


I knew the HTML5 gamepad API was experimental, so I wanted an implementation that could roll with the times. This means I want the input of the controller to be freely mapped to any control scheme I have got going on. In order to make this, I chose my ArrType test platform as I had allready implemented a keyboard control scheme to move the little ship around.

This scheme was based on readily available code examples and used an object in Javascript to map the event codes to names. The game can now, in its loop, check which buttons are pressed and call the appropriate function like rotate, move or shoot.

var Key = {
      _pressed: {},

      P1_LEFT:65,
      P1_RIGHT:68,
      P1_UP:87,
      P1_DOWN:83,
      P1_SPACE: 32,

      isDown: function(keyCode) {
        return this._pressed[keyCode];
      },

      onKeydown: function(event) {
        this._pressed[event.keyCode] = true;
      },

      onKeyup: function(event) {
        delete this._pressed[event.keyCode];
      },
 
    };

My idea is to map the buttons of a gamepad to the key names. This would not require any change in the game code that defines the actions and it means that remapping the keyboard controls from say the arrow keys to the A,S,W,D configuration would not affect the gamepad configuration.

var padMapped = new Array();
    padMapped[0] = new Array();
    padMapped[0][14] = "P1_LEFT";
    padMapped[0][15] = "P1_RIGHT";
    padMapped[0][12] = "P1_UP";
    padMapped[0][13] = "P1_DOWN";
    padMapped[0][5] = "P1_SPACE";

This array simply maps the necessary buttons to the names mapped to the keyboard input array. The upside of this method is twofold. Firstly, we do not have to define every button. Buttons pressed but not mapped, will be ignored without errors. The second benefit is that we actually can define keymaps that are outside the scope it can get via the keyboard. This means this setup is equally able to handle separate gamepad events. Multiple gamepads are supported.

The code for the gamepad detection, enumeration and update function, I got from there : https://github.com/luser/gamepadtest

It's a bit old, but it was short, it worked and I could easily fiddle my hooks into it. In any event, If/when I come across better/newer methods to do this, It won't be hard to take the old stuff out and put the new code in without actually having to worry about the underlying control scheme of the game/application itself.

The difference between the keyboard event listener and the gamepad update function is that the first is triggered on a button press and the later is dependent on an function that checks the status of the gamepad X times per second. If there is an event driven gamepad API solution, I'd like to know about it!

So basically all I have to do now is to trigger the keymapped object when a mapped gamepad button is pressed. That turned out to be quite simple.

     if (pressed) {
          padkey = Key[padMapped[j][i]];
          var padEvent = {
              keyCode: padkey
          }
          controllerStatus[j][i] = true;
          Key.onKeydown(padEvent);
          console.log(i)
      } else {
          if (controllerStatus[j][i]){
              controllerStatus[j][i] = false;
            padkey = Key[padMapped[j][i]];
              var padEvent = {
                  keyCode: padkey
              }
                Key.onKeyup(padEvent);
        }
      }


Of course the update function iterates through all the controllers and their buttons (and axis'), but at the core is this. The number of the button is used as the index for the name of the mapped key input. An object is made that resembles the event.keycode object and it is send to the up or down function of that object. As the gamepad API does not track buttonUp events, the state has to be checked with a status array, otherwise the controller would stop the keyboard from working properly.

Well, that is about it for the gamepad implementation. Here is a demo I filmed with a potato!

Discussions