New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

iOS 7 Game Controller Support #244

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

Comments

Projects
None yet
5 participants
@alexcash

alexcash commented Oct 20, 2013

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

This comment has been minimized.

Show comment
Hide comment
@phoboslab

phoboslab Nov 20, 2013

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? :)

Owner

phoboslab commented Nov 20, 2013

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

This comment has been minimized.

Show comment
Hide comment
@phoboslab

phoboslab Feb 1, 2014

Owner

Closing for lack of interest.

Owner

phoboslab commented Feb 1, 2014

Closing for lack of interest.

@phoboslab phoboslab closed this Feb 1, 2014

@bnolan

This comment has been minimized.

Show comment
Hide comment
@bnolan

bnolan 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.

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

This comment has been minimized.

Show comment
Hide comment
@ericjbasti

ericjbasti Nov 6, 2015

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

ericjbasti commented Nov 6, 2015

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

@phoboslab

This comment has been minimized.

Show comment
Hide comment
@phoboslab

phoboslab Nov 7, 2015

Owner

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

Owner

phoboslab commented Nov 7, 2015

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

@ericjbasti

This comment has been minimized.

Show comment
Hide comment
@ericjbasti

ericjbasti Nov 7, 2015

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

ericjbasti commented Nov 7, 2015

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

@phoboslab

This comment has been minimized.

Show comment
Hide comment
@phoboslab

phoboslab Nov 8, 2015

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 :)

Owner

phoboslab commented Nov 8, 2015

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

This comment has been minimized.

Show comment
Hide comment
@phoboslab

phoboslab Nov 11, 2015

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);
Owner

phoboslab commented Nov 11, 2015

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

This comment has been minimized.

Show comment
Hide comment
@degenet

degenet 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?

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

This comment has been minimized.

Show comment
Hide comment
@phoboslab

phoboslab Nov 14, 2015

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];
    }
}
Owner

phoboslab commented Nov 14, 2015

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

This comment has been minimized.

Show comment
Hide comment
@phoboslab

phoboslab Nov 14, 2015

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!

Owner

phoboslab commented Nov 14, 2015

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

This comment has been minimized.

Show comment
Hide comment
@degenet

degenet Nov 14, 2015

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

degenet commented Nov 14, 2015

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

finscn referenced this issue in finscn/Ejecta Dec 30, 2015

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