游戏邦在:
杂志专栏:
gamerboom.com订阅到鲜果订阅到抓虾google reader订阅到有道订阅到QQ邮箱订阅到帮看

iOS游戏平台Game Center成就显示设置指南

发布时间:2011-12-12 22:00:26 Tags:,

作者:Greg Holsclaw

我的某个项目有部分美工工作尚未完成,我决定先着手整合Game Center。此指南旨在说明当成就被发送至Game Center时,如何在游戏中呈现成就内容。我会深入探讨如何融入成就图像,这里还会涉及到一些简单框架的运用。

在此,我默认你的应用已植入Game Center系统。通过运用《Learning Cocos2D》、Ray Wenderlich等教程传授的方法,我得以在短短几小时内就运行应用。

通过用户身份验证,设置好系列iTunes Connect成就后,你定会希望在获得新成就时呈现“Achievement Attained”信息。同样,这不同于仅仅展示成就视窗,还会呈现玩家获得的所有成就。我们希望呈现若干通知消息。

苹果iOS 5已给予我们预先打包好的API调用,这会在玩家完成某项成就时展示成就横幅。下面是最简单的方式:

- (void)sendAchievement:(GKAchievement *)achievement {

achievement.percentComplete = 100.0;   //Indicates the achievement is done
achievement.showsCompletionBanner = YES;    //Indicate that a banner should be shown
[achievement reportAchievementWithCompletionHandler:
^(NSError *error) {
dispatch_async(dispatch_get_main_queue(), ^(void)
{
if (error == NULL) {
NSLog(@"Successfully sent archievement!");
} else {
NSLog(@"Achievement failed to send... will try again \
later.  Reason: %@", error.localizedDescription);
}
});
}];
}

此代码根据标识符(你在iTunes中设定的内容)创建成就目标,你将完成状态设置成100%,将“showsCompletionBanner”属性设置成YES,提交给苹果。新iOS属性“showsCompletionBanner”,其默认设置是NO,但若将其调整成YES,屏幕就呈现包含成就标题和描述的漂亮横幅,如下所示。

achieve1(from gamasutra)

achieve1(from gamasutra)

但此方式存在局限性。首先,其运用普通Game Center图标,而非从游戏成就中下载来的图像。更糟的是,若玩家同时获得两项成就,页面只会呈现一个,我觉得这非常糟糕。最后,这只锁定iOS 5的API调用,所以会导致任何未搭载iOS 5系统的设备崩溃。

所以为修复这些问题,我们将不采用“showsCompletionBanner”属性,而是执行我们自己的通知,或者更准确地说借鉴和修改前人所使用的代码。

首先,我借鉴Type One Error所展示的优秀代码,从https://github.com/typeoneerror/GKAchievementNotification中抓取一些代码植入自己的项目。我们将采用GKAchievementNotification和GKAchievementHandler类,同时进行相应更新和修改。首先,若你在游戏中运用ARC,快速扫描代码,移除那些发行、保留和自动发行代码属性。若你不想进行扫描,试着将文件放入项目及修复不符编译程序的内容,然后再创建内容。

Type One Error类将展示类似于iOS 5所呈现的通知内容,但代码需获悉成就标题和描述是什么。为实现这点,你需要嵌入“showsCompletionBanner”目标。

GKAchievementDescription目标的优点是它们已根据用户语言设定进行本土化,因此采用此方式不存在任何本土化问题。

其弊端在于你无法只加载一个成就描述,你需要加载所有内容。我认为进行此操作的最佳时间是用户已在应用上认证Game Center,此时你需要通过异步调用获得这些消息。值得欣慰的是,苹果在此设有API调用,我将此放置在用户认证访问的CompletionHandler中。

若你采用Ray Wenderlich网站的代码,那么你就既能够运用此方法,又拥有新方法。将NSMutableDictionary * self.achievementsDescDictionary添加至所有处理游戏Game Center代码的类(游戏邦注:它会在随后的体验中存储成就数据)。

- (void)authenticateLocalUser {

if (!gameCenterAvailable) return;

NSLog(@”Authenticating local user…”);
if ([GKLocalPlayer localPlayer].authenticated == NO) {
[[GKLocalPlayer localPlayer]
authenticateWithCompletionHandler:^(NSError *error) {
if([GKLocalPlayer localPlayer].isAuthenticated){
[self retrieveAchievmentMetadata];         //Here is the new code
}
}];
}
}

//Here is the new method.
- (void) retrieveAchievmentMetadata
{
self.achievementsDescDictionary = [[NSMutableDictionary alloc] initWithCapacity:2];

[GKAchievementDescription loadAchievementDescriptionsWithCompletionHandler:
^(NSArray *descriptions, NSError *error) {
if (error != nil) {
NSLog(@"Error %@", error);

} else {
if (descriptions != nil){
for (GKAchievementDescription* a in descriptions) {
[achievementsDescDictionary setObject: a forKey: a.identifier];
}
}
}
}];
}

“retrieveAchievmentMetadata”方法会初始化所有信息库,然后调用游戏所有成就描述,进行循环,将它们添加至信息库。这属于异步调用,所以不应减缓游戏或项目的启动。

