robreuss / VirtualGameController

Software-based game controllers for iOS, tvOS, OS X and watchOS in Swift 4.2.
MIT License
462 stars 44 forks source link

tvOS Remote not recognized #9

Closed jakubknejzlik closed 8 years ago

jakubknejzlik commented 8 years ago

Am I correct, that on tvOS Apple TV Remote is not recognized with VgcController (not returned by VgcController.controllers())? I guess it's because it uses bluetooth, but is there some way to get this controller through VgcController?

robreuss commented 8 years ago

As soon as you call "startAs" on tvOS you should get a VgcControllerDidConnectNotification notification indicating that the Apple TV Remote is connected (unless the Remote is physically out of range). VGC relies on Apple's Game Controller framework and intercepts the GCControllerDidConnectNotification when the remote connects, and issues it's own notification.

If you run the tvOS Central sample app, you should immediately see a view showing for the Remote. I just tested this on my Apple TV and it worked fine.

Note that until you receive the VgcControllerDidConnectNotification notification, the Remote may not be present in VgcController.controllers(). This is true of Apple's Game Controller framework as well.

Note also that I just noticed that the Simulator Remote is showing up in the Central tvOS sample as an "ExtendedGamepad" - it should show up as a MicroGamepad - but this shouldn't make a difference functionally. I'll fix it anyway.

jakubknejzlik commented 8 years ago

You're right about the real device, unfortunately in Simulator I see the ExtendedGamepad, but it's not sending any values from TV Remote Simulator.

screenshot 2015-11-28 07 07 54

robreuss commented 8 years ago

I just checked and the Game Controller framework is reporting the vendorName of the simulator Remote as being "ExtendedGamepad" for some reason, and as well an extendedGamepad profile shows up as non-nil on the simulator Remote (which it should not, for a microGamepad). I've played around with this a bit and cannot get any input events to trigger the handlers that I've set on the GCController microGamepad. I fired up Demobots project and although it runs in the simulator, I'm unable to get it to respond to the simulator Remote.

Do you have any code of your own that uses GCController where you can test if the simulator works as a microGamepad game controller? I can create a fresh project tomorrow and give it try. I know the simulator Remote was broken in earlier betas but I thought it had been fixed.

robreuss commented 8 years ago

I created a quick tvOS project to test and I cannot get the valueChangedHandler for microGamepad to fire in the simulator context.

Create a single window tvOS project and this is the VC code:

import UIKit
import GameController

class ViewController: UIViewController {

    override func viewDidLoad() {
        super.viewDidLoad()

        NSNotificationCenter.defaultCenter().addObserver(self, selector: "controllerDidConnect:", name: "GCControllerDidConnectNotification", object: nil)
        NSNotificationCenter.defaultCenter().addObserver(self, selector: "controllerDidDisconnect:", name: "GCControllerDidDisconnectNotification", object: nil)

    }

    @objc func controllerDidConnect(notification: NSNotification) {

        if notification.object is GCController {

            let controller = notification.object as! GCController

            print("Vendor name is: \(controller.vendorName), Extended gamepad: \(controller.extendedGamepad)")

            controller.microGamepad?.valueChangedHandler = { (gamepad: GCMicroGamepad, element: GCControllerElement) in

                print("Got TV remote event")

            }

        }

    }
}
robreuss commented 8 years ago

I also tried pairing my hardware Apple TV remote with my Mac and although OS-level navigation worked, the remote did not show up to the Game Controller framework.

jakubknejzlik commented 8 years ago

I had to create Remote detection on simulator (for debugging purposes for my colleagues) and ended up using this nasty solution:

   @objc func detectAppleTVRemote() {
        for controller in GCController.controllers() where controller.vendorName == "Remote" {
            self._tvRemoteController = controller
        }
        if (self._tvRemoteController == nil) {
            self.performSelector("detectAppleTVRemote", withObject: nil, afterDelay: 1)
        }
    }

...because on simulator, the connection of tv remote doesn't fire GCControllerDidConnectNotification notifications :-/

Also, instead of:

NSNotificationCenter.defaultCenter().addObserver(self, selector: "controllerDidConnect:", name: "GCControllerDidConnectNotification", object: nil)

...shouldn't it be?

NSNotificationCenter.defaultCenter().addObserver(self, selector: "controllerDidConnect:", name: GCControllerDidConnectNotification, object: nil)    
robreuss commented 8 years ago

Is the simulator Remote working for you, e.g. that is, generating input? If so, maybe I'll go ahead and put in place a similar solution (that inspects the array).

I think the slightly less nasty way you could do it would be to inspect the "vendorName" property for "ExtendedGamepad", which is what the simulator remote returns (for no good reason).

Another direction that you could go, which requires a bit more effort, would be to create a Peripheral that mimics the Remote. I realize that would not be best for accurate testing. I might create something like that at some point.

Yes, I should probably be using the constant GCControllerDidConnectNotification instead of the string "GCControllerDidConnectNotification" - both work. I'll fix it.

robreuss commented 8 years ago

Just thought this through a bit more and realized that for me, the TV simulator is generating the GCControllerDidConnectNotification notification - the sample code I posted above reflects that in it's results. The only problematic things for me were: 1) that the Remote has it's extendedGamepad profile set (and it shouldn't as a microGamepad) and 2) the handlers do not fire.

jakubknejzlik commented 8 years ago

Yes, for me too (with vendorName "ExtendedGamepad"). But after that, the simulator adds another gccontroller, which has microGamepad and every input is working fine. But doesn't post the notification (that's the reason for checking the array)

robreuss commented 8 years ago

I just implemented a version of your code above and confirmed what you're describing - obviously a bug on Apple's end.

I went ahead and enhanced the iOS Peripheral sample app to have a microGamepad mode, screenshot attached. Rather than mimicking the exact layout, I made the best use of space and just represented the active elements (dpad, A and X). Works fine with the simulator (of course). Should be pushing it up tomorrow. Not sure if it matters to your situation.

screen shot 2015-11-28 at 9 55 33 pm

jakubknejzlik commented 8 years ago

Definitely helps, although I have to leave the code to as is, because they need to test in without using any device (just with on screen simulator remote).