AppleTV remote as GCController microGamepad

I want to use the AppleTV remote control as a GCController microGamepad as suggested in the

tvOS beta documentation.

In my viewDidLoad method I´m trying to obtain the available controllers, but the remote control

doesn't show up in the GCController.controllers() array:


The code


// Obtain available controllers
  
let controllers = GCController.controllers()


  
if controllers.isEmpty {
      
print("No Controllers, trying to find one...")
      
GCController.startWirelessControllerDiscoveryWithCompletionHandler({
         

          
let controllers = GCController.controllers()
          
if controllers.isEmpty {
              
print("No Controllers found :(")
          
} else {
              
print("Controllers detected: \(controllers)")
          
}
      
})
  
} else {
  
print("Controllers detected: \(controllers)")
  
}
}


never prints: Controllers detected...


Any idea what I'm doing wrong?

Thank you for your help.

You have to use the GCControllerDidConnectNotification notification to discover the remote. The documentation explains that the controllers array should be populated after receiving this notification.

Accepted Answer

Here is my implementation which works fine, though I am assuming the first controller is the 'master controller' for now....


@import UIKit;
@import GameController;
@import AudioToolbox;
@import AVFoundation;
@interface BaseViewController : UIViewController
    /
    /
    /
    @property (strong) NSMutableArray* contentControls;
    /
    /
    /
    @property (strong) GCController* gameController;
    /
    /
    /
    @property (nonatomic, strong) CADisplayLink *updateLoopTimer;
/
/
/
-(void)onBackPressed;
/
/
/
-(void)onButtonPressed;
/
/
/
-(void)createUpdateLoop;
/
/
/
-(void)update:(float)deltaTime;
/
/
/
-(void)unloaded;
/
/
/
-(void)animateIn;
/
/
/
-(void)animateOut;
@end