现在我们握有各成就的标题和描述,因此能够修改原始代码创造iOS 4/5的善意通知,其将通过Type One Error代码连续展示所有成就。

- (void)reportAchievement:(NSString *)identifier
percentComplete:(double)percentComplete {

GKAchievement* achievement = [[GKAchievement alloc]
initWithIdentifier:identifier];
achievement.percentComplete = percentComplete;

if (percentComplete == 100.0) {
//Show banners manually
GKAchievementDescription *desc = [achievementsDescDictionary objectForKey:identifier]; //Update pull achievement description for dictionary

[[GKAchievementHandler defaultHandler] notifyAchievement:desc];  //Display to user

}
[achievementsToReport addObject:achievement];    //Queue up the achievement to be sent
[self save];

if (!gameCenterAvailable || !userAuthenticated) return;
[self sendAchievement:achievement];   //Try to send achievement
}

- (void)sendAchievement:(GKAchievement *)achievement {
[achievement reportAchievementWithCompletionHandler:
^(NSError *error) {
dispatch_async(dispatch_get_main_queue(), ^(void)
{
if (error == NULL) {
NSLog(@"Successfully sent archievement!");
[achievementsToReport removeObject:achievement];   //Remove Achievement from queue.
} else {
NSLog(@”Achievement failed to send… will try again \
later.  Reason: %@”, error.localizedDescription);
}
});
}];
}

另外查看成就是否100%完整,若是如此,提取正确成就描述目标,向玩家展示。然后继续告知苹果新成就,若游戏属于风景类型,其外观将类似如此。

achieve2(from gamasutra)

achieve2(from gamasutra)

但我觉得若能呈现成就图像而非默认图像会更好。所以为达到此目的,将通知的部分代码更新成如下内容:

if (percentComplete == 100.0) {
//Show banners manually
GKAchievementDescription *desc = [achievementsDescDictionary objectForKey:identifier];

[desc loadImageWithCompletionHandler:^(UIImage *image, NSError *error) {
if (error == nil)
{
[[GKAchievementHandler defaultHandler] setImage:desc.image];   //If image found, updates the image to the achievement image.
}
[[GKAchievementHandler defaultHandler] notifyAchievement:desc];
}];

}

代码处在完成处理模块之中,因此只有处理程序返回时,它才会执行。若其返回图像,那么通知就会更新成图像,然后向玩家展示。

achieve3(from gamasutra)

achieve3(from gamasutra)

这里包含临时美工元素。我之前有说过美工工作有些滞后。通过创造性地运用此方式,你会发现若你制作的是款风景游戏,通知就会显示在侧边。想要解决此风景问题,你需要完成两项工作,调整框架和循环。

在“GKAchievementHandler”类中找到“notifyAchievement”,更新为:

- (void)notifyAchievement:(GKAchievementDescription *)achievement
{

GKAchievementNotification *notification = [[GKAchievementNotification alloc] initWithAchievementDescription:achievement];
notification.frame = kGKAchievementFrameStart;
notification.handlerDelegate = self;

//Adjusting rotation.

if ([[UIApplication sharedApplication] statusBarOrientation] == UIInterfaceOrientationLandscapeLeft) {
notification.transform = CGAffineTransformRotate(notification.transform, degreesToRadian(-90));
} else {
notification.transform = CGAffineTransformRotate(notification.transform, degreesToRadian(90));
}

notification.frame = kGKAchievementFrameStartLandscape;  //Update the frame, you need to create this definition.

[_queue addObject:notification];
if ([_queue count] == 1)
{
[self displayNotification:notification];
}
}

必要时候调整“animateIn”和“animateOut”。我发现这些框架作用显著:

#define kGKAchievementFrameStartLandscape    CGRectMake(-53.0f, 350.0f, 104.0f, 284.0f);

#define kGKAchievementFrameEndLandscape      CGRectMake(20.0f, 350.0f, 104.0f, 284.0f);

所以不妨从Ray Wenderlich指南的Game Center代码着手,连同Type One Error通知代码,然后进行必要调整,所以我们最终的解决方案是:

1)呈现1个或更多成就通知。

2)以图像及完整标题和描述呈现通知。

3)以独立于iOS版本的形式呈现。

你认定玩家已完成某成就,不妨嵌入:

[GCHelper sharedInstance] reportAchievement:identifier
percentComplete:percentComplete];

或其他访问Game Center成就的方式。

