I'm wondering if there is a simple a way to retrieve the best score and the last achievement that has been unlocked for the local user in an iOS app.
Thanks for your advices !
Yes there is:
1) Best score: Create a GKLeaderBoard instance called myLeaderBoard and set the category you want (as in leader board ID) and set the timeScope property to GKLeaderboardTimeScopeAllTime
Then, GKLeaderBoard has the following:
- (void)loadScoresWithCompletionHandler:(void (^)(NSArray *scores, NSError *error))completionHandler
When the request is done, the completion handler is called and from that moment on, myLeaderBoard instance will have the following property: localPlayerScore which is the best score up to now, store it and use it as you wish :)
2) Most recent Achievement: This works in the same spirit, do the following:
[GKAchievement loadAchievementsWithCompletionHandler:^(NSArray *achievements, NSError *error) {
for(GKAchievement *ach in achievements) {
//sort using the lastReportedDate property of GKAchievement (which is an NSDate).
}
}];
Related
I have used Google play game leaderboard in my iOS app.i am submitting score from my and fetch score from there. i am using following method to submit score
`[score submitScoreWithCompletionHandler: ^(GPGScoreReport *report, NSError *error) {
if (error) {
// Handle the error
} else {
// Analyze the report, if you'd like
}
}];`
and below code to present leaderboard
NSString *targetLeaderboardId = #"my id";
[[GPGLauncherController sharedInstance] presentLeaderboardWithLeaderboardId:targetLeaderboardId];
but fetching score takes to much time.is there any way to reload or refresh leaderboard data.
anyone have any idea!
Unfortunately, Google Play Games Services for iOS has been deprecated, so I would not recommend putting too much time into it. More information: https://android-developers.googleblog.com/2017/04/focusing-our-google-play-games-services.html
I create a turn-based match and proceed to invite a single opponent as follows:
GPGMultiplayerConfig *config = [[GPGMultiplayerConfig alloc] init];
// We will automatically match with one other player
config.invitedPlayerIds = #[self.opponent.googlePlayID];
config.minAutoMatchingPlayers = 0;
config.maxAutoMatchingPlayers = 0;
[GPGTurnBasedMatch
createMatchWithConfig:config
completionHandler:^(GPGTurnBasedMatch *match, NSError *error) {
if (error) {
completion(NO);
return;
}
}];
After this device places the first move and passes the next turn to my opponent device, my opponent device receives the push notification to join the match. I respond by joining. At this point my self.match.userMatchStatus for this invited device is invited:
[self.match joinWithCompletionHandler:^(NSError *error) {
if (error) {
completion(NO);
return;
}
}];
This doesn't give an error. Upon calling self.match.isMyTurn, I get back YES. A call to self.match.userMatchStatus gives the status of invited; not joined. The documentation (which is incredibly poor, by the way) states that this joinWithCompletionHandler: method:
Joins a turn-based match that the player has been invited to.
Even when adding a dispatch time delay in of 3 seconds after this, to give it a chance, I find that it's still set to invited. Calling further methods, such as takeTurnWithNextParticipantId:data:results:completionHandler:, fails with an entirely undocumented error:
Error Domain=com.google.GooglePlayGames Code=3 "The operation couldn’t
be completed. (com.google.GooglePlayGames error 3.)"
Here's a link to Google's documentation:
https://developers.google.com/games/services/ios/api/interface_g_p_g_turn_based_match
I guess you are passing the player id instead of participant id to takeTurnWithNextParticipantId. The error code 3 (and http response code 400) means that something in passed parameters is invalid, in my case it was the participant id which i had set wrong.
My game implements a custom user interface that lists the local player's friends.
I also have a Game Center leaderboard.
When my game lists the players, it also tries to load their scores from the leaderboard, using this code:
GKLeaderboard *request = [[GKLeaderboard alloc] initWithPlayerIDs:myFriends];
request.timeScope = GKLeaderboardTimeScopeAllTime;
request.identifier = #"my_leaderboards";
if (request != nil) {
[request loadScoresWithCompletionHandler: ^(NSArray *scores, NSError *error) {
if (error != nil) {
NSLog(#"Error: %#",error.localizedDescription);
}
if (scores != nil) {
NSLog(#"WORKED: %#",scores);
}
}];
}
And it works just fine.
... except when one of the friends has no score (for instance, they never played the game in the first place). When one of the players in myFriends has no score entry in the leaderboard, the completion handler is never called. There is no error and no score reported, because it never fires in the first place.
I realised this when testing an account that has two friends. One friend has played the game (so they have a score), and the other has not. The completion handler never got called. Then, I unfriended the guy that had no score, and the completion handler worked fine, returning the score of the friend that did have a score.
I somewhat understand this behaviour - after all, I'm asking it to give me a score that does not exist. But is there a workaround? As in, tell it to return a 0 if there is no score?
iOS 7.
It cannot be helped, the completion handler indeed won't be called because there is no score entry in the leaderboards. Although I still don't understand why doesn't it just return an error saying so.
Since the custom friend list just shows statistics, I just put a loading icon in place for each statistic. If the handler is not called for more than 10 seconds, I assume that there is no score entry and just display a 0.
In my app I have an achievement for 10 wins in a row. So when the user wins 5 games in a row I report the achievement 50% completed - this works fine. When the user loses some games I call my resetAchievment method which sets the percentage to 0 and reports the percentage again. However when I restart the app the percentage gets read from the GKAchivement and it still shows 50%.
- ( void ) resetAchievement
{
_gamekitAchievement.percentComplete=0.0f;
_counter = 0;
[self report];
}
- ( void ) report
{
_gamekitAchievement.showsCompletionBanner = YES;
[_gamekitAchievement reportAchievementWithCompletionHandler:^(NSError *error)
{
if (error)
{
NSLog(#"reporting Achievment: %# failed, error: %#", _gamekitAchievement.identifier, [error localizedDescription]);
}
}];
}
Is it not possible to report a smaller percentage again - or am I doing something wrong?
I have no actual experience from GameKit at all but from reading the documentation and searching the web it seems you're only able to report progress and not regression(?) Not to mention the fact that you can only reset ALL achievements... Perhaps the following would still help you achieve (ahem) what you want:
Update all your local achievement data with + (void)loadAchievementsWithCompletionHandler:
Store this data in the userDefaults for safety
Do NOT reset the local achievement data
Reset all the achievements with + (void)resetAchievementsWithCompletionHandler:
Change the percentComplete to 0 on the achievement in question.
report progress of all the achievements from your local storage
Now, as I mention, having no hands-on experience with this framework the above might not be practical for a number of reasons I am unaware of (the way progress is presented to the player for instance). Guess it was worth a shot to share the idea anyways...
Short answer: It is not possible to report a smaller percentage again.
Long answer: A lower GameCenter score will not overwrite a higher one.
For example you have a high score of 10 points and it's recorded in GameCenter.
If in the next try you get 5 points and they are submitted to GC, your high score of 10 won't be affected.
Similarly if you try to 'reset' your score, it counts as submitting a new score of 0, so it won't make a difference.
This only applies to scores and achievements submitted to GameCenter, inside your app you can do anything you want (for example your own custom leaderboard or something)
If you think about it, 5 wins is not 50% of the way there, since they could lose the 6th, then win 10 in a row, in which case they played 16 total games and 5 wins was more like 30% of the way there. In this case, it is all or nothing, so don't report anything until they have all 10.
Please be aware that storing the progress towards an achievement in NSUserDefaults doesn't take different GameCenter users into account. That means, if Player A (logged into GameCenter) makes some progress and at 90 % progress another Player B on the same device logs into GameCenter with his own account, he starts with the progress made by Player A.
You might solve this by storing a whole NSDictionary with the Player IDs as keys into NSUserDefaults.
What you'll want to do is save it NSUserdefaults. then, Call the NSUser default and check what number it is by using an IF statement.
-(void)checkAcheivement{
scoreNumber = [NSUserDefaults standardUserDefaults] objectForKey:#"GamesWon"]
if(scoreNumber == 10){
//Run code to save achievement in Game Center(_gamekit stuff)
}
//-(void)checkScoreFire{
//scoreNumber = [NSUserDefaults standardUserDefaults] objectForKey:#"GamesWon"]
//if(scoreNumber == 5){
//Run code to set up alert view for 5 wins below
//UIAlertView* message = [[UIAlertView alloc]
// initWithTitle: #"Alert"
// message: #"You are halfway to the Achievement!"
// delegate: self
// cancelButtonTitle: #"Dismiss"
// [message show];
//}
//to save score use this method:
-(void)saveScore{
scoreNumber = [NSUserDefaults standardUserDefaults] objectForKey:#"GamesWon"]
if(gameScore > scoreNumber){
[[NSUserDefaults standardUserDefaults]setInteger:scoreNumber forKey:#"GamesWon"];
}
This will only post the achievement if it is ten, i suggest not putting the achievement in until then, because it makes it more difficult. I also suggest setting up an AlertView to let the person know they are halfway there. You can do so with the code commented above with the checkScoreFive method. Remember to declare everything in the .h file as well.
IBOutlet UIAlertView *AlertView;
-(void)checkAcheivement;
-(void)checkScoreFire;
-(void)saveScore;
int gameScore;
int scoreNumber;
Is it possible to take data we get from GameCenter and create our own skin for it?
If so where can I access all the data we receive? The important data I want is current matches, everything else is not a big deal to me. Can anyone help?!
These should get you going:
[GKTurnBasedMatch loadMatchesWithCompletionHandler:(void (^)(NSArray *matches, NSError *error))completionHandler];
[GKTurnBasedMatch loadMatchDataWithCompletionHandler:(void (^)(NSData *matchData, NSError *error))completionHandler];
Edit:
It would take a very long post to explain the whole process step by step, but here is the main idea:
[GKTurnBasedMatch loadMatchesWithCompletionHandler:(void (^)(NSArray *matches, NSError *error)){
for (GKTurnBasedMatch *myMatch in matches) {
// update your UI depending on the games. Below is just an example.. This part is up to you - update a tableView, manage a view etc..
int k = 0; // will hold the number of active players still in the game
for (GKTurnBasedParticipant *part in myMatch.participants) {
if(participant.matchOutcome != GKTurnBasedMatchOutcomeQuit){
k++;
}
}
if ([myMatch.currentParticipant.playerID isEqualToString [GKPlayer localPlayer].playerID]) {
//our turn
if (k<2) { //there are less than 2 active players - end game if it's your turn etc...
//end turn depending on your turn.
return;
}
//update your UI for that match..
} else { //not your turn
//update your UI - goes to their turn section for example
}
}
}];
Again I just wrote all of this from the top of my head so I'm sure there are mistakes but thats the main route you want to take. You want to get a list of your current matches - and list them according to whose turn it is or if the game is ended and other things.