#import "BaseViewController.h"
@implementation BaseViewController : UIViewController
/
/
/
-(void)viewDidLoad
{
    self.updateLoopTimer = nil;
    self.gameController = nil;
    self.contentControls = [[NSMutableArray alloc] init];
    [self createUpdateLoop];
    [[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(gameControllerDidConnect:) name:GCControllerDidConnectNotification object:nil];
    [[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(gameControllerDidDisconnect:) name:GCControllerDidDisconnectNotification object:nil];
    [GCController startWirelessControllerDiscoveryWithCompletionHandler:nil];
    [super viewDidLoad];
}
-(void)viewWillAppear:(BOOL)animated
{
    if( [self hasControllerConnected] )
    {
        self.gameController = [[GCController controllers] firstObject];
    }

    [super viewWillAppear:animated];
}
/
/
/
-(void)viewWillDisappear:(BOOL)animated
{
    [self.updateLoopTimer invalidate];
    [[NSNotificationCenter defaultCenter] removeObserver:self name:GCControllerDidConnectNotification object:nil];
    [[NSNotificationCenter defaultCenter] removeObserver:self name:GCControllerDidDisconnectNotification object:nil];
}
/
/
/
-(void)dealloc
{
}
#pragma mark -
#pragma mark Update Loops
#pragma mark -
//***********************************************************
/
//***********************************************************
-(void)createUpdateLoop
{
    if (!self.updateLoopTimer)
    {
        self.updateLoopTimer = [CADisplayLink displayLinkWithTarget:self selector:@selector(updateRefreshRate:)];
        [self.updateLoopTimer addToRunLoop:[NSRunLoop currentRunLoop] forMode:NSDefaultRunLoopMode];
    }
}
/
/
/
- (void)updateRefreshRate:(CADisplayLink *)displayLink
{
    CFTimeInterval deltaTime = displayLink.duration * displayLink.frameInterval;
    [self update:(float)deltaTime];
}
/
/
/
-(void)onButtonPressed
{ /base*/ }
/
/
/
-(void)onBackPressed
{ /base*/ }
/
/
/
static bool wasAPressed = false;
static bool wasXPressed = false;
-(void)update:(float)deltaTime
{
    /
    if( self.gameController != nil )
    {
        GCMicroGamepad* microPad = self.gameController.microGamepad;
        if ( microPad != nil )
        {
            bool isAPressed = microPad.buttonA.isPressed;
            bool isXPressed = microPad.buttonX.isPressed;
            if( !wasAPressed && isAPressed )
            { [self onButtonPressed]; }
            if( !wasXPressed && isXPressed )
            { [self onBackPressed]; }
       
            wasAPressed = isAPressed;
            wasXPressed = isXPressed;
        }
    }
}
#pragma mark -
#pragma mark Controller Connected
#pragma mark -
/
/
/
-(BOOL)hasControllerConnected
{ return [[GCController controllers] count] > 0; }
/
/
/
-(void)gameControllerDidDisconnect:(NSNotification *)notification
{
    self.gameController = nil;
    NSLog(@"gameControllerDidDisconnect");
}
/
/
/
-(void)gameControllerDidConnect:(NSNotification *)notification
{
    if( notification.object != nil )
    { self.gameController = notification.object; }

    NSLog(@"gameControllerDidConnect");
}
/
/
/
-(void)viewDidAppear:(BOOL)animated
{
    [GCController startWirelessControllerDiscoveryWithCompletionHandler:nil];
    [self animateIn];
}
/
/
/
-(void)unloaded
{
}
/
/
/
-(void)animateIn
{
    /
}
/
/
/
-(void)animateOut
{
    /
}
@end



And an example subclassed BaseViewController which responds to user input:


/
/
/
-(void)updateCursor:(float)dt
{
    /
    if( self.gameController != nil )
    {
        GCMicroGamepad* microPad = self.gameController.microGamepad;
        if ( microPad != nil )
        {
            GCControllerDirectionPad *dpad= microPad.dpad;
            float offsetX = self.currentImage.size.width/2 * dpad.xAxis.value;
            float offsetY = self.currentImage.size.height/2 * dpad.yAxis.value;
            self.imageCursor.center = CGPointMake( self.imageMain.centerX + offsetX, self.imageMain.centerY - offsetY);
        }
    }
}


#pragma mark -
#pragma mark Input
#pragma mark -
/
/
/
-(void)onBackPressed
{
    NSLog(@"onBackPressed");
}
/
/
/
-(void)onButtonPressed
{
    NSLog(@"onButtonPressed");
}

Wow, thank you very much - I'll try this out soon!

does this work in the simulator? i don't seem to be getting any notifications in the tvOS simulator but i might be doing something else wrong. thanks 🙂

nope, it doesn't work as a GCMicroGamepad in the simulator


(ps: hi!)

Now I have finally managed it to properly connect to the AppleTV remote and read data from the device...


Here is the (Swift) code I have used:


    // Property to hold the microGamePad profile object
    var gamePad: GCMicroGamepad? = nil


    override func viewDidLoad() {
        .
        .
        // Obtain available game controller
        NSNotificationCenter.defaultCenter().addObserver(self,
            selector: Selector("gameControllerDidConnect:"),
            name: GCControllerDidConnectNotification,
            object: nil)
       
        NSNotificationCenter.defaultCenter().addObserver(self,
            selector: Selector("gameControllerDidDisconnect:"),
            name: GCControllerDidDisconnectNotification,
            object: nil)
        .
        .
    }

    // Cleanup…
    deinit {
       
        NSNotificationCenter.defaultCenter().removeObserver(self)
    }

    // Game controller detected…
   func gameControllerDidConnect(notification : NSNotification) {
       
        if let controller = notification.object as? GCController {
           
            if let mGPad = controller.microGamepad {
               
                // Some setup
                gamePad = mGPad
                gamePad!.allowsRotation = true
                gamePad!.reportsAbsoluteDpadValues = true
               
                print("MicroGamePad connected...")
               
                // Add valueChangedHandler for each control element
                gamePad!.dpad.valueChangedHandler = { (dpad: GCControllerDirectionPad, xValue: Float, yValue: Float) -> Void in
                   
                    print("dpad xValue = \(xValue), yValue = \(yValue)")
                    .
                    .
                }
               
                gamePad!.buttonA.valueChangedHandler = { (buttonA: GCControllerButtonInput, value:Float, pressed:Bool) -> Void in
                   
                    print("\(buttonA)")
                    .
                    .                                       
                }
               
                gamePad!.buttonX.valueChangedHandler = { (buttonX: GCControllerButtonInput, value:Float, pressed:Bool) -> Void in
                   
                    print("\(buttonX)")
                    .
                    .                   
                }
            }
        }
    }

    // Game controller disconnected
    func gameControllerDidDisconnect(notification : NSNotification) {
       
        if let controller = notification.object as? GCController {
           
            if  controller.microGamepad != nil {
               
                self.gamePad = nil
                print("MicroGamePad disconnected...")
            }
        }
    }
AppleTV remote as GCController microGamepad
 
 
Q