(译)如何使用GameCenter制作一个简单的多人游戏教程:第一部分
?免責申明(必讀!):本博客提供的所有教程的翻譯原稿均來自于互聯網,僅供學習交流之用,切勿進行商業傳播。同時,轉載時不要移除本申明。如產生任何糾紛,均與本博客所有人、發表該翻譯稿之人無任何關系。謝謝合作!
原文鏈接地址:http://www.raywenderlich.com/3276/how-to-make-a-simple-multiplayer-game-with-game-center-tutorial-part-12
程序截圖:
我正在實驗以一種新的方式來撰寫教程--通過采納你們的建議來寫教程!
在網站右邊,你會發現一個新的區域,在那里,你們可以通過投票的方式來決定下一篇教程是什么。(當然,在原作者網站上面,我這里沒有啦:)
在第一次投票中,你們中的好多人說,想讓我寫一篇關于如何制作一個簡單的多人在線游戲教程---現在滿足你們的要求!
在這個2部分系列教程中,你將會學習到如何使用cocos2d和game center來做一個簡單的2人聯機玩的小游戲。
這個游戲非常簡單,就是一只狗與一只貓在比賽跑步---你點屏幕點得越快就越容易贏得比賽!
這個教程假設你對于cocos2d的基礎知識已經非常熟悉了。如果你對于cocos2d完全陌生的話,你可以需要先看看這個網站里的其它教程。
注意:為了完整實踐本系列教程,你必須注冊iOS開發者,這樣的話,你才可以激活Game Center。當然,你將至少需要一臺物理設備(這樣的話,你就可以運行一個程序在模擬器上面,另一個程序在你的設備上面啦)。最后,你將至少需要2個不同的Game Center帳號來測試(不用擔心,你可以創建n個免費的帳號,只需要提供不同的郵件地址就ok了)
準備好了嗎?出發!
Getting Started
這個教程將教你如何向一個簡單的游戲里面添加matchmaking和多人在線支持。
因為游戲邏輯并不是本教程的重點,所以,我已經準備好了一些代碼,只是還沒有聯網功能。
下載上面鏈接中的代碼,編譯并運行,你將會看到下面的游戲截屏:
這個游戲非常簡單,而且代碼注釋良好---你可以直接去研究代碼,但是要確保你能夠看懂每一行代碼。
如果你們對些代碼感興趣的話,我可以再單獨寫一個教程來教大家如何從頭開始構建一個這樣的游戲。(如果你愿意,請到原作者的網站上面去投票!)
激活Game Center:總覽
現在,你已經有了一個非常簡單的可以玩的游戲了,但是,它很無聊,因為你老是自己跟自己玩!
如何使用Game Center的話,這個游戲會變得灰常有趣,因為你可以邀請朋友和你一起玩,或者使用matchmaking來隨機查找線上的玩家。
但是,在你開始寫任何Game Center代碼之前,你需要做以下2件事情:
讓我們一步步來:
創建和設置一個App ID
第一步就是創建并設置一個App ID。首先,你需要登錄到 iOS Dev Center,然后進到 iOS Provisioning Portal。
在那里,選擇App IDs標簽,然后為你的應用程序創建一個App ID,和下面的圖類似(需要你填寫的值可能有差異)。
?
最重要的部分就是Bundle Identifier--你需要設置它為一個唯一的字符串(因此,你不能使用這個教程里我所使用過的!!!)最佳做法是使用你的域名來避免名字沖突。
一旦完成之后,點擊Submit。然后打開Cat Race Xcode工程,選擇Resources\Info.plist,然后輸入你在iOS Provisioning portal里面輸入的任何唯一的字符串,如下所示:(注意,要填寫你自己的)
最后一件事,Xcode有時候會出問題,特別是在你更改了bundle identifier之后,因此,為了保證萬無一失,你需要做以下3步:
- 刪除你的模擬器或者設備上面的Cat Race程序
- 如果模擬器正在運行的話就退出來。
- 點Project\Clean來清理工程
恭喜---現在你已經為你的應用程序創建App ID了,而且接下來會使用到它。下面,你可以通過ITunes Connect注冊你的應用程序并激活Game Center。
在Itunes Connect中注冊你的程序
接下來,登錄到 ?iTunes Connect并為你的應用創建一個新的入口(entry)。
一旦你登錄入到iTunes Connect以后,選擇Manage Your Applications,然后點左上角的藍色的“Add New App”按鈕。
在出現的第一個屏幕中,在App Name中輸入Cat Race,SKU Number中輸入CRI,然后選擇你之前創建的id,類似于下面的截屏:
點continue,并按照提示輸入關于你的app的一些基本信息。
不用擔心你填寫的值對不對,盡管亂填,因為你之后還是可以改的---你只需要往里面添加一些傻瓜式的icon和screenshoot就可以讓iTunes Connect很happy了。
當你做玩這些之后,點Save。如果一切ok,你將會到達“Prepare for Upload”階段,如下圖所示:
點右上角的藍色的“Manage Game Center”按鈕,然后點“Enable”按鈕,再點“Done”。就這么多,你的app的Game Center功能已經激活了,接下來是時候寫一些代碼了。
順便提一下,在“Manager Game Center”部分,你可能注意到了Leaderboards和Achievments等選項。不過,這篇教程中,我們并不會介紹它們,但是,如果你們對此感興趣的話,你可以在我即將出版的書里找到。
認證本地的用戶:策略
當你的游戲開始的時候,第一件事你需要做的就是認證本地玩家。
你可以把它看作是“把玩家添加進Game Center”。如果他已經登錄了的話,那么會收到“Welcome Back!”消息。否則,它會要求玩家輸入用戶名和密碼。
認證本地用戶是非常容易的---你只需要調用 authenticateWithCompletionHandler就可以了。你可以選擇性地傳入一個block,當用戶被認證身份以后就會回調這個block。(block是ios的新特性,ios3.0之前是不能使用的)
但是,這里有個技巧。還有另外一種方式讓用戶登錄和登出。他可以先打開你的app,然后切換到Game Center app,從這里登錄或登出,然后回到你的app。
因此,你的app在用戶認證狀態改變的時候應該得到通知。你可以注冊一個“authentication changed” notification。
因此,我們的策略來認證用戶的過程如下:
- 創建一個單例類來管理所有與Game Center相關的代碼.
- 當單例對象創建的時候,它會注冊“authentication changed” notification。
- 游戲將調用單例對象上的一個方法來認證用戶。
- 不管什么時候用戶被認證(或登出),“authentication changed”回調將會觸發。
- 這個回調將會追蹤用戶當前是否被認證。
現在,你已經知道怎么做啦,讓我們直接coding吧!
認證本地用戶:實現
打開Cat Race項目,點 ?File\New\New File,然后選擇 ?iOS\Cocoa Touch\Objective-C class,再點Next。選擇NSObject作為基類,再點Next,把它命名為GCHelper,然后點Finish。
把GCHelper.h換成下面的形式:
#import<Foundation/Foundation.h>#import<GameKit/GameKit.h>
@interface GCHelper : NSObject {
BOOL gameCenterAvailable;
BOOL userAuthenticated;
}
@property (assign, readonly) BOOL gameCenterAvailable;
+ (GCHelper *)sharedInstance;
- (void)authenticateLocalUser;
@end
這里導入了GameKit頭文件,然后定義了兩個bool型的實例變量--一個用來追蹤設備是否支持game center,還有一個用來追蹤當前用戶是否被認證。
我們也需要創建property,這樣的話就可以直接查看game center是否可用。同時,還需要定義一個靜態方法用來創建單例,還有一個認證本地用戶的方法(這個方法會在app啟動的時候被調用)
接下來,回到GCHelper.m,然后替換成下面的樣子:
@synthesize gameCenterAvailable;#pragma mark Initialization
static GCHelper *sharedHelper = nil;
+ (GCHelper *) sharedInstance {
if (!sharedHelper) {
sharedHelper = [[GCHelper alloc] init];
}
return sharedHelper;
}
這里synthesize gameCenterAvailable屬性,然后定義了單例方法的實現。
注意,有很多方式可以實現單例方法,但是,我們這里使用了最簡單,我們沒有考慮多線程的情況。
接下來,在sharedInstance方法后面加入下列代碼:
- (BOOL)isGameCenterAvailable {// check for presence of GKLocalPlayer API
Class gcClass = (NSClassFromString(@"GKLocalPlayer"));
// check if the device is running iOS 4.1 or later
NSString *reqSysVer =@"4.1";
NSString *currSysVer = [[UIDevice currentDevice] systemVersion];
BOOL osVersionSupported = ([currSysVer compare:reqSysVer
options:NSNumericSearch] != NSOrderedAscending);
return (gcClass && osVersionSupported);
}
這個方法是直接從蘋果的 Game Kit Programming Guide中copy過來的。它用來檢測當前設備是否支持game center。
在使用game center之前必須要判斷其是否可用,這和網絡編程一樣,沒有網的情況一定要判斷。同時,這個app只能運行ios4.0及其以后的系統上面。
接下來,在 isGameCenterAvailable方法后面添加下列代碼:
- (id)init {if ((self = [super init])) {
gameCenterAvailable = [self isGameCenterAvailable];
if (gameCenterAvailable) {
NSNotificationCenter *nc =
[NSNotificationCenter defaultCenter];
[nc addObserver:self
selector:@selector(authenticationChanged)
name:GKPlayerAuthenticationDidChangeNotificationName
object:nil];
}
}
return self;
}
- (void)authenticationChanged {
if ([GKLocalPlayer localPlayer].isAuthenticated &&!userAuthenticated) {
NSLog(@"Authentication changed: player authenticated.");
userAuthenticated = TRUE;
} elseif (![GKLocalPlayer localPlayer].isAuthenticated && userAuthenticated) {
NSLog(@"Authentication changed: player not authenticated");
userAuthenticated = FALSE;
}
}
init方法檢測Game Center是否可用,如果可用,則注冊 ?“authentication changed” notification。(這是非常經典的觀察者模式)。在嘗試認證用戶之前,注冊這個通告灰常重要,這樣,當認證完成的時候,它就會被調用。
這里的 authenticationChanged回調函數是很簡單的--它只是簡單地判斷用戶是否被認證,并且相應地更新標記變量。
注意,實際上,這個回調可能會被調用許多次,所以要確保 userAuthenticated和之前的狀態不一樣,只有當上一次狀態改變的時候才更新。(具體理解參考代碼)
最后,在 authenticationChanged方法后面添加下面方法:
#pragma mark User functions- (void)authenticateLocalUser {
if (!gameCenterAvailable) return;
NSLog(@"Authenticating local user...");
if ([GKLocalPlayer localPlayer].authenticated == NO) {
[[GKLocalPlayer localPlayer] authenticateWithCompletionHandler:nil];
} else {
NSLog(@"Already authenticated!");
}
}
這里調用前面提到過的 authenticateWithCompletionHandler來認證用戶。注意,這里目前并沒有傳入一個完整的處理器(傳入的是nil)。因為,前面你已經注意了 ?“authentication changed”notification了,所以這里就沒有必要再寫一個handler了。
好了--GCHelper現在包含認證用戶所需的所有代碼了,所以,你是時候使用它們了!回到AppDelegate.,然后做如下更改:
// At the top of the file#import"GCHelper.h"
// At the end of applicationDidFinishLaunching, right before
// the last line that calls runWithScene:
[[GCHelper sharedInstance] authenticateLocalUser];
這里創建GCHelper的單例(它的init方法里面注冊了 “authentication changed”通告)并調用 authenticateLocalUser 方法。
差不多快完成了!最后一步就是添加Game Kit framework到你的工程中。我們先點工程文件,然后選擇Build Phases 標簽,展開 “Link Binary with Libraries”,再點”+“號來添加相應的framework。選擇GameKit.framework并點Add。把type改成Required,然后看起來如下圖所示:
就這么多!編譯并運行工程,如果你登入Game Center,你將會看到下面的輸出:
現在,你已經認證用戶了,你可以進而探尋更有趣的部分了,比如找一個人與你共同來玩這個游戲!
Matchmaker, Matchmaker, 給我找個對手吧!
這里有兩種方法可以通過Game Center來找一些人來一起玩游戲:一種是編程來主動查找,另一種是使用內置的matchmaking接口。
在這篇教程中,我們將使用內置的matchmaking接口。當你想要尋找一個對手的時候,只需要在 GKMatchRequest對象上面設置幾個參數,然后再創建并顯示一個 GKMatchmakerViewController就ok了。
讓我們來看一看這個具體是怎么工作的。首先在GCHelper.h中做一些修改:
// Add to top of file@protocol GCHelperDelegate
- (void)matchStarted;
- (void)matchEnded;
- (void)match:(GKMatch *)match didReceiveData:(NSData *)data
fromPlayer:(NSString *)playerID;
@end
// Modify @interface line to support protocols as follows
@interface GCHelper : NSObject <GKMatchmakerViewControllerDelegate, GKMatchDelegate> {
// Add inside @interface
UIViewController *presentingViewController;
GKMatch *match;
BOOL matchStarted;
id<GCHelperDelegate>delegate;
// Add after @interface
@property (retain) UIViewController *presentingViewController;
@property (retain) GKMatch *match;
@property (assign) id<GCHelperDelegate>delegate;
- (void)findMatchWithMinPlayers:(int)minPlayers maxPlayers:(int)maxPlayers
viewController:(UIViewController *)viewController
delegate:(id<GCHelperDelegate>)theDelegate;
這里出現了一些新的內容,讓我們一點一點來看:
- 你定義了一個協議,叫做 GCHelperDelegate。當match開始,結束,或者從第三方接收到數據的時候就會通知其它對象,當然前提是那個對象要實現該協議。在本例中,cocos2d的layer將會實現此協議。
- 同時,GCHelper對象實現了兩個協議。第一個是matchmaker進行玩家查找,不管有沒有找到一個新的match,就會通知實現該協議的對象。第二個就是當數據到達或者連接狀態改變的時候,Game Center會通知GCHelper對象。
- 創建一個新的實例變量和相應的屬性來追蹤view controlller對象(這個對象將會用來顯示matchmaker用戶界面),一個match對象的引用,match是否開始的標記以及一個代理。
- 創建一個新的方法,我們之后的cocos2d layer將會調用這個方法來查找可以一起玩游戲的玩家。
接下來跳轉到GCHelper.m文件,然后做如下修改:
// At top of file@synthesize presentingViewController;
@synthesize match;
@synthesizedelegate;
// Add new method, right after authenticateLocalUser
- (void)findMatchWithMinPlayers:(int)minPlayers maxPlayers:(int)maxPlayers
viewController:(UIViewController *)viewController
delegate:(id<GCHelperDelegate>)theDelegate {
if (!gameCenterAvailable) return;
matchStarted = NO;
self.match = nil;
self.presentingViewController = viewController;
delegate= theDelegate;
[presentingViewController dismissModalViewControllerAnimated:NO];
GKMatchRequest *request = [[[GKMatchRequest alloc] init] autorelease];
request.minPlayers = minPlayers;
request.maxPlayers = maxPlayers;
GKMatchmakerViewController *mmvc =
[[[GKMatchmakerViewController alloc] initWithMatchRequest:request] autorelease];
mmvc.matchmakerDelegate = self;
[presentingViewController presentModalViewController:mmvc animated:YES];
}
這個cocos2d layer將要調用的方法的主要功能就是查找一個玩家。如果Game Center不可用的話,那么就什么也不干,直接返回。
它首先初始化match為未開始狀態,并把match對象設置為nil。并且,存儲視圖控制器和代碼,以便后面使用。同時,還要銷毀前面已經出現的任何模態視圖控制器(比如: GKMatchmakerViewController已經顯示出來了)。
然后,我們來講一下重點內容。 GKMatchRequest允許你配置你將要查找的match的類型,比如最小或者最大的玩家數量。這個方法比較靈活,你可以傳遞任何數量。但是,本游戲只需要設置最小和最大都為2就可以了。
接下來,我們使用給定的 request來創建一個 GKMatchmakerViewController類的實例,同時把代理設置為GCHelper對象,然后把它顯示到屏幕上。
?這時,GKMatchmakerViewController這個類對象的視圖就開始接管工作了。它會允許用戶查找一個隨機的玩家來一起玩游戲。
接下來,我們需要定義一些代理方法:
#pragma mark GKMatchmakerViewControllerDelegate// The user has cancelled matchmaking
- (void)matchmakerViewControllerWasCancelled:(GKMatchmakerViewController *)viewController {
[presentingViewController dismissModalViewControllerAnimated:YES];
}
// Matchmaking has failed with an error
- (void)matchmakerViewController:(GKMatchmakerViewController *)viewController didFailWithError:(NSError *)error {
[presentingViewController dismissModalViewControllerAnimated:YES];
NSLog(@"Error finding match: %@", error.localizedDescription);
}
// A peer-to-peer match has been found, the game should start
- (void)matchmakerViewController:(GKMatchmakerViewController *)viewController didFindMatch:(GKMatch *)theMatch {
[presentingViewController dismissModalViewControllerAnimated:YES];
self.match = theMatch;
match.delegate= self;
if (!matchStarted && match.expectedPlayerCount ==0) {
NSLog(@"Ready to start match!");
}
}
如果用戶取消查找match或者查找過程中出現了錯誤的話,那么我們需要關閉matchmaker 視圖。
但是,如果找到一個match的話,我們需要隱藏此對象,并且設置match的delegate為GCHelper對象。這樣的話,當有新的數據到達,或者連接狀態改變的話,GCHelper對象就會得到通知。
同時,我們也需要檢測是否可以開始match了。match對象保存了仍然需要多少個玩家才能完成連接,這個數目由“ expectedPlayerCount”來定。
如果這個變量是0的話,那么所有人都準備好了。當然,現在我們只是用NSLog輸出一些語句,看是否執行到此處了。
接下來,添加 GKMatchDelegate回調函數實現:
#pragma mark GKMatchDelegate// The match received data sent from the player.
- (void)match:(GKMatch *)theMatch didReceiveData:(NSData *)data fromPlayer:(NSString *)playerID {
if (match != theMatch) return;
[delegate match:theMatch didReceiveData:data fromPlayer:playerID];
}
// The player state changed (eg. connected or disconnected)
- (void)match:(GKMatch *)theMatch player:(NSString *)playerID didChangeState:(GKPlayerConnectionState)state {
if (match != theMatch) return;
switch (state) {
case GKPlayerStateConnected:
// handle a new player connection.
NSLog(@"Player connected!");
if (!matchStarted && theMatch.expectedPlayerCount ==0) {
NSLog(@"Ready to start match!");
}
break;
case GKPlayerStateDisconnected:
// a player just disconnected.
NSLog(@"Player disconnected!");
matchStarted = NO;
[delegate matchEnded];
break;
}
}
// The match was unable to connect with the player due to an error.
- (void)match:(GKMatch *)theMatch connectionWithPlayerFailed:(NSString *)playerID withError:(NSError *)error {
if (match != theMatch) return;
NSLog(@"Failed to connect to player with error: %@", error.localizedDescription);
matchStarted = NO;
[delegate matchEnded];
}
// The match was unable to be established with any players due to an error.
- (void)match:(GKMatch *)theMatch didFailWithError:(NSError *)error {
if (match != theMatch) return;
NSLog(@"Match failed with error: %@", error.localizedDescription);
matchStarted = NO;
[delegate matchEnded];
}
match:didReceiveData:fromPlayer這個方法是在其他玩家給你發送數據的時候被調用的。這個方法只是簡單的把這些數據再轉發給它的代理類。(我們這個游戲中,cocos2d layer會實現此代碼,所以這個代碼是跟游戲需要相關的。
For match:player:didChangState這個方法,是當有玩家接入的時候,你需要檢測是否所有的玩家都已經就緒了。同時,當有玩家斷開連接的時候,這個方法也會被調用。
最后兩個方法是發生錯誤的時候被調用。任何一種情形,都把match標記為已經結束了,同時通知delegate對象。
好了,我們現在寫一些代碼來建立一個match吧。首先從HelloWorldLayer中開始,打開HelloWorldLayer.h,并做如下修改:
// Add to top of file#import"GCHelper.h"
// Mark @interface as implementing GCHelperDelegate
@interface HelloWorldLayer : CCLayer <GCHelperDelegate>
然后,跳轉到HelloWorldLayer.m中做如下修改:
// Add to top of file#import"AppDelegate.h"
#import"RootViewController.h"
// Add to bottom of init method, right after setGameState
AppDelegate *delegate= (AppDelegate *) [UIApplication sharedApplication].delegate;
[[GCHelper sharedInstance] findMatchWithMinPlayers:2 maxPlayers:2 viewController:delegate.viewController delegate:self];
// Add new methods to bottom of file
#pragma mark GCHelperDelegate
- (void)matchStarted {
CCLOG(@"Match started");
}
- (void)matchEnded {
CCLOG(@"Match ended");
}
- (void)match:(GKMatch *)match didReceiveData:(NSData *)data fromPlayer:(NSString *)playerID {
CCLOG(@"Received data");
}
這里最重要的部分就是init方法。它從AppDelegate那里得到一個RootViewController,因為這個視圖控制器將會顯示出matchmaker界面。然后創建一個GCHelper對象來查找一個match。
剩下的部分僅僅是一些樁代碼,簡單的實現了GCHelper協議,同時在里面輸出了一些語句。
最后一件事,默認情況下面,cocos2d 模板并沒有在AppDelegate里面包含一個RootViewController的屬性,因此你必須手動添加一個。跳轉到AppDelegate.h文件,并添加下面的代碼:
@property (nonatomic, retain) RootViewController *viewController;然后跳轉到AppDelegate.m,synthesize之:
@synthesize viewController;就這么多!編譯并運行你的程序,現在你將看到matchmaker視圖了,它看起來如下圖所示:
現在,在另一個設備上運行你的程序。當然,你也可以一個運行在模擬器上面,一個運行在iphone上面。
注意:每一個設備上面需要使用一個不同的game center帳號,否則的話就不能工作。
在兩個設備上都點擊“Play Now”,然后,過了一段時間后,matchmaker視圖將會消失,接著你將會在控制臺輸出下面的語句:
CatRace[16440:207] Authentication changed: player authenticated.CatRace[16440:207] Player connected!
CatRace[16440:207] Ready to start match!
恭喜你!你已經在兩臺設備之間完成了一次match了。你正在制作一個網絡游戲,知道嗎?:)呵呵
Landscape Orientation and GKMatchmakerViewController
你可以已經注意到了,默認情況下 GKMatchmakerViewController顯示的方向是豎的(portrait)。很明顯,這不行,因此,cocos2d模板生成的程序是橫版的。
幸運的是,你可以為 GKMatchmakerViewController寫一個類別,讓它強制只接收橫版方向。
讓我們實現這個,首先點擊 File\New\New File,然后選擇 ?iOS\Cocoa Touch\Objective-C class,再點Next。把NSObject作為基類,點Next,并把這個類取名為 GKMatchmakerViewController-LandscapeOnly.m,最后點擊Finish。
把 GKMatchmakerViewController-LandscapeOnly.h 換成下面的代碼:
#import<Foundation/Foundation.h>#import<GameKit/GameKit.h>
@interface GKMatchmakerViewController(LandscapeOnly)
- (BOOL)shouldAutorotateToInterfaceOrientation:(UIInterfaceOrientation)interfaceOrientation;
@end
然后把 GKMatchmakerViewController-LandscapeOnly.m相應的替換成下面的代碼:
#import"GKMatchmakerViewController-LandscapeOnly.h"@implementation GKMatchmakerViewController (LandscapeOnly)
- (BOOL)shouldAutorotateToInterfaceOrientation:(UIInterfaceOrientation)interfaceOrientation {
return ( UIInterfaceOrientationIsLandscape( interfaceOrientation ) );
}
@end
大功告成!編譯并運行程序,這時視圖控制器顯示為橫版模式了:
何去何從?
這里有本教程的完整源代碼。
在下部分教程中,我們將會涉及到如何在兩臺設備之間發送數據,同時把cat Vs kid包裝成一個非常好玩的游戲!
譯者的話:前段時間忙著考試,現在又忙著做項目,不好意思,很長一段時間沒有更新了,大家見諒。同時,如果翻譯過程中有什么明顯的錯誤,請大家給我指出來,謝謝!
?
著作權聲明:本文由http://www.cnblogs.com/andyque翻譯,歡迎轉載分享。請尊重作者勞動,轉載時保留該聲明和作者博客鏈接,謝謝!
轉載于:https://www.cnblogs.com/zilongshanren/archive/2011/06/24/2088383.html
總結
以上是生活随笔為你收集整理的(译)如何使用GameCenter制作一个简单的多人游戏教程:第一部分的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: resharper 6.0 注册码
- 下一篇: .xib .plist .pch