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 13d

How to use the Gamepad mapper with Unity

Posted by 3p0ch - April 18th, 2022


For Unity, I made a public static Gamepad class to handle the gamepad stuff and you can call it from the other scripts in your game. Setting it up is a little complicated but it's described in the comments of the file below, which should be saved as Gamepad.cs. After that, the script that was used in the Unity demo shows how the Gamepad class can be called from other scripts.


Gamepad.cs

using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using System.Runtime.InteropServices;
using Newtonsoft.Json;

// ==================
// === FILE SETUP ===
// ==================
// Save this file as "Gamepad.cs" and include it in your project's Scripts

// Save the following code between /* and */ as "link.xml" in Unity's Assets folder
/*
<linker>
  <assembly fullname="System.Core">
    <type fullname="System.Linq.Expressions.Interpreter.LightLambda" preserve="all" />
  </assembly>
</linker>
*/

// Make an Assets/Plugins subdirectory in Unity
// Go to https://www.nuget.org/packages/Newtonsoft.Json/ and click "Download Package"
// Rename it to newtonsoft.json.##.##.##.zip (instead of .nupkg)
// Put its /lib/netstandard2.0/Newtonsoft.Json.dll file in Assets/Plugins in Unity

// Save the following code between /* and */ as "gampepad.jslib" in Assets/Plugins
/*
mergeInto(LibraryManager.library, {
  ReadGamepadConfig: function() {
    var returnStr = localStorage.getItem("gamepad_configuration");
    var bufferSize = lengthBytesUTF8(returnStr) + 1;
    var buffer = _malloc(bufferSize);
    
    stringToUTF8(returnStr, buffer, bufferSize);
    return buffer;
  },
  
  UpdateGamepad: function() {
    var button_names = ["ButtonUp", "ButtonDown", "ButtonLeft", "ButtonRight",
          "DUp", "DDown", "DLeft", "DRight", "L1", "L2", "R1", "R2", "Start", "Select",
          "LStickPress", "RStickPress"];
    var axis_names = ["LStickUp", "LStickDown", "LStickLeft", "LStickRight",
          "RStickUp", "RStickDown", "RStickLeft", "RStickRight"];
    
    var returnStr = "";
    var gamepadArray = window.navigator.getGamepads();
    
    while ((gamepadArray.length > 0) && (gamepadArray[0] == null)) {
      gamepadArray.shift();
    }
    
    if (gamepadArray.length > 0) {
      returnStr = '{"timestamp":' + gamepadArray[0].timestamp;
      for (var button = 0; button < gamepadArray[0].buttons.length; button++) {
        returnStr += ',"button' + button + '":' + gamepadArray[0].buttons[button].pressed;
      }
      for (var axis = 0; axis < gamepadArray[0].axes.length; axis++) {
        returnStr += ',"axis' + axis + '":' + gamepadArray[0].axes[axis];
      }
      returnStr += '}';
    }
    
    var bufferSize = lengthBytesUTF8(returnStr) + 1;
    var buffer = _malloc(bufferSize);
    
    stringToUTF8(returnStr, buffer, bufferSize);
    return buffer;
  },
  
  OpenGamepadUrl: function(url_ptr) {
    var url = Pointer_stringify(url_ptr);
    window.open(url);
  }
});
*/

// ===============================
// === USING THE GAMEPAD CLASS ===
// ===============================
// This creates a public static class that can be used in any of your other scripts.
// These functions will only be active in the HTML5 build, not when running from the Unity editor,
//   but they will throw Debug.Log notices if you try to check a button or axis name
//   that doesn't exist while you're testing it from the Unity editor.
//
// Gamepad.OpenConfigPage()
// Opens the webpage with the gamepad configuration gizmo
//
// Gamepad.ReadConfig()
// Reads the configuration data from the browser's LocalStorage
// It should be called once, before the gamepad state (which buttons are pressed and
//   where the sticks are) begins to be read
// I recommend having a main menu with a button that can call OpenConfigPage(),
//   and when transitioning from the main menu to gameplay call ReadConfig()
// Alternatively, make the game call ReadConfig() whenever the game regains focus
//   (so it will be read if the player launches the configuration gizmo and
//   then comes back to the game) like so:
//  void OnApplicationFocus() {
//    Gamepad.ReadConfig();
//  }
//
// Gamepad.ReadState()
// Updates the Gamepad class to hold a snapshot of the current state of the gamepad
//   (which buttons are pressed and the values for each axis)
// This should be called once per frame before processing gamepad input
//
// Gamepad.IsPressed(string button)
// Returns true if the button is currently pressed
// See the list of button names below in buttonList
// ButtonUp, ButtonDown, ButtonLeft, and ButtonRight are the cluster on the right of the gamepad
// DUp, DDown, DLeft, and DRight are the D-pad, which is the one part of the gamepad that doesn't work reliably
//
// Gamepad.IsJustPressed(string button)
// Returns true if the button was just pressed on this frame
//
// Gamepad.AxisValue(string axis)
// Returns a float with the value of an axis in the range -1 to 1
// See the list of axis names below in axisList
// LStickLeft should be the negative of LStickRight if the player configured their gamepad correctly
//   so use whichever of Up, Down, Left, Right makes your code easier to understand
//
// Gamepad.buttonList and Gamepad.axisList
// These are arrays of the button and axis names (strings), in case you want to loop through them all
// 
// Gamepad.deadZone
// This is a float; if the absolute value of an axis is less than this, Gamepad.AxisValue() for that axis returns zero
// The default value is set just below here, and it is public so it can be modified by other scripts

public static class Gamepad {
  // Set the sticks' dead zone here
  public static float deadZone = 0.1f;
  
  // Variables to read the gamepad configuration from the player's browser
  private static Dictionary<string, string> config = new Dictionary<string, string>();
  private static Dictionary<string, int> buttonConfig = new Dictionary<string, int>();
  private static Dictionary<string, int> axisConfig = new Dictionary<string, int>();
  private static Dictionary<string, int> axisDirectionConfig = new Dictionary<string, int>();
  
  // Variables to read the current state of the gamepad
  private static Dictionary<string, string> state = new Dictionary<string, string>();
  private static Dictionary<string, bool> previousButtonState = new Dictionary<string, bool>();
  private static Dictionary<string, bool> currentButtonState = new Dictionary<string, bool>();
  private static Dictionary<string, float> currentAxisState = new Dictionary<string, float>();
  
  // Names of buttons and axes
  public static string[] buttonList = {"ButtonUp", "ButtonDown", "ButtonLeft", "ButtonRight",
            "DUp", "DDown", "DLeft", "DRight", "L1", "L2", "R1", "R2", "Start", "Select",
            "LStickPress", "RStickPress"};
  public static string[] axisList = {"LStickUp", "LStickDown", "LStickLeft", "LStickRight",
            "RStickUp", "RStickDown", "RStickLeft", "RStickRight"};
  
  // Conditionally compile this code only if running a WebGL build
  // Otherwise if it's running in the Unity editor, just use functions that do nothing
  //   unless an invalid button or axis name is used, in which case let the dev know with Debug.Log
  #if (UNITY_WEBGL && !UNITY_EDITOR)
    // Import the JavaScript functions from gamepad.jslib
    [DllImport("__Internal")]
    private static extern string ReadGamepadConfig();
    [DllImport("__Internal")]
    private static extern string UpdateGamepad();
    [DllImport("__Internal")]
    private static extern string OpenGamepadUrl(string url);
    
    // Open the webpage with the gamepad configuration gizmo
    // If you have your game hosted on a site other than NewGrounds, the hosting site will also
    //   need to have the gizmo page and this url should be changed to the gizmo page for that site
    // (LocalStorage doesn't allow cross-site data access for security reasons)
    public static void OpenConfigPage() {
      OpenGamepadUrl("https://www.newgrounds.com/portal/view/project/1765261");
    }
    
    // Read the gamepad configuration (imported as strings, which can be null instead of "" or "null")
    public static void ReadConfig() {
      config = JsonConvert.DeserializeObject<Dictionary<string, string>>(ReadGamepadConfig());
      
      // Put the buttons' configuration data into buttonConfig
      foreach (string button in buttonList) {
        if (config.ContainsKey(button) && (config[button] != null)) {
          try {
            buttonConfig[button] = System.Int32.Parse(config[button]);
          } catch (System.FormatException) {
          }
        }
      }
      
      // Put the axes' configuration data into axisConfig and axisDirectionConfig
      foreach (string axis in axisList) {
        if (config.ContainsKey(axis) && (config[axis] != null)) {
          try {
            axisConfig[axis] = System.Int32.Parse(config[axis]);
            axisDirectionConfig[axis] = System.Int32.Parse(config[axis + "_direction"]);
          } catch (System.FormatException) {
          }
        }
      }
      
      // Initialize previousButtonState and currentButtonState to false
      foreach (string button in buttonList) {
        previousButtonState[button] = false;
        currentButtonState[button] = false;
      }
      // Initialize all axes to zero
      foreach (string axis in axisList) {
        currentAxisState[axis] = 0.0f;
      }
    }
    
    // Read the current gamepad state
    public static void ReadState() {
      state = JsonConvert.DeserializeObject<Dictionary<string, string>>(UpdateGamepad());
      
      foreach (string button in buttonList) {
        if (buttonConfig.ContainsKey(button)) {
          previousButtonState[button] = currentButtonState[button];
          if (state.ContainsKey("button" + buttonConfig[button])) {
            currentButtonState[button] = (state["button" + buttonConfig[button]] == "true");
          }
        }
      }
      
      foreach (string axis in axisList) {
        if (axisConfig.ContainsKey(axis)) {
          if (state.ContainsKey("axis" + axisConfig[axis])) {
            currentAxisState[axis] = System.Convert.ToSingle(state["axis" + axisConfig[axis]]) * axisDirectionConfig[axis];
          }
        }
      }
    }
    
    // Return true if a button is pressed currently
    public static bool IsPressed(string button) {
      if (System.Array.Exists(buttonList, b => b == button)) {
        if (buttonConfig.ContainsKey(button)) {
          return currentButtonState[button];
        } else {
          return false;
        }
      }
      Debug.Log("Attempted to use IsPressed on button " + button + " but no button with that name exists.");
      return false;
    }
    
    // Return true if a button was just pressed on the current frame
    public static bool IsJustPressed(string button) {
      if (System.Array.Exists(buttonList, b => b == button)) {
        if (buttonConfig.ContainsKey(button)) {
          return (currentButtonState[button] && !previousButtonState[button]);
        } else {
          return false;
        }
      }
      Debug.Log("Attempted to use IsJustPressed on button " + button + " but no button with that name exists.");
      return false;
    }
    
    // Return the value of an axis
    public static float AxisValue(string axis) {
      if (System.Array.Exists(axisList, a => a == axis)) {
        if (axisConfig.ContainsKey(axis)) {
          if (System.Math.Abs(currentAxisState[axis]) > deadZone) {
            return currentAxisState[axis];
          } else {
            return 0.0f;
          }
        } else {
          return 0.0f;
        }
      }
      Debug.Log("Attempted to use AxisValue on axis " + axis + " but no axis with that name exists.");
      return 0.0f;
    }
  #else
    // This is what gets compiled if the game is run in anything other than an HTML5 build
    // These are pretty much empty, and allow the game to run in the Unity editor without the gamepad
    public static void OpenConfigPage() {}
    public static void ReadConfig() {}
    public static void ReadState() {}
    public static bool IsPressed(string button) {
      if (!System.Array.Exists(buttonList, b => b == button)) {
        Debug.Log("Attempted to use IsPressed on button " + button + " but no button with that name exists.");
      }
      return false;
    }
    public static bool IsJustPressed(string button) {
      if (!System.Array.Exists(buttonList, b => b == button)) {
        Debug.Log("Attempted to use IsJustPressed on button " + button + " but no button with that name exists.");
      }
      return false;
    }
    public static float AxisValue(string axis) {
      if (!System.Array.Exists(axisList, a => a == axis)) {
        Debug.Log("Attempted to use AxisValue on axis " + axis + " but no axis with that name exists.");
      }
      return 0.0f;
    }
  #endif
}


And here's what I had as Textbox.cs which shows how to use the Gamepad class

using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using UnityEngine.UI;
using UnityEngine.InputSystem;

// This is an example of a script that uses the Gamepad class
// I recommend having a way from the game's main menu to call Gamepad.OpenConfigPage() to
//   open the webpage with the configuration gizmo
// Then when the player starts the main game, run Gamepad.ReadConfig()
// Or you can do like below and have OnApplicationFocus() calling Gamepad.ReadConfig()
//   so it reads the config if the player launches the configuration page and finishes it
//   and then returns to your game
//
// Each frame, call Gamepad.ReadState() to get the current state of the buttons and axes
//   once before you start checking if buttons are pressed or axes are moved
// This loops through each of the possible buttons and axes
// If you want to check a specific button or axis, do something like
// Gamepad.IsPressed(ButtonRight) -- returns a bool
// Gamepad.AxisValue(LStickRight) -- returns a float from -1 (stick is held left) to 1 (held right)

public class Textbox : MonoBehaviour {
  Text textComponent;
  
  void Start() {
    textComponent = GetComponent<Text>();
    Gamepad.ReadConfig();
  }

  void Update() {
    Gamepad.ReadState();
    textComponent.text = "";
    foreach (string button in Gamepad.buttonList) {
      textComponent.text += "IsPressed(" + button + ") = " + System.Convert.ToString(Gamepad.IsPressed(button)) + "\n";
      textComponent.text += "IsJustPressed(" + button + ") = " + System.Convert.ToString(Gamepad.IsJustPressed(button)) + "\n";
    }
    foreach (string axis in Gamepad.axisList) {
      textComponent.text += "AxisValue(" + axis + ") = " + System.Convert.ToString(Gamepad.AxisValue(axis)) + "\n";
    }
  }
  
  void OnApplicationFocus() {
    Gamepad.ReadConfig();
  }
  
  public void OnButtonPress() {
    Gamepad.OpenConfigPage();
  }
}

Tags:

Comments

Comments ain't a thing here.