00:00
00:00
3p0ch
If you like hard games try my Daxolissian System series

plasmid @3p0ch

Cat

Scientist

Read the manual & try stuff

So Cal

Joined on 2/13/10

Level:
13
Exp Points:
1,650 / 1,880
Exp Rank:
38,330
Vote Power:
5.48 votes
Audio Scouts
1
Rank:
Portal Security
Global Rank:
23,500
Blams:
50
Saves:
375
B/P Bonus:
8%
Whistle:
Normal
Medals:
4,624
Supporter:
3y 3m 14d

Tutorial on using the Gamepad mapper with JavaScript

Posted by 3p0ch - March 5th, 2022


Here's the source code for the tutorial on using the Gamepad mapper in a pure JavaScript game. Some general-use functions that you can copy/paste into your game are included in the beginning, the rest shows how to implement them in practice.

<!DOCTYPE html>
<html>
  <body bgcolor="white">
    <button id="button"></button>
    <p id="para"></p>
    <script>
      
      // **** Here are some generally useful functions ****
      
      // This function attempts to read the gamepad configuration from localStorage
      // It will either return an object with the configuration data,
      //   or null if no gamepad configuration has been saved yet.
      // In your game, you can test whether or not it's null and give the player a
      //   visual cue of whether a gamepad config has been successfully loaded
      //   (like show "Configue gamepad" if not, or "Re-configure gamepad" if so)
      function read_gamepad_config() {
        const gamepad_key = "gamepad_configuration";
        
        return JSON.parse(localStorage.getItem(gamepad_key));
      }
      
      // Open the page on NewGrounds to configure the gamepad
      // The page to configure the gamepad will automatically close itself when it's done
      // So you can listen for when your game's window is focused again and then
      //   attempt to read the gamepad configuration that was just set
      function run_config() {
        const gamepad_mapper_url = "https://www.newgrounds.com/portal/view/project/1765261";
        
        window.open(gamepad_mapper_url);
        window.addEventListener("focus", function(){
          read_gamepad_config();
        }, {"once": true});
      }
      
      // Return true if a gamepad button is pressed
      // If you make your gamepad object and your gamepad configuration global
      //   then you could set up your function to just take button_name
      function is_button_pressed(button_name, gamepad, config) {
        // Make sure button_name exists in config (check for typos of button_name)
        if (button_name in config) {
          if (config[button_name] !== null) {
            return gamepad.buttons[config[button_name]].pressed;
          }
        } else {
          console.log ("Attempted to read button named " + button_name +
              " but that button name is not configured");
        }
        return false;
      }
      
      // Return the value of an axis
      // If you make your gamepad object and your gamepad configuration global
      //   then you could set up your function to just take axis_name
      // If you want to set dead-zones to zero within in this function, feel free
      function axis_value(axis_name, gamepad, config) {
        // Make sure axis_name exists in config (check for typos of button_name)
        if (axis_name in config) {
          if (config[axis_name] !== null) {
            return gamepad.axes[config[axis_name]] * config[axis_name + "_direction"];
          }
        } else {
          console.log ("Attempted to read axis named " + axis_name +
              " but that axis name is not configured");
        }
      }
      
      
      // **** End of generally useful functions ****
      // The rest is demonstration of using them in practice
      
      let html_button = document.getElementById("button");
      let html_para = document.getElementById("para");
      let gamepad_index;
      let gamepad_config;
      let gamepad_object;
      
      let button_names = ["ButtonUp", "ButtonDown", "ButtonLeft", "ButtonRight",
            "DUp", "DDown", "DLeft", "DRight", "L1", "L2", "R1", "R2", "Start", "Select",
            "LStickPress", "RStickPress"];
      let axis_names = ["LStickUp", "LStickDown", "LStickLeft", "LStickRight",
            "RStickUp", "RStickDown", "RStickLeft", "RStickRight"];
      
      // Show this until a gamepad is detected
      html_button.innerHTML = "No gamepad detected, try plugging in and interacting with it";
      
      // Listen for gamepad connection
      window.addEventListener("gamepadconnected", function(event) {
        gamepad_index = event.gamepad.index;
        // Read the config data if it's present
        // Show either "Configure" or "Re-configure" depending on if data has been saved
        if (gamepad_config = read_gamepad_config()) {
          html_button.innerHTML = "Re-configure gamepad";
        } else {
          html_button.innerHTML = "Configure gamepad";
        }
        
        // Add an event listener to configure the gamepad if the HTML button is pressed
        html_button.addEventListener("click", run_config);
        // Enter the main loop
        show_gamepad_status();
      });
      
      // Main loop showing the current buttons pressed and axis values
      function show_gamepad_status() {
        // Only do this if the gamepad is configured
        if (gamepad_config) {
          html_para.innerHTML = "";
          // Get the current gamepad state
          gamepad_object = navigator.getGamepads()[gamepad_index];
          // Show axis status
          for (let axis_name of axis_names) {
            html_para.innerHTML += `${axis_name} ${axis_value(axis_name, gamepad_object, gamepad_config)}<br>`;
          }
          // Show button status
          html_para.innerHTML += "<br>Pressed buttons<br>";
          for (let button_name of button_names) {
            if (is_button_pressed(button_name, gamepad_object, gamepad_config)) {
              html_para.innerHTML += `${button_name}<br>`;
            }
          }
        }
        requestAnimationFrame(show_gamepad_status);
      }
    </script>
  </body>
</html>

The one tricky part is that the gamepad mapper gizmo on NewGrounds will save the gamepad configuration data in Local Storage which is only accessible to webpages running on NewGrounds, so if you're running your game locally from file:// to test it then it won't be able to read the configuration that was set on NewGrounds. So you should copy/paste the gamepad_configuration to Local Storage for your game while it's running locally, and then any time you run your game locally the gamepad will be configured. If you're not already familiar with editing Local Storage directly from your browser, here are super detailed instructions for Firefox and Chrome:


After you've used the Gamepad mapper on NewGrounds, go to any NG page with a game actively running (the Gamepad mapper's page would work) and in your browser's Developer Tools under the tab called Application (on Chrome) or Storage (on Firefox) you should have an option of looking in Local Storage and selecting https://uploads.ungrounded.net. Pick that, then find gamepad_configuration (your browser will probably show a Filter box after you click ungrounded.net under Local Storage, where you can type gamepad_configuration to narrow it down). Right-click on the Value and choose Edit, and you can copy it to your clipboard.


Then while you're running your own game locally to test it, you can go to the Developer Tools tab, Application, Local Storage, whatever it has for your game (probably file://), and click on file:// or whatever it shows. If you're using Firefox: click the + button to the right of the Filter box to add a new item in Local Storage and set its Key to gamepad_configuration and paste the Value in. If you're using Chrome: click on the empty row at the end of the list of Key/Values (which might be the very first row if you haven't been using Local Storage locally), right-click to add a new key called gamepad_configuration, and paste the configuration data as its value. Then reload the page, and it should be able to read the gamepad configuration.


Tags:

Comments

For me this solution doesn't work when testing the game locally. On Firefox, I get an "Uncaught TypeError: event.gamepad is undefined" log. I'm not sure what's different about the NG HTML environment that makes it work. I'm also unsure how I would implement this in Phaser 3; Could I use this configuration with Phaser's gamepad API?