最后,若你认为在认证调用初期提取所有成就数据属于运行性能或数据问题,那么可以待到玩家获得成就后再设置“retrieveAchievmentMetadata”调用,然后将其放入完成处理程序,添加代码呈现成就通知。但保存成就需要较少额外收集工作,直到你取回所有描述数据。(本文为游戏邦/gamerboom.com编译,拒绝任何不保留版权的转载,如需转载请联系:游戏邦

iOS Game Center Achievement Display

by Greg Holsclaw

In one of my projects, while I waited for some art work, I wanted to get ahead with my Game Center integration. This tutorial is about how to get Achievements to be displayed in the Game when the achievement is sent to Game Center. I will further explore how to get the achievement images also included. A few simple frameworks will be used.

Also, I am taking it for granted that Game Center is up and running on your app. I was able to get it up and running in a few hours using the methods found in Learning Cocos2D or in numerous tutorials around, here or here.

Once you have a user authenticating, and you have setup a few iTunes Connect achievements, you might want to display an ‘Achievement Attained’ message when a new achievement has been accomplished. Again, this is different than just showing the Achievements window, that shows all the achievements a player has completed. We want a little notification message displayed.

In iOS 5, Apple finally gave us a prepackaged API call that will show an achievement banner when an achievement is complete (per your code giving a 100.0% to an achievement). Here is the simplest way:

This code creates the achievement object according to your identifier (whatever you setup in iTunes), you set the completion to 100%, set the ‘showsCompletionBanner’ property to YES, and shoot it off to Apple. The new iOS property is ‘showsCompletionBanner’ which defaults to NO, but if updated to YES, will display a cute little banner with the title and description of the achievement, as shown below (yes I am using the http://www.neuroshimahex.com/ game as inspiration, so placed as my dev background. Check it out, very fun strategy game).

But this approach has a few limitations. First off, it uses the generic Game Center icon, instead of the image that I uploaded for my game achievement. Worst off, if the player earns two achievements at the same time, only one of them will be displayed, which I think is a bummer. And lastly, this is an iOS 5 only API calls, so will crash any device not running iOS5, oops!

So to repair all of these issues, we will not be using the ‘showsCompletionBanner’ property, but will be implementing our own notification, or more correctly using and modifying some code of people who have gone before us.

To get started, I used the great code that Type One Error demonstrated in http://www.typeoneerror.com/articles/post/game-center-achievement-notifi…. Read that article for some background if you want, and then grab the code at https://github.com/typeoneerror/GKAchievementNotification and install into your project. We will be using the GKAchievementNotification and GKAchievementHandler classes, with some updates and modifications. First off, if you are using ARC for your game, do a quick scan of the code and remove the few release, retain and autoreleases you find in that code. If you don’t want to scan, just try to build after you place the files in your project and fix wherever the compiler squawks at.

The Type One Error classes will display a notification that is similar to the one that the iOS 5 call gives, but your code needs to know what the achievement title and description is. To do this you need to populated a’GKAchievementDescription’ object.

One great thing about GKAchievementDescription objects is that they are already localized according to the language setting of the user (if you support that language) and thus don’t have to worry about any localization issue if you use this method.

The bummer is that you can’t load just one Achievement description, you have to load them all. I believe the best time to do this is when a user has authenticated Game Center on your app, you should do an async call to get them. Galdly Apple does give an API call for this, which I place in the CompletionHandler of the user authentication call.

If you are using the code by Ray Wenderlich, then you have a method like this, which I add one line to, and a new method. Also add an NSMutableDictionary * self.achievementsDescDictionary to whatever class is processing your Game Center code, which will store the achievement data for the rest of the active session.

The ‘retrieveAchievmentMetadata’ method initializes the dictionary then calls to get all the Achievement descriptions for your game, and cycles through and adds them to the dictionary. This is an async call, so it shouldn’t slow down the start of your game or project.

Now that we have all the titles and descriptions for all your achievements, we can modify our original code to create an iOS 4/5 friendly notification, that will also show all achievements in succession using the Type One Error code, again inside of Ray Wenderlich’s code (resource links at the top and bottom of this post).

The additional checks to see if the achievement is 100% complete, and if so, grabs the correct achievement description object and displays it to the user. Then it continues on with telling Apple about the new achievement, and if you have a landscape game, it will look something like this.

But, I think it would be even better to also have the achievement image displayed, instead of the default image. So to do that, update the code with the notification part to this:

Since that code is inside a completion handler block, it will only execute once the handler returns. If it returns an image, then the notification is updated with that image, and then it displays to the user.

Yeah, yeah, I have temporary art in there. I told you the art was falling behind. Anyway, using this method out of the box you will notice that if you have a landscape game, that the notifications are off to the side and sideways (the Type One Error guys made it for portrait only). To fix this for landscape you need to do two things, adjust the frame and the rotation.

Find the ‘notifyAchievement’ method in the GKAchievementHandler class and update to:

Also adjust the ‘animateIn’ and ‘animateOut’ frames as needed. I found these frames to be useful:

So, by using the Game Center code from Ray Wenderlich’s
tutorial as a starting point, along with the Type One Error notification code, we then added adjustments so our final solution does the following:

1) Display one or more achievement notifications (the Type One Error code queues the display of many notifications)

2) Displays the notifications with images and full title and descriptions.

3) In a manner that is iOS version independent.

You determine a player had completed an achievement, sprinkle in:

Or whatever way you access your Game Center achievements

Lastly, if you believe pulling all the achievement data early in the authentication call is a performance or data issue, you can wait and place the ‘retrieveAchievmentMetadata’ call until the player earns an achievement, and place in its completion handler, add the code to display the achievement notification. But there is a little extra legwork needed to save the achievement until you have retrieved all the description data. I leave it to the reader to finish off that particular use case.(Source:gamasutra


上一篇:

下一篇: