npm package discovery and stats viewer.

Discover Tips

  • General search

    [free text search, go nuts!]

  • Package details

    pkg:[package-name]

  • User packages

    @[username]

Sponsor

Optimize Toolset

I’ve always been into building performant and accessible sites, but lately I’ve been taking it extremely seriously. So much so that I’ve been building a tool to help me optimize and monitor the sites that I build to make sure that I’m making an attempt to offer the best experience to those who visit them. If you’re into performant, accessible and SEO friendly sites, you might like it too! You can check it out at Optimize Toolset.

About

Hi, 👋, I’m Ryan Hefner  and I built this site for me, and you! The goal of this site was to provide an easy way for me to check the stats on my npm packages, both for prioritizing issues and updates, and to give me a little kick in the pants to keep up on stuff.

As I was building it, I realized that I was actually using the tool to build the tool, and figured I might as well put this out there and hopefully others will find it to be a fast and useful way to search and browse npm packages as I have.

If you’re interested in other things I’m working on, follow me on Twitter or check out the open source projects I’ve been publishing on GitHub.

I am also working on a Twitter bot for this site to tweet the most popular, newest, random packages from npm. Please follow that account now and it will start sending out packages soon–ish.

Open Software & Tools

This site wouldn’t be possible without the immense generosity and tireless efforts from the people who make contributions to the world and share their work via open source initiatives. Thank you 🙏

© 2024 – Pkg Stats / Ryan Hefner

@bigfootds/react-gamepad-utils

v0.1.3

Published

A base piece of a larger, ElectronJS + ReactJS game "engine" tech stack. Built for BigfootDS' specific needs, intended to be used by other packages or components.

Downloads

17

Readme

@bigfootds/react-gamepad-utils

A base piece of a larger, ElectronJS + ReactJS game "engine" tech stack. Built for BigfootDS' specific needs, intended to be used by other packages or components.

Hey!

This package does not currently do much, and should not be considered "stable". I'm literally only making it public right now because I want to make sure I can compile and publish to NPM properly. Hang tight for stability!

Installation

Install this package from NPM with this command:

npm i @bigfootds/react-gamepad-utils

Package Goals

This package provides a configurable input polling loop, and some helper methods to serialize gamepad input data.

It is a base package - other packages should be made to use its functionality in more-complex or more-thorough ways.

Keep in mind that this area of browser-based JavaScript functionality is somehow both barely-touched and one spec decision away from major breaking issues. Keep an eye on the MDN web docs for Gamepad functionality and updates, and raise issues here if I've missed something!

Package Anti-Goals

This package will not cover the features listed here, as those will instead be covered by other packages that depend on this one instead.

  • UI navigation trees.
    • That system depends on the outputs of this one!
    • If you've used Unity, it's the UI navigation stuff that you can configure that determines which UI element is highlighted/focused when you press a directional/arrow/movement input. The yellow connectors in the documentation here, essentially.
  • Gamepad controller maps.
    • That system depends on the outputs of this one!
    • Realistically, controller maps should be a design problem that you, the game developer, solves. How are they persisted? What is default? What controllers do you support? What UI do you use to remap? What UX should be in place to remap? Games should strive to have remappable controller layouts as per industry standards, but figuring out answers to those questions specifically for your game is up to you!
  • Gamepad-controlled DOM elements.
    • This is kinda in the same boat as controller mapping and UI navigation trees - depends on output of this, and additionally is going to be very specific to your game.

Package Exports

  • Contexts
    • GamepadContext: Context reference for the rest of the things in this library. Its values are determined by the logic in the GamepadBaseProvider - it should be an object containing properties and functions.
  • Components
    • GamepadBaseProvider: Initializes and provide the GamepadContext, as well as various functions and variables to configure the input polling loop.
  • Hooks
    • useGamepadContext: Just returns the GamepadContext as-is.
  • Utilities
    • Gamepad Data helper functions:
      • getGamepadsData(): An error-safe function that attempts to retrieve gamepad data from the browser's navigator system. Will catch errors and return an empty array if no data can be retrieved, logging a warning when that happens.
      • serializeGamepads(): Returns a large object of data, which is all essentially just a processed and serializable snapshot of the unserializable snapshot returned by getGamepadsData().

Considerations

  • "Where's the backwards compatible XYZ? Where's the conditional check for the gamepad system and webkit fallback?"
    • This library is built for BigfootDS' needs, and we are using ElectronJS. ElectronJS bundles in an extremely modern Chromium. If you're not using a recent version of ElectronJS, you should update!

TODO

  • Actual control over the polling loop
    • need better logic over requesting and cancelling animation frames, the latestFrameId doesn't give us much functionality
  • Confirm the other gamepad serialized data - buttons work, but what about axes? Can we name buttons?

Usage

Given that this repo is in very early development, don't expect this to be accurate for long... but... here:


import { useEffect } from 'react';
import './App.css'
import { useGamepadContext } from '@bigfootds/react-gamepad-utils';

function App() {
  let { gamepads, setShouldBePolling, deadzoneOffsetRaws, setAxesDeadzonesForGamepad } = useGamepadContext();

  useEffect(() => {
    setShouldBePolling(true)
  }, [setShouldBePolling]);

  useEffect(() => {
    // console.log("Gamepad data updated.");
  }, [gamepads, deadzoneOffsetRaws]);

  if (gamepads.some(entry => entry)) {
    return <div id='gamepadDemoZone'>
      <h1>Found {gamepads.reduce((total, gamepad) => {
        return gamepad ? total + 1 : total;
      }, 0)} devices.</h1>
      {gamepads.map((data, index) => {
        return <div key={data.id + "-" + index + "-" + data.index}>
          <h2>{data.id}</h2>
          <h3>Gamepad {index}</h3>
          <button onClick={() => setAxesDeadzonesForGamepad(index)}>
            Update Deadzones (Do not touch the joysticks when clicking this button)
          </button>
          <div className='inputsDisplayZone'>
            <ul>
              {data.buttons.map((button, buttonIndex) =>
                <div key={"button-" + buttonIndex}>
                  <li>
                    Button {buttonIndex}
                    <ul>
                      <li>Button {buttonIndex}: {button.pressed ? "Pressed" : "Not Pressed"}</li>
                      <li>Button {buttonIndex}: {button.touched ? "Touched" : "Not Touched"}</li>
                      <li>Button {buttonIndex}: {button.value}</li>
                    </ul>
                  </li>

                </div>
              )}
            </ul>
            <ul>
              {data.axes.map((axis, axisIndex) =>
                <div key={"axis-"+ axisIndex}>
                  <li>
                    Axis {axisIndex}
                    <ul>
                      <li>Raw: {axis.raw}</li>
                      <li>Deadzone Offset: {axis.deadzoneOffset}</li>
                      <li>Value: {axis.value}</li>
                    </ul>
                    
                  </li>

                </div>
              )}
            </ul>
          </div>

        </div>
      })}
    </div>
  } else {
    return <>
      <h1>No devices available.</h1>
    </>
  }
}

export default App

Add this CSS to your default Vite ReactJS app to see a demo like the screenshot below:

#gamepadDemoZone {
  text-align: left;
  
  .inputsDisplayZone {
    display: flex;
  }
}

Per Chromium restrictions, gamepads do not get detected until after they have been used. You'll wanna prompt your users to "press a button to play" or some similar message.

From the sample code above, this image appears. No devices are detected until they are used.

Each device can be accessed and its properties can be read.

A sample of what we can display or detect once a controller has been detected.

To set a gamepad axis' deadzone, just call the setAxesDeadzonesForGamepad() function and pass in the gamepad's index. Just keep in mind that deadzones may lead to weird behaviour - and not supporting deadzones will definitely lead to weirder behaviour. You should provide a way for users to wipe any stored deadzone data, a way to add a "minimum required movement" property (which we're thinking of adding, who knows when?) to help offset drifty joysticks, and a way to call that automated setAxesDeadzonesForGamepad() function too. Controller input can be a lot of work!

An example of axis deadzones and their impact on an axis value.

Device Confirmation List

This is a list of physical game controllers that have been confirmed to work with this package.

  • Xbox Elite Wireless Controller Series 2