Gamepad Controller

Hey yall,

I looking for more documentation or examples regarding the classes:

{ 
  GamepadManager, Gamepad, ,Xbox360Pad, 
  DualShockPad, GenericPad, PoseEnabledController, ArcRotateCamera, 
  ArcRotateCameraGamepadInput, ArcRotateCameraInputsManager,
} 

But I’m not really finding much other than these two pages

I want to have my character and it’s ArcRotateCamera be controlled by a gamepad class. I can set this up using my own event handlers and what not, but it seems like there is a way to do this through babylon.js - but I’m not sure how.

Adding our input Guru @PolygonalSun and @PirateJC as it seems like there is room to improve the Doc in this area.

1 Like

Is this kinda what you looking for?

const gamepadManager = new BABYLON.GamepadManager();
    gamepadManager.onGamepadConnectedObservable.add((gamepad, state) => {
        gamepad.onleftstickchanged((values) => {
            console.log(values.x+" "+values.y)
        });

        camera.inputs.add(new BABYLON.ArcRotateCameraGamepadInput());
        camera.inputs.attached.gamepad.gamepadAngularSensibility = 250;
        camera.inputs.addGamepad();
    });
1 Like

Yeah more or less-

This example ^ has the behavior I’m looking for.

I’ve implemented something seems to work for now, but it feels hacky:

/* ControllerPlugin.ts */
export type XBOX_BUTTON = 0 | 1 | 2 | 3 | 10
const A: XBOX_BUTTON = 0
const B: XBOX_BUTTON = 1
const X: XBOX_BUTTON = 2
const Y: XBOX_BUTTON = 3
const DASH: XBOX_BUTTON = 10
export const XBOX_BUTTONS = { A, B, X, Y, DASH }

export const leftStick = { x: 0, y: 0 }
export const rightStick = { x: 0, y: 0 }

function setupXboxController(gamepad: Gamepad) {
    if (!(gamepad instanceof Xbox360Pad)) return
    gamepad.onButtonDownObservable.add((button, _state)=>{
      console.log('xbox controller button pressed: ', button)
      switch(button) {
        case XBOX_BUTTONS.A:
          this._playerInput._jumpCallback()
          break
        case XBOX_BUTTONS.B:
          break
        case XBOX_BUTTONS.X:
          this._playerInput._onRightClickCallback()
          break
        case XBOX_BUTTONS.Y:
          this._playerInput._inventoryToggleCallback()
          break
        case XBOX_BUTTONS.DASH:
          console.log('dashing!')
          this._playerInput.dashing = true
          break
        default:
          console.warn('pressed unknown button: ', button)
          break
      }

    })
    gamepad.onButtonUpObservable.add((button, _state) => {
      console.log('xbox controller button released: ', button)
      switch(button) {
        case XBOX_BUTTONS.DASH:
          console.log('stop dashing!')
          this._playerInput.dashing = false
          break
        default:
          console.warn('released unknown button: ', button)
          break
      }
    })

    /* Stick events */
    gamepad.onleftstickchanged((values) => {
      /*                                  Account for dead-zone                 */
      leftStick.x = Math.abs(values.x) > 0.05 ? values.x : 0
      leftStick.y = Math.abs(values.y) > 0.05 ? values.y : 0
    })
    gamepad.onrightstickchanged((values) => {
      /*                                  Account for dead-zone                 */
      rightStick.x = Math.abs(values.x) > 0.05 ? values.x : 0
      rightStick.y = Math.abs(values.y) > 0.05 ? values.y : 0
    })
  }

And then in my player model file:

/* Player.ts */
const rightAxis = new Vector3(1, 0, 0)

function updateFromGamepadControls(): void {
    const cameraForwardRayPosition = this.camera.getForwardRay().direction
    const cameraRight = this.camera.getDirection(rightAxis).scaleInPlace(leftStick.x)

    this._moveDirection = cameraForwardRayPosition.scale(
      -leftStick.y
    ).addInPlace(cameraRight)

    this._moveDirection.y = 0

    if (playerInput.dashing) {
      this.speed = this.runSpeed
    } else {
      this.speed = this.walkSpeed 
    }
}

If I don’t do this in this way, and instead attach the gamepad input directly to the camera via camera.inputs.addGamepad() then the left stick will control the zoom radius of the ArcRotateCamera, which I don’t want.

To change leftstick-behaviour you can customize camera inputs, see docs and babylonjs code:

Regarding non-smooth feeling, maybe a slowed stabilistation similiar to this can help:

2 Likes

Hi @arcman7 just checking in if you need any more help?

1 Like

Not as of yet, but thank you for checking in! I have an initial implementation working atm based on what’s been posted so far.

Works for me - until I switch the camera. After that, gamepad doesn’t work with ArcRotateCamera any longer, no matter what.
I’m switching between 1st and 3rd person camera, Universal/ArcRotate, like scene.activeCamera = camera, camera.attachControl(). Everything works fine, except the gamepad input of ArcRotateCamera never takes.
Seems that one special case it works is when gamepad is attached while ArcRotateCamera is active camera.
Looks like bug in attachControl(). Or, is there some additional bookkeeping needed?

I think I noticed that as well a while back, but I haven’t been using the controller that I built lately. I may have ended up just keeping a separate camera on hand for each control type in use.

It’s a bug alright.
I’ve compared attachControl() methods of FreeCameraGamepadInput and ArcRotateCameraGamepadInput, and this is this is missing for arc rotate camera:

        // if no xbox controller was found, but there are gamepad controllers, take the first one
        if (!this.gamepad && manager.gamepads.length) {
            this.gamepad = manager.gamepads[0];
        }

Filed the bug here: ArcRotateCamera gamepad input other than XBOX
Works for me with this workaround:

    const gamepadManager = this.scene.gamepadManager;
    this.gamepadInput = new BABYLON.ArcRotateCameraGamepadInput();
    const oldAttach = this.gamepadInput.attachControl;
    this.gamepadInput.attachControl = () => {
      oldAttach;
      if (!this.gamepadInput.gamepad && gamepadManager.gamepads.length ) {
        this.gamepadInput.gamepad = gamepadManager.gamepads[0];
      }
    }

Regarding to the original code, note that we don’t need to create new GamepadManager, Scene already contains one.

will be fixed by Fix arcrotate gamepad input by sebavan · Pull Request #14665 · BabylonJS/Babylon.js · GitHub

2 Likes

Thanks for the quick fix!

1 Like