I have the game running on the Xcode simulator and also my iPhone. When I hit search they are both put into separate games, instead of one player joining the existing one. It is a turn based game, a player can take their first turn while Game Center is automatching. I am using two separate Game Center accounts with the Sandbox setting on. Any idea what I may be doing wrong? To those who have developed a Game Center supported game, how did you test it? Thanks for the help!
Some code:
- (void)findMatchWithMinPlayers:(int)minPlayers maxPlayers:(int)maxPlayers // view controller calls to find match - nothing if GC unavaiable
viewController:(UIViewController *)viewController {
if (!_enableGameCenter) return;
_matchStarted = NO; // Match not started yet
self.currentMatch = nil;
[viewController dismissViewControllerAnimated:NO completion:nil];
GKMatchRequest *request = [[GKMatchRequest alloc] init]; // Set number of players
request.minPlayers = minPlayers;
request.maxPlayers = maxPlayers;
GKTurnBasedMatchmakerViewController *mmvc = // new instance of the GKMatchmakerViewController with the given request, sets its delegate to the GameKitHelper object, and uses the passed-in view controller to show it on the screen. Shows the user to search for a random player and start a game.
[[GKTurnBasedMatchmakerViewController alloc] initWithMatchRequest:request];
mmvc.turnBasedMatchmakerDelegate = self;
[viewController presentViewController:mmvc animated:YES completion:nil];
}
#pragma mark GKTurnBasedMatchmakerViewControllerDelegate
// A peer-to-peer match has been found, the game should start
- (void)turnBasedMatchmakerViewController:(GKTurnBasedMatchmakerViewController *)viewController didFindMatch:(GKTurnBasedMatch *)match {
[viewController dismissViewControllerAnimated:YES completion:nil];
self.currentMatch = match;
GKTurnBasedParticipant *firstParticipant = [match.participants objectAtIndex:0];
if (firstParticipant.lastTurnDate == NULL) {
// It's a new game!
[delegate enterNewGame:match];
} else {
if ([match.currentParticipant.player isEqual:[GKLocalPlayer localPlayer].playerID]) {
// It's your turn!
[delegate takeTurn:match];
} else {
// It's not your turn, just display the game state.
[delegate layoutMatch:match];
}
}
}
Are you deleting old matches with a partially filled participant list generated during testing prior to starting/joining matches for a new test? You can manually remove old matches from the game center app or use loadMatchesWithCompletionHandler which "loads the turn-based matches involving the local player and creates a match object for each match" at the appropriate time in your code to identify matches for deletion. Refer to Apple's documentation to identify the steps to properly leave, end, and remove a match.
Related
I am in the process of making an iOS app (Objective-C), I am nearing the end of my production of the application itself and now I am trying to implements Interstitial Ads. I was before seeing the Blue You are connected to iAd banners before on my app but I do not seem to see them now. My test device (iPhone 5S, iOS 9.2.1) is connected to the internet, so that is not the issue and the account I am using is a free developer account, not a paid one.
Below is code from one of my view controllers (the code is identical on all three of them with regard to iAd), I got the code from Apple's test project and modified it slightly.
- (void)viewWillAppear:(BOOL)animated {
[super viewWillAppear:animated];
[self cycleInterstitial];
// Other non-relavent stuff
}
- (void)like: (UIButton*)button {
// Other non-relavent stuff
NSInteger i = arc4random_uniform(3);
if (i == 2) {
[self presentInterlude];
}
}
- (void)cycleInterstitial {
// Clean up the old interstitial...
NSLog(#"Cycling");
self.inter.delegate = nil;
// and create a new interstitial. We set the delegate so that we can be notified of when
self.inter = [[ADInterstitialAd alloc] init];
self.inter.delegate = self;
}
- (void)presentInterlude {
// If the interstitial managed to load, then we'll present it now.
if (self.inter.loaded) {
NSLog(#"Requesting the ad");
[self requestInterstitialAdPresentation];
}
}
- (void)interstitialAdDidUnload:(ADInterstitialAd *)interstitialAd {
[self cycleInterstitial];
}
- (void)interstitialAd:(ADInterstitialAd *)interstitialAd didFailWithError:(NSError *)error {
[self cycleInterstitial];
}
I know it is not my random number as I removed that previously. Any ideas on why my ads are not showing up? Could it be my code, my non-paid developer account, or is it something else?
I am not trying to display an actual ad I am only trying to get that You are connected to iAd popup to display to confirm that my implementation is working, so it should not be the fact that I am using a non-paid developer account.
I just noticed different behaviour in my app after upgrading to iOS9. I have a view that shows the device contacts of the phone.
My code is the following:
if (... == YES)
{
ABRecordSetValue(aContact, kABPersonEmailProperty, email, &anError);
if (anError == NULL)
{
ABUnknownPersonViewController *picker = [[ABUnknownPersonViewController alloc] init];
picker.unknownPersonViewDelegate = self;
picker.displayedPerson = aContact;
picker.allowsAddingToAddressBook = YES;
picker.allowsActions = YES;
picker.alternateName = #"John Appleseed";
picker.title = #"John Appleseed";
picker.message = #"Company, Inc";
[self.navigationController pushViewController:picker animated:YES];
}
Then I use the delegate to make a few decisions
- (BOOL)peoplePickerNavigationController:(ABPeoplePickerNavigationController *)peoplePicker shouldContinueAfterSelectingPerson:(ABRecordRef)person
property:(ABPropertyID)property identifier:(ABMultiValueIdentifier)identifier
{
//make decisions
return YES or NO;
}
The user taps in a phone number.
In IOS8 >> Code reaches shouldContinueAfterSelectingPerson and then the native dialler appears
In IOS9 >> The native dialler appears BEFORE the code reaches shouldContinueAfterSelectingPerson.
Any way to resolve it?
I am facing the same issue. What I have noticed is, if you do certain calculations within the delegate method (it obviously takes some time to do that calculation) and the native dialer gets called.
So, to avoid this problem, I am immediately returning NO from this delegate method and performing my calculations in a different thread. This is of course a workaround, hope the issue is fixed in the next release of iOS.
I tried to add a Game Center leaderboard, first setting it on iTunes Connect but when I have to write the code in Xcode I don't know what I have to do. I want to open the leaderboard with a shake gesture and for that I will use this code in the ViewController.m file:
-(void)motionBegan:(UIEventSubtype)motion withEvent:(UIEvent *)event {
if(event.subtype == UIEventSubtypeMotionShake) {
// Code you want to run when the shake began
}
}
Is that code right?
And please can anyone help me what I have to do for appear the leaderboard when shaking?
Enable GameCenter in the Project's capabilities tab
Create a GKGameCenterViewController instance
Set the appropriate viewState property
If necessary, set the gameCenterDelegate property
Present the VC:
GKGameCenterViewController *vc = [GKGameCenterViewController new];
vc.viewState = GKGameCenterViewControllerStateLeaderboards;
vc.gameCenterDelegate = self;
[self presentViewController:vc animated:YES];
I'm preparing to launch my first app and want to have multiple leaderboards inside my game. Currently in sandbox mode I can track and log scores into Game Center successfully. Game Center saves my scores (only if it is higher) and seems to be fully functional.
I know through Itunes Connect we have the ability to set up multiple leaderboards and it seems pretty straight forward. I still want to be able to test multiple leaderboards before publishing my game though. Is there a way to do this in sandbox mode? Currently it seems like my scores are only automatically logged into a default leaderboard. Below is the relevant code I'm using to save/access scores. Thanks!
ABGameKitHelper.m
#pragma mark - Leaderboard
-(void) reportScore:(long long)aScore forLeaderboard:(NSString*)leaderboardId
{
GKScore *score = [[GKScore alloc] initWithCategory:leaderboardId];
score.value = aScore;
[score reportScoreWithCompletionHandler:^(NSError *error) {
if (!error)
{
if(![self hasConnectivity])
{
[self cacheScore:score];
}
if (ABGAMEKITHELPER_LOGGING) NSLog(#"ABGameKitHelper: Reported score (%lli) to %# successfully.", score.value, leaderboardId);
}
else
{
[self cacheScore:score];
if (ABGAMEKITHELPER_LOGGING) NSLog(#"ABGameKitHelper: ERROR -> Reporting score (%lli) to %# failed, caching...", score.value, leaderboardId);
}
}];
}
-(void) showLeaderboard:(NSString*)leaderboardId
{
GKLeaderboardViewController *viewController = [GKLeaderboardViewController new];
viewController.leaderboardDelegate = self;
if (leaderboardId)
{
viewController.category = leaderboardId;
CCLOG(#"Going to category already created");
}
[[self topViewController] presentViewController:viewController animated:YES completion:nil];
}
MainScene.m
- (void)gameCenter {
[[ABGameKitHelper sharedHelper] reportScore:1400 forLeaderboard:#"Score"];
[[ABGameKitHelper sharedHelper] showLeaderboard:#"Score"];
}
I'm not sure if I understand your question properly, but I'll try to answer! Game Center does support multiple leaderboards:
-If you want to send a score to specific leaderboard, you just have to call the function [[ABGameKitHelper sharedHelper] reportScore:X forLeaderboard:LEADERBOARD_ID];, where X represents the score you'd like to send, and LEADERBOARD_ID is the ID of the leaderboard you want to send the score to, as specified in iTunes Connect.
-When you have multiple leaderboards, if you don't want to show just one leaderboard, but a list of them all, you should use the GKGameCenterViewController class instead. However, be careful; this ViewController has been added in iOS 6 only, so you must check which version the device is running. I am also using the ABGameKitHelper, so I've made a function to show this kind of view. Here it goes :
ABGameKitHelper.m
- (void) showGameCenter{
if (![[ABGameKitHelper sharedHelper] hasConnectivity]) return;
//Check if device runs on iOS 5
if([[[UIDevice currentDevice]systemVersion]intValue]==5)
{
//If so, we must use the GKLeaderboardViewController
GKLeaderboardViewController *leaderboard = [[GKLeaderboardViewController alloc] init];
if (leaderboard != nil)
{
leaderboard.leaderboardDelegate = self;
[[self topViewController] presentViewController:leaderboard animated:YES completion:nil];
}
}else if ([[[UIDevice currentDevice]systemVersion]intValue]>=6)
{
//if it runs on iOS 6 or higher, we use GKGameCenterViewController
GKGameCenterViewController *gameCenterController = [[GKGameCenterViewController alloc] init];
if (gameCenterController != nil)
{
gameCenterController.gameCenterDelegate = self;
gameCenterController.viewState = GKGameCenterViewControllerStateDefault;
[[self topViewController] presentViewController:gameCenterController animated:YES completion:nil];
}
}
}
And don't forget to add :
- (void) gameCenterViewControllerDidFinish:(GKGameCenterViewController *)gameCenterViewController{
[gameCenterViewController dismissViewControllerAnimated:YES completion:nil];
}
Using this function will allow you to show a nice view containing all your leaderboards and achievements.
Hope this helps!
I'm trying to use game center : multi player
Till now, players are Authenticating to Game center, they can send/read scores, and acheivements.
For multiplayer features, I tried both methods :
- using Game center interface to find a match.
- Find a Match Programmatically.
For both ways I have the following issue: the match delegate’s match:player:didChangeState: method is not called.
In apple docs, it's stated that this delegate is called if one player is connected or disconnected.
In my case this delegate is never called. I think that I'm missing a step.
here after the implementation of my delegate (as specified in apple doc).
- (void)match:(GKMatch *)match player:(NSString *)playerID didChangeState:(GKPlayerConnectionState)state
{
switch (state)
{
case GKPlayerStateConnected:
// handle a new player connection.
break;
case GKPlayerStateDisconnected:
// a player just disconnected.
break;
}
if (!self.matchStarted && match.expectedPlayerCount == 0)
{
self.matchStarted = YES;
// handle initial match negotiation.
}
}
and also the code to find a match.
-(void) findProgrammaticMatch
{
GKMatchRequest *request = [[[GKMatchRequest alloc] init] autorelease];
request.minPlayers = 2;
request.maxPlayers = 2;
[[GKMatchmaker sharedMatchmaker] findMatchForRequest:request
withCompletionHandler:^(GKMatch *FoundMatch, NSError *error)
{
if (error)
{
// Process the error.
StatusLabel.text = #"Match Not Found";
}
else if (FoundMatch != nil)
{
MultiPlayerMatch = FoundMatch; // Use a retaining property to retain the match.
StatusLabel.text = #"Match Found";
MultiPlayerMatch.delegate = self; // start!
// Start the match.
// Start the game using the match.
[self StartMatch];
}
}];
}
Thanks for your help.
It was working all along. The only difference is that... when you use invites the event "didChangeState" doesn't get called. You're connected without notice and you can start to receive data. I never tried to send/receive data because I was expecting the event first, but i did send something by mistake one time, and it worked.
- (void)matchmakerViewController:(GKMatchmakerViewController *)viewController didFindMatch:(GKMatch *) match {
//Dismiss window
[self dismissModalViewControllerAnimated:YES];
//Retain match
self.myMatch = match;
//Delegate
myMatch.delegate = self;
//Flag
matchStarted = TRUE;
//Other stuff
}
- (void)match:(GKMatch *)match player:(NSString *)playerID didChangeState:(GKPlayerConnectionState)state {
//This code gets called only on auto-match
}
The above code works as expected.
I think didChangeState: GKPlayerStateConnected may only happen if a player was GKPlayerStateUnknown and then comes back, or if they are added/invited back to a match in progress.