iOS 7 Game Controller Support #244

Closed
alexcash opened this Issue Oct 20, 2013 · 12 comments

Projects

None yet

5 participants

@alexcash

With iOS 7, Apple has provided an API for Game controller support. It'd be really cool to have it in Ejecta, and I know that would be quite an undertaking. However, I'd like to get the conversation started.

On the Obj-C side, it looks like you can either read the values of each input at any time you'd like, or you can receive callbacks when the state of an input changes. I'm not sure which would be preferable on the JS side, but I do know that games must respond to an pause callback from the controller.

This would require us going back to Xcode 5 and iOS 7 base SDK. It might also require a bit of fancy footwork to detect existence of the API and provide good messaging back to the JS side.

I'd be very interested in helping out with a pull request if we can agree on a clean API model.

https://developer.apple.com/library/ios/documentation/ServicesDiscovery/Conceptual/GameControllerPG/Introduction/Introduction.html#//apple_ref/doc/uid/TP40013276-CH1-SW1

@phoboslab
Owner

HTML5 has a Gamepad API, so if we do this in Ejecta, I think it should use the same API (or something very close). See: http://www.html5rocks.com/en/tutorials/doodles/gamepad/

Somebody wants to sponsor a Logitech Powershell or the Moga Ace? :)

@phoboslab
Owner

Closing for lack of interest.

@phoboslab phoboslab closed this Feb 1, 2014
@bnolan
bnolan commented Jan 15, 2015

I'm not saying this should be reopened, just saying I'm interested in it. And yeah, it should match the html5 gamepad API.

@ericjbasti

Any chance this request gets some new life now that Ejecta and tvOS are a thing?

@phoboslab
Owner

Well, my offer still stands. Sponsor a GamePad (and a new AppleTV maybe? :)) and I'll do it.

@ericjbasti

Let's figure out how to do this. I don't have an issue sponsoring a controller.

@phoboslab
Owner

Just ordered.

ysqgj

Should be here on Tuesday. I'll probably find some time over the next weekend to implement it.

If you want to contribute:

PayPal: dominic.szablewski@gmail.com
Bitcoin: 1m8mc4p98VjhGiLWoY6ri8Rz7no6MkfgD

Thanks :)

@phoboslab phoboslab reopened this Nov 8, 2015
@phoboslab phoboslab closed this in 448720d Nov 11, 2015
@phoboslab
Owner

This supports Gamepads on iOS and tvOS. It closely follows the W3C Gamepad API Spec - including the "standard" mapping.

On tvOS, the TV Remote is mapped to the appropriate buttons, the touchpad is mapped to an analog stick and(!) a D-Pad. The one addition to the W3C spec is the gamepad.exitOnMenuPress flag. See here for the fascinating details about why this flag exists.

Example, as shown in this Demo Video:

// Setup canvas with retina res
var canvas = document.getElementById('canvas');
canvas.width = window.innerWidth * window.devicePixelRatio;
canvas.height = window.innerHeight * window.devicePixelRatio;
canvas.style.width = window.innerWidth + "px";
canvas.style.height = window.innerHeight + "px";

var ctx = canvas.getContext("2d");

// The gamepad we draw below is about 1280px wide. We just
// scale the ctx so that it fills the actual screen width

var scale = canvas.width / 1280;
ctx.scale(scale, scale);
ctx.font = '32px Helvetica';
ctx.fillStyle = '#fff';
ctx.strokeStyle = '#fff';
ctx.lineWidth = 2;

var drawButton = function(button, x, y) {
    ctx.fillRect(x - 24, y + 24 - 48 * button.value, 48, 48 * button.value );

    ctx.strokeStyle = button.pressed ? '#0f0' : '#ccc';
    ctx.strokeRect(x - 24, y - 24, 48, 48);
    ctx.strokeStyle = '#fff';
};

var drawAnalogStick = function( axisX, axisY, x, y ) {
    ctx.beginPath();
    ctx.arc(x, y, 64, 0, 2*Math.PI);
    ctx.stroke();

    ctx.beginPath();
    ctx.arc(x + axisX*48, y + axisY*48, 16, 0, 2*Math.PI);
    ctx.fill();
};

setInterval(function(){
    ctx.clearRect(0, 0, canvas.width/scale, canvas.height/scale);

    // Always use 2nd gamepad for this visualization if it's present. On an
    // Apple TV when the TV Remote and a Game Controller is connected, the Remote
    // will be the first entry in the gamepads array; we want a dedicated
    // Game Controller if there is one.

    var gamepads = navigator.getGamepads();
    var gamepad = gamepads[1] || gamepads[0];

    if( !gamepad ) {
        ctx.fillText('No Gamepads connected', 32, 32);
        return;
    }

    ctx.fillText('Using Gamepad: #' + gamepad.index + ' ('+gamepad.id+')', 32, 32);


    // Button Mappings according to http://www.w3.org/TR/gamepad/#remapping

    drawButton(gamepad.buttons[0], 928, 354); // A
    drawButton(gamepad.buttons[1], 994, 288); // B
    drawButton(gamepad.buttons[2], 862, 288); // X
    drawButton(gamepad.buttons[3], 928, 224); // Y

    drawButton(gamepad.buttons[4], 412, 96); // L1
    drawButton(gamepad.buttons[5], 868, 96); // R1
    drawButton(gamepad.buttons[6], 288, 96); // L2
    drawButton(gamepad.buttons[7], 994, 96); // R2

    drawButton(gamepad.buttons[12], 352, 224); // Up
    drawButton(gamepad.buttons[13], 352, 354); // Down
    drawButton(gamepad.buttons[14], 288, 288); // Left
    drawButton(gamepad.buttons[15], 416, 288); // Right

    drawButton(gamepad.buttons[16], 640, 196); // Menu

    drawAnalogStick(gamepad.axes[0], gamepad.axes[1], 540, 416); // left stick
    drawAnalogStick(gamepad.axes[2], gamepad.axes[3], 736, 416); // right stick


    // You can control whether the MENU button exits your game. Apple will reject your
    // App if it does not. For Gamepads, the B button can also act as a MENU button.
    // However, MENU should only exit the App the in certain cases.

    // The expected behavior is this:
    // > Pauses/resumes gameplay. Returns to previous screen, exits to main game menu,
    // > and/or exits to Apple TV Home screen.
    // ~ https://developer.apple.com/tvos/human-interface-guidelines/remote-and-interaction/

    // In any case, judging from other games, you're probably safe to set 'exitOnMenuPress'
    // to false during gameplay and only set it to true when you're on the Title Screen of
    // your game.

    gamepad.exitOnMenuPress = true;
}, 16);
@degenet
degenet commented Nov 13, 2015

Hi @phoboslab!

Awesome job with the gamepad and tvOS integration. I'm having one issue with the gamepad.exitOnMenuPress and the apple tv remote though. After the following algorithm, setting it to false still exits the game.

To Reproduce:

  1. Run and set gamepad.exitOnMenuPress = false;
  2. Press the Menu button (works as expected :)
  3. Set gamepad.exitOnMenuPress = true and press the Menu button to exit the app.
  4. Reopen the app, and set the gamepad.exitOnMenuPress = false.
  5. Press the Menu button (it exits the app :/ )

I've set breakpoints in XCode to check for the value of exitOnMenuPress inside EJJavaScriptView.m and it is correct every time. For some reason, I believe pressesBegan is still being called in super.

Any ideas?

@phoboslab
Owner

Weird. Maybe pressesEnded also exits the App under certain conditions!? Can you try adding the following method in EJJavaScriptView.m and see if it makes a difference?

-(void)pressesEnded:(NSSet*)presses withEvent:(UIPressesEvent *)event {
    if( exitOnMenuPress && ((UIPress *)presses.anyObject).type == UIPressTypeMenu ) {
        return [super pressesEnded:presses withEvent:event];
    }
}
@phoboslab
Owner

Just did a quick test and this seems to indeed fix the issue. It's weird that it reacts on pressesBegan one time and pressesEnded on another.

Thanks for reporting!

@degenet
degenet commented Nov 14, 2015

Seems like adding the pressesEnded handler does the trick. Thanks!

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment