Log In  

Hey everybody,

I was sad that gamepads don't work in PICO-8 games exported for the browser. Fortunately, you can control PICO's inputs from JS, so I created a drop-in script that uses the HTML5 Gamepad API to add this :).

Github: https://github.com/krajzeg/pico8gamepad/
Direct download for JS: https://github.com/krajzeg/pico8gamepad/raw/master/pico8gamepad.js

Just drop this script in the same directory as your HTML page, add "<script src="pico8gamepad.js"></script>" somewhere above the 'yourgame.js' line, and done!

EDIT: Now updated with pause support and multiplayer support as per ultrabrite's edits in this thread :)

P#27181 2016-08-21 19:57 ( Edited 2017-01-06 21:17)

Thanks for this. I just added your script to my exported game and uploaded it to itch.io - works great. Hopefully this can be added to the exporter to save the trouble. Great work.

P#27224 2016-08-22 23:12 ( Edited 2016-08-23 03:12)

Thanks so much!
Now I'm trying to figure out how to do all the other functions like...
Reset
Pause
Fullscreen and
Sound mute
with the controller instead of pressing the button.

P#32683 2016-11-21 21:02 ( Edited 2016-11-22 02:02)

pause button is m.i.a:

bitmask |= (btn(gp,8) || btn(gp,9)) ? 64 : 0;

I think 2 players support would be great!

P#34906 2017-01-04 09:04 ( Edited 2017-01-04 14:04)
:: Scathe

I second 2 player support!

P#34910 2017-01-04 11:02 ( Edited 2017-01-04 16:02)

silly me, I already had multiplayer support done:

// Copyright (c) 2016 Jakub Wasilewski
// aka krajzeg https://www.lexaloffle.com/bbs/?tid=4100
// Github: https://github.com/krajzeg/pico8gamepad/
// multiplayer & additional buttons mod by ultrabrite

// Array through which we'll communicate with PICO-8.
var pico8_buttons = [0];
// Start polling gamepads.
requestAnimationFrame(updateGamepads);

// handle gamepad redundancy
// useful if you have some unpractical gear
// (driving wheel or something) that sits at slot 0.
var actual_players=2 // 3rd controller will act as 1st

// Workhorse function, updates pico8_buttons once per frame.
function updateGamepads() {

  var gamepads = navigator.getGamepads ? navigator.getGamepads() : [];
  // Gather input from all known gamepads.
  var nbg = (gamepads.length>8) ? 8 : gamepads.length

  for (var i = 0; i < nbg; i++) pico8_buttons[i]=0

  for (var i = 0; i < nbg; i++) {
    var gp = gamepads[i];
    if (!gp || !gp.connected) continue;

    var bitmask = 0;
    // directions (from axes or d-pad "buttons")
    bitmask |= (axis(gp,0) < -0.5 || btn(gp,14)) ? 1 : 0;
    bitmask |= (axis(gp,0) >  0.5 || btn(gp,15)) ? 2 : 0;
    bitmask |= (axis(gp,1) < -0.5 || btn(gp,12)) ? 4 : 0;
    bitmask |= (axis(gp,1) >  0.5 || btn(gp,13)) ? 8 : 0;
    // buttons (mapped twice for user convenience)
    bitmask |= (btn(gp,0) || btn(gp,2)) ? 16 : 0;
    bitmask |= (btn(gp,1) || btn(gp,3)) ? 32 : 0;
    bitmask |= (btn(gp,8) || btn(gp,9)) ? 64 : 0;
    // update actual array
    pico8_buttons[i%actual_players] |= bitmask
  }

  // restart next frame.
    requestAnimationFrame(updateGamepads);
}

// Helpers for accessing gamepad
function axis(gp,n) { return gp.axes[n] || 0.0; }
function btn(gp,b) { return gp.buttons[b] ? gp.buttons[b].pressed : false; }


that's where I pulled the menu line from. facepalm

edit: that's for 2 players, if you need 4, set 'actual_players' to 4...

P#34913 2017-01-04 11:54 ( Edited 2017-01-05 13:57)
:: Scathe

Thanks Ultrabrite!

P#34914 2017-01-04 12:04 ( Edited 2017-01-04 17:04)

new (last) version!
added shoulders and triggers and right stick for further convenience
(LS/RS -> o/x, LT/RT -> x/o, right stick -> directions)
...and stick buttons for completeness (never ever used them)

// Copyright (c) 2016 Jakub Wasilewski
// aka krajzeg https://www.lexaloffle.com/bbs/?tid=4100
// Github: https://github.com/krajzeg/pico8gamepad/
// multiplayer & additional buttons mod by ultrabrite

