GameCenter integration Complications? - iphone

I called the code below to add Game center user banner pop up at the top of the screen, but it says Game is not recognized by Game Center:
I added this to my addDidFinishLaunching:
- (void)applicationDidFinishLaunching:(UIApplication *)application {
[[GKLocalPlayer localPlayer] authenticateWithCompletionHandler:^(NSError *error){
if (error ==nil) {
NSLog(#"Success");
} else {
NSLog(#"Fail");
}
}];
For viewing leader boards, what code do I need to add to properly call it?
-(void)viewscores:(SPEvent*)event{
CODE HERE
}

You could try it like that :
GKLeaderboardViewController *leaderboardVC = [[[GKLeaderboardViewController alloc]init]autorelease];
if (leaderboardVC !=nil) {
leaderboardVC.leaderboardDelegate = self;
[self presentViewController:leaderboardVC];
}
I hope it helps :-)

Related

Sprite Kit Game Center Leaderboards

so I've been reading articles and the Apple developer library for a while but I'm not able to solve this.
I want to have a Game Center Leaderboard for my app, that's accessible through my game menu. However, since I'm using Sprite Kit for creating my game this causing me some problems.
I registered my app in iTunes Connect, enabled Game Center and the Identity information within Xcode matches the ones in iTunes Connect.
I created a button, that should open the Game Center Leaderboard. When it's tapped this is called:
GKLeaderboardViewController *leaderboardController = [[GKLeaderboardViewController alloc] init];
if (leaderboardController != NULL)
{
leaderboardController.timeScope = GKLeaderboardTimeScopeAllTime;
leaderboardController.leaderboardDelegate = self;
GKGameCenterViewController *gameCenterController = [[GKGameCenterViewController alloc] init];
if (gameCenterController != nil)
{
gameCenterController.viewState = GKGameCenterViewControllerStateLeaderboards;
UIViewController *vc = self.view.window.rootViewController;
[vc presentViewController: gameCenterController animated: YES completion:nil];
}
}
Now I get the message, that Game Center is unavailable because no player is signed in.
To fix that I tried using the authentication method from apple:
- (void) authenticateLocalPlayer
{
GKLocalPlayer *localPlayer = [GKLocalPlayer localPlayer];
localPlayer.authenticateHandler = ^(UIViewController *viewController, NSError *error){
if (viewController != nil)
{
//showAuthenticationDialogWhenReasonable: is an example method name.
[self showAuthenticationDialogWhenReasonable: viewController];
}
else if (localPlayer.isAuthenticated)
{
//authenticatedPlayer: is an example method name.
[self authenticatedPlayer: localPlayer];
}
else
{
[self disableGameCenter];
}
}];
}
I'm now having trouble defining these methods being called in the authentication method.
My explicit question: How do I authenticate a local player?

AVFoundation - How to control exposure

AFTER tapping to take picture, I want to lock exposure and turn off torch as soon as exposure is no longer adjusting. So, I added an observer to handle adjustingExposure:
- (IBAction)configureImageCapture:(id)sender
{
[self.session beginConfiguration];
[self.cameraController device:self.inputDevice exposureMode:AVCaptureExposureModeAutoExpose];
[self.cameraController device:self.inputDevice torchMode:AVCaptureTorchModeOn torchLevel:0.8f];
[self.session commitConfiguration];
[(AVCaptureDevice *)self.inputDevice addObserver:self forKeyPath:#"adjustingExposure" options:NSKeyValueObservingOptionNew context:MyAdjustingExposureObservationContext];
}
Here is the observeValueForKeyPath method:
- (void)observeValueForKeyPath:(NSString *)keyPath ofObject:(id)object change:(NSDictionary *)change context:(void *)context
{
if (context == MyAdjustingExposureObservationContext) {
if( [keyPath isEqualToString:#"adjustingExposure"] )
{
BOOL adjustingExposure = [ [change objectForKey:NSKeyValueChangeNewKey] isEqualToNumber:[NSNumber numberWithInt:1] ];
if (!adjustingExposure)
{
[(AVCaptureDevice *)self.cameraController.inputDevice removeObserver:self forKeyPath:#"adjustingExposure"];
if ([self.inputDevice isExposureModeSupported:AVCaptureExposureModeLocked]) {
dispatch_async(dispatch_get_main_queue(),
^{
NSError *error = nil;
if ([self.inputDevice lockForConfiguration:&error]) {
// 5) lock the exposure
[self.cameraController device:self.inputDevice exposureMode:AVCaptureExposureModeLocked];
// 6) turn off the Torch
[self.cameraController device:self.inputDevice torchMode:AVCaptureTorchModeOn torchLevel:0.0001f];
[self.inputDevice unlockForConfiguration];
}
});
}
}
}
} else {
[super observeValueForKeyPath:keyPath ofObject:object change:change context:context];
}
}
#user3115647 posted this information, which is exactly what I am trying to do.
But my picture is taken BEFORE the torch is turned off.
Here is my captureStillImageAsynchronouslyFromConnection:self.captureConnection completionHandler. This block occurs after the image is taken. The observeValueForKeyPath is supposed to occur while the camera is adjusting exposure BEFORE the image is taken. But my torch is not going low BEFORE the image is being taken. Either this is a timing issue or I'm not setting up the camera configuration correctly.
- (void)captureImage
{
// configureImageCapture has already been done
[self.stillImageOutput captureStillImageAsynchronouslyFromConnection:self.captureConnection completionHandler: ^(CMSampleBufferRef imageSampleBuffer, NSError *error)
{
if (imageSampleBuffer != NULL)
{
// Log the image properties
CFDictionaryRef attachmentsRef = CMGetAttachment(imageSampleBuffer, kCGImagePropertyExifDictionary, NULL);
NSDictionary *properties = (__bridge NSDictionary *)(attachmentsRef);
NSLog(#"Image Properties => %#", (properties.count) ? properties : #"none");
I got something similar to happen by using flash instead of the torch. I have an observer for #"videoDevice.flashActive" as well. I did try using exposureModeLocked first, but it didn't work for me either.
Take a photo with flash on
Then instantly turn flash off and take another photo before the exposure has time to adjust
The code below probably doesn't just work on its own, but it's simplified from what I did.
- (void)observeValueForKeyPath:(NSString *)keyPath ofObject:(id)object change:(NSDictionary *)change context:(void*)context
{
if (context == AdjustingExposureContext)
{
self.isAdjustingExposure = [change[NSKeyValueChangeNewKey] boolValue];
}
else if (context == FlashModeChangedContext)
{
self.isFlashActive = [change[NSKeyValueChangeNewKey] boolValue];
if (!self.flashActive)
{
[self captureImage]; // QUICKLY! capture 2nd image without
} // flash before exposure adjusts
}
if (!self.isAdjustingExposure && self.flashActive)
{
[self removeObserver:self forKeyPath:#"videoDevice.adjustingExposure" context:AdjustingExposureContext];
[self captureImage]; // capture 1st image with the flash on
}
}
Now in the callback for captureStillImageAsynchronouslyFromConnection:,
if (self.isFlashActive)
[self.videoDeviceInput.device setFlashMode:NO];
However, if you need to take more than one photo without flash at the lowered exposure, this strategy may not work.
It is almost certainly a timing issue. Call captureStillImageAsynchronouslyFromConnection:completionHandler: inside your if block. Then the capture will always be executed after exposure has been locked.
if ([self.inputDevice isExposureModeSupported:AVCaptureExposureModeLocked]) {
dispatch_async(dispatch_get_main_queue(),
^{
NSError *error = nil;
if ([self.inputDevice lockForConfiguration:&error]) {
//code to lock exposure here
//take photo here
}
});
}

Game Center Matchmaking GKTurnBasedMatch has significant lag (~1 min)

I'm implementing a turn-based game with multiplayer mode through gamecenter. I have 2 devices (1 ipad, 1 iphone) to test in sandbox mode which were working fine but lately it has started to struggle in auto matchmaking process. After I send the first turn from one user, the other device doesn't immediately recognize that game but opens up its own fresh game. Before it was able to immediately spot the game started in the other device and matchmaking was fairly straightforward. And I don't remember changing any parts relevant to matchmaking (NSCoding, GKTurnBasedEventHandler, GKTurnBasedMatchmakerViewControllerDelegate delegate methods etc).
Now I send the first turn from one device and need to wait around 1 min so the other device can successfully connect to that game. After connection occurs endTurnWithMatchData calls work without any problems, it can send and receive data within 1-2 secs. But it won't be a good UX if users start a fresh game and had to wait 1 min so another user can connect to his game. Has anyone been experiencing significant lag in auto matchmaking process? I didn't implement invitations yet, so I cannot check it. The matchdata I archive with NSKeyedArchiver seemed quite big, 3396 bytes, even for a fresh game with almost no data. And here are relevant parts of my code:
GameOptionsViewController:
- (void)turnBasedMatchmakerViewControllerWasCancelled:(GKTurnBasedMatchmakerViewController *)viewController
{
[self dismissViewControllerAnimated:YES completion:nil];
}
- (void)turnBasedMatchmakerViewController:(GKTurnBasedMatchmakerViewController *)viewController didFailWithError:(NSError *)error
{
[self dismissViewControllerAnimated:YES completion:nil];
}
- (void)turnBasedMatchmakerViewController:(GKTurnBasedMatchmakerViewController *)viewController didFindMatch:(GKTurnBasedMatch *)match
{
[self dismissViewControllerAnimated:NO completion:nil];
self.gcMatch = match;
[self performSegueWithIdentifier:#"GameMultiplayer" sender:self];
}
- (void) prepareForSegue:(UIStoryboardSegue *)segue sender:(id)sender
{
if([segue.identifier isEqualToString:#"GameMultiplayer"])
{
GameViewController *GameVC = (GameViewController *)segue.destinationViewController;
[GameVC setGameMode:GAMEMODE_MULTIPLAYER_SAMEDEVICE];
//Multiplayer game it is
if(self.gcMatch != nil)
{
[GameVC setGameMode:GAMEMODE_MULTIPLAYER_GAMECENTER];
GameVC.gcMatchDelegate = self;
GameVC.gcMatch = self.gcMatch;
NSLog(#"Game OVC Segue: Match ID | %#", self.gcMatch.matchID);
}
}
else
{
...
}
}
GameViewController:
//This method is called according to user actions
//It's the only method I use to send data to other participant
-(void) sendCurrentGameDataWithNewTurn:(BOOL) newTurn
{
NSLog(#"Sending game data current participant : %#", gcMatch.currentParticipant.playerID);
//Update match data if it is corrupted anyhow
if (gcMatch.currentParticipant == nil)
{
[GKTurnBasedMatch loadMatchWithID:gcMatch.matchID withCompletionHandler:^(GKTurnBasedMatch *match, NSError *error)
{
if (error != nil)
{
NSLog(#"Error :%#", error);
return ;
}
[self sendCurrentGameDataWithNewTurn:newTurn];
}];
}
else
{
NSData *matchData = [NSKeyedArchiver archivedDataWithRootObject:game];
//Game advances to new player, buttons are disabled
if(newTurn)
{
NSLog(#"SENDING NEW TURN");
NSUInteger currentIndex = [gcMatch.participants
indexOfObject:gcMatch.currentParticipant];
GKTurnBasedParticipant *nextParticipant;
nextParticipant = [gcMatch.participants objectAtIndex:
((currentIndex + 1) % [gcMatch.participants count])];
[gcMatch endTurnWithNextParticipants:[NSArray arrayWithObject:nextParticipant] turnTimeout:GC_TURN_TIMEOUT matchData:matchData completionHandler:^(NSError *error) {
NSLog(#"Sent");
if (error) {
NSLog(#"SNT - %#", error);
}
}];
}
else
{
NSLog(#"ONLY UPDATING DATA");
[gcMatch saveCurrentTurnWithMatchData:matchData completionHandler:^(NSError *error) {
NSLog(#"Sent");
if (error) {
NSLog(#"OUD - %#", error);
}
}];
}
}
}
-(void) updateGameDataWithGCMatch
{
//Update whole game data
self.game = [NSKeyedUnarchiver unarchiveObjectWithData:self.gcMatch.matchData];
//Update game ui
...
}
-(void) handleTurnEventForMatch:(GKTurnBasedMatch *)match didBecomeActive:(BOOL)didBecomeActive
{
//Check if I got data for the currently active match that options vc forwarded me here, if not do some debug print and return
if(![self.gcMatch.matchID isEqual:match.matchID])
{
//For debugging reasons I skip if i get info for any previous match (other player quit etc)
NSLog(#"GCMatch matchID: %# match matchID: %#",self.gcMatch.matchID,match.matchID);
return;
}
NSLog(#"Turn event handle");
self.gcMatch = match;
if([match.currentParticipant.playerID isEqualToString: [GKLocalPlayer localPlayer].playerID ])
{
//Disable field buttons
[self setFieldButtonsEnabled:TRUE];
[self turnChangeAnimationFromLeftToRight:FALSE];
}
[self updateGameDataWithGCMatch];
}
As for your question:
I myself tempered with matchmaking over Game Center quite a bit and also experienced lags quite frequently, which have been proven to not have been caused by my site but by apples game center servers.
As for additional guidance:
As far as I can see your current approach to matchmaking on a device is:
look if there is a match I can connect to --> If YES request gamedata and connect to the match ELSE start your own match and broadcast the matchdata
From my experience it is better practice to start with matchrequestbroadcasts, wait until you find a second player, define the server device (e.g. by lower checksum of game-center names) and then start the game on that device.

GameCenter Operation cancelled if account never used with game center

I am using GameCenter on my app. I have these lines
GKLocalPlayer *localPlayer = [GKLocalPlayer localPlayer];
[localPlayer authenticateWithCompletionHandler:^(NSError *error) {
if (localPlayer.isAuthenticated)
{
}
the problem is that the localPlayer.isAuthenticated flag is always TRUE bur error variable comes with code 2 = "operation was cancelled" (???).
I have sign out from device's game center and from the store but this flag is always true and I do not see the game center sign in that my app should show when it starts. I don't see either the "welcome" banner that always show when a game that uses game center starts.
How do I force a sign out of game center to make the sign in window to show again?
I am compiling for iOS 4.3.
thanks
What I have discovered now is that this happens if you never signed in on device's game center. Once you login there, and say you want to use your username on game center, the app works. The worst part is this: suppose someone downloads the game but does not have the game center set yet. So, the game will never work for them? My game is supposed to work exclusively with game center on. So, for me this is an issue.
For me its working great. Just changed code from
if([GKLocalPlayer localPlayer].authenticated)
To
if([GKLocalPlayer localPlayer].authenticated == NO)
//Other codes
if([GKLocalPlayer localPlayer].authenticated == NO)
{
[[GKLocalPlayer localPlayer] authenticateWithCompletionHandler:^(NSError *error)
{
[self processGameCenterAuth: error];
}];
}
- (void) processGameCenterAuth: (NSError*) error
{
if(error == NULL)
{
[mGameCenterManager reloadHighScoresForCategory: self.currentLeaderBoard];
}
else
{
// NSLog(#"%#\n\n",[NSString stringWithFormat: #"Reason: %#", [error localizedDescription]]);
AppController *app = (AppController*)[UIApplication sharedApplication].delegate;
if(!app.isgameCenterStarted)
{
UIAlertView* alert= [[[UIAlertView alloc] initWithTitle:#"Game Center Unavailable" message: #"Player is not signed in"
delegate: NULL cancelButtonTitle: #"OK" otherButtonTitles: NULL] autorelease];
[alert show];
}
else
{
[[NSNotificationCenter defaultCenter] postNotificationName:#"GameCenterUnAvailable" object:nil];
}
}
}

Login in iPhone App via GameKit

I want to login to my app via GameCenter Login API.
Is it possible ?
Is Apple game Center login API public?
If you're using iOS 6, see the documentation for GKLocalPlayer. You'll see that you assign a block to the 'authenticateHandler' property of localPlayer. When you assign it, if the player isn't already logged into Game Center, one of the arguments to the block (UIViewController *viewController) gets filled in with the address of a view controller that will present the regular Apple Game Center login screen. After you get that address you do presentViewController:viewController and the user sees the normal Apple login screen. When the user finishes interacting with it you get a call back to 'gameCenterViewControllerDidFinish'. The block you provide runs more than once, which makes the process pretty hard to follow, but it works. For what it's worth I'll post below a method I'm using that seems to work. It assumes either iOS5 or iOS6. It isn't be good for anything earlier than 5. OS6 is method that returns YES on iOS6 and NO otherwise. This wasn't written for public consumption so please excuse all the debugging stuff and the unexplained stuff in it.
-(void) authenticateLocalPlayer {
ANNOUNCE
GKLocalPlayer *localPlayer = [GKLocalPlayer localPlayer];
_lastError = nil;
//iOS 6
if ( [self os6] ) {
localPlayer.authenticateHandler = ^(UIViewController *loginVC, NSError *error) {
NSLog(#"in authenticateHandler 1");
[self setLastError:error];
//... resume application responses
[[CCDirector sharedDirector] resume]; //if not paused does nothing
if ( [GKLocalPlayer localPlayer].authenticated) {
NSLog(#"in authenticateHandler 2 - local player is authenticated");
} else if (loginVC) {
NSLog(#"in authenticateHandler 3 - local player is not authenticated, will present VC");
//... pause applications responses
[[CCDirector sharedDirector] pause];
[self presentViewController:loginVC];
} else {
NSLog(#"in authenticateHandler 4 - local player is NOT authenticated, no VC returned");
}
NSLog(#"authenticateHandler error: %#", error.localizedDescription);
};
//iOS 5
} else {
if ( [GKLocalPlayer localPlayer].authenticated == NO ) {
//no completion handler because we're relying on NSNotificationCenter
[[GKLocalPlayer localPlayer] authenticateWithCompletionHandler:nil];
NSLog(#"local player authentication requested");
} else {
NSLog(#"local player was already authenticated");
}
}
}
You can do that surely. there is no gamecenter API for direct use. you can show the gamecenter authentication screen and after authentication, you can proceed.