// Array through which we'll communicate with PICO-8.
var pico8_buttons = [0];
// Start polling gamepads.
requestAnimationFrame(updateGamepads);

// handle gamepad redundancy
// useful if you have some unpractical gear
// (driving wheel or something) that sits at slot 0.
var actual_players=2 // 3rd controller will act as 1st

// Workhorse function, updates pico8_buttons once per frame.
function updateGamepads() {

  var gamepads = navigator.getGamepads ? navigator.getGamepads() : [];
  // Gather input from all known gamepads.
  var nbg = (gamepads.length>8) ? 8 : gamepads.length

  for (var i = 0; i < nbg; i++) pico8_buttons[i]=0

  for (var i = 0; i < nbg; i++) {
    var gp = gamepads[i];
    if (!gp || !gp.connected) continue;

    var bitmask = 0;
    // directions (from axes or d-pad "buttons")
    bitmask |= (axis(gp,0)<-0.5 || axis(gp,2)<-0.5 || btn(gp,14)) ? 1 : 0;
    bitmask |= (axis(gp,0)> 0.5 || axis(gp,2)> 0.5 || btn(gp,15)) ? 2 : 0;
    bitmask |= (axis(gp,1)<-0.5 || axis(gp,3)<-0.5 || btn(gp,12)) ? 4 : 0;
    bitmask |= (axis(gp,1)> 0.5 || axis(gp,3)> 0.5 || btn(gp,13)) ? 8 : 0;
    // buttons (mapped twice for user convenience)
    bitmask |= (btn(gp,0) || btn(gp,2) || btn(gp,4) || btn(gp,7) || btn(gp,10)) ?16:0;
    bitmask |= (btn(gp,1) || btn(gp,3) || btn(gp,5) || btn(gp,6) || btn(gp,11)) ?32:0;
    bitmask |= (btn(gp,8) || btn(gp,9)) ? 64 : 0;
    // update actual array
    pico8_buttons[i%actual_players] |= bitmask
  }

  // restart next frame.
    requestAnimationFrame(updateGamepads);
}

// Helpers for accessing gamepad
function axis(gp,n) { return gp.axes[n] || 0.0; }
function btn(gp,b) { return gp.buttons[b] ? gp.buttons[b].pressed : false; }


actual js file + test cart: https://www.lexaloffle.com/bbs/files/13845/pico8gamepads.zip

(test cart from here: https://www.lexaloffle.com/bbs/?tid=3714)

P#34988 2017-01-05 08:56 ( Edited 2017-01-05 17:16)

ultrabrite:
Cool upgrades! Actually didn't know the pause button was mappable through pico8_buttons (maybe it wasn't when I first wrote it?)

Do you mind if I pull your code into the repository? Unless you use Github and want to make a pull request yourself :)

One thing I'm not sure is the mapping of the buttons to everything - stick buttons seem a stretch and more likely to be used accidentally than intentionally :). The shoulders might work for some games, though.

P#35002 2017-01-05 15:01 ( Edited 2017-01-05 20:01)

sure krajzeg, use anything as you see fit!

game controllers come in all kinds of shape or form, and people too!
with all those buttons mapped:

  • you can play with one hand. if you happen to have another hand, you could be sipping some coffee or making a phone call.
  • kids can just grab the controller and move sticks and smash buttons (well, grown-ups can too)
  • you have some chance of finding a comfy way to play on your oddball non-xinput controller whose button numbers are littered all around (a regular action button can end-up with number 10 or 11, aka xinput stick buttons)
  • maybe you can play marballs2 with your refurbished f16 tomcat stick (jump with one of the hat switches)
  • you get the idea: freedom! :)

edit: I have an older (not worn-out) xbox360 controller that behaves badly. its stick buttons get pressed while simply moving the sticks. I guess I was spoiled by my xbox one controllers, whose stick buttons are quite stiff.
good call on those, krajzeg!

P#35024 2017-01-05 19:24 ( Edited 2017-04-11 08:52)

Updated the repo with support for multiple players and pausing.

Also, added the more exotic mappings behind a configuration parameter - if somebody really wants to map the triggers and stick buttons, they can, but it's not the default in my version. The shoulder buttons and face buttons are mapped by default.

P#35101 2017-01-06 16:16 ( Edited 2017-01-06 21:16)

[Please log in to post a comment]

Follow Lexaloffle:        
Generated 2020-07-10 15:05 | 0.014s | 4194k | Q:29