如何在 iOS 上構建 Infinite Runner:Cocos2D、自動化等

已發表: 2022-03-11

就個人和財務增長而言,開發 iOS 遊戲可以是一種豐富的體驗。 今年早些時候,我在 App Store 中部署了一款基於 Cocos2D 的遊戲 Bee Race。 它的遊戲玩法很簡單:一個無限的跑步者,玩家(在本例中為蜜蜂)收集積分並避開障礙物。 請參閱此處進行演示。

在本教程中,我將解釋為 iOS 開發遊戲的過程,從 Cocos2D 到發布。 作為參考,這裡有一個簡短的目錄:

  • 精靈和物理對象
  • Cocos2D 簡介
  • 將 Cocos2D 與情節提要一起使用
  • 遊戲玩法和(簡要)項目描述
  • 自動化作業。 使用工具。 冷靜點。
  • 應用內結算
  • 使用 Game Center 進行多人遊戲
  • 改進空間
  • 結論

精靈和物理對象

在我們深入細節之前,了解精靈和物理對象之間的區別會很有幫助。

對於出現在無盡賽跑遊戲屏幕上的任何給定實體,該實體的圖形表示稱為精靈,而該實體在物理引擎中的多邊形表示稱為物理對象

因此,精靈被繪製在屏幕上,由其對應的物理對象支持,然後由您的物理引擎處理。 該設置可以在此處可視化,其中精靈顯示在屏幕上,其物理多邊形對應物以綠色勾勒:

在 iOS 無限奔跑遊戲中,精靈和物理對象共存。

默認情況下,物理對像不會連接到它們各自的精靈,這意味著作為 iOS 開發人員,您可以選擇使用哪個物理引擎以及如何連接精靈和身體。 最常見的方法是繼承默認精靈並為其添加一個具體的物理體。

考慮到這一點…

Cocos2D iOS遊戲開發簡明教程

Cocos2D-iphone 是一個 iOS 開源框架,使用 OpenGL 進行硬件圖形加速,支持 Chipmunk 和 Box2D 物理引擎。

首先,我們為什麼需要這樣一個框架? 好吧,對於初學者來說,框架實現了遊戲開發中經常使用的組件。 例如,Cocos2D 可以加載精靈(特別是精靈表(為什麼?)),啟動或停止物理引擎,並正確處理時間和動畫。 它使用經過廣泛審查和測試的代碼來完成這一切——為什麼要花時間重寫可能較差的代碼?

然而,也許最重要的是——Cocos2D遊戲開發使用圖形硬件加速。 如果沒有這樣的加速,任何具有中等數量精靈的 iOS 無限奔跑遊戲都會以顯著較差的性能運行。 如果我們嘗試製作一個更複雜的應用程序,那麼我們可能會開始在屏幕上看到“子彈時間”效果,即每個精靈在嘗試動畫時都有多個副本。

最後,Cocos2D 優化了內存使用,因為它緩存了精靈。 因此,任何重複的精靈都需要最少的額外內存,這顯然對遊戲很有用。

將 Cocos2D 與情節提要一起使用

在我對 Cocos2D 贊不絕口之後,建議使用 Storyboard 似乎不合邏輯。 為什麼不直接用 Cocos2D 等來操作你的對象呢? 好吧,老實說,對於靜態窗口,使用 Xcode 的 Interface Builder 和它的 Storyboard 機制通常更方便。

首先,它允許我用鼠標拖動和定位我的無盡跑步遊戲的所有圖形元素。 其次,Storyboard API 非常非常有用。 (是的,我知道 Cocos Builder)。

這是我的故事板的快速瀏覽:

要學習如何製作無盡的跑步遊戲,請從一個好的故事板開始。

遊戲的主視圖控制器只包含一個 Cocos2D 場景,頂部有一些 HUD 元素:

我們的 Cocos2D 教程從視圖控制器開始。

注意白色背景:這是一個 Cocos2D 場景,它將在運行時加載所有必要的圖形元素。 其他視圖(實時指示器、蒲公英、按鈕等)都是標準的 Cocoa 視圖,使用 Interface Builder 添加到屏幕上。

我不會詳述細節——如果你有興趣,可以在 GitHub 上找到示例。

遊戲玩法和(簡要)項目描述

(為了提供更多動力,我想稍微詳細地描述一下我的無盡奔跑遊戲。如果您想繼續進行技術討論,請隨意跳過本節。)

在現場遊戲中,蜜蜂一動不動,而場地本身實際上是在奔跑,帶來各種危險(蜘蛛和毒花)和特權(蒲公英和它們的種子)。

Cocos2D 有相機對象,旨在跟隨角色; 在實踐中,操作包含遊戲世界的 CCLayer 並不復雜。

控件很簡單:點擊屏幕將蜜蜂向上移動,再一次點擊將其向下移動。

世界層本身實際上有兩個子層。 遊戲開始時,第一個子層從 0 填充到 BUF_LEN 並最初顯示。 第二個子層預先從 BUF_LEN 填充到 2*BUF_LEN。 當蜜蜂到達 BUF_LEN 時,第一個子層被清理並立即從 2*BUF_LEN 重新填充到 3*BUF_LEN,並呈現第二個子層。 通過這種方式,我們在層之間交替,從不保留過時的對象,這是避免內存洩漏的重要部分。

我的無限亞軍遊戲由多層世界組成。

在物理引擎方面,我使用 Chipmunk 有兩個原因:

  1. 它是用純 Objective-C 編寫的。
  2. 我以前使用過 Box2D,所以我想比較兩者。

物理引擎實際上只用於碰撞檢測。 有時,有人問我,“你為什麼不編寫自己的碰撞檢測?”。 實際上,這樣做並沒有多大意義。 物理引擎就是為此目的而設計的:它們可以檢測複雜形狀的物體之間的碰撞並優化該過程。 例如,物理引擎經常將世界分割成多個單元,並且只對相同或相鄰單元中的物體執行碰撞檢查。

自動化作業。 使用工具。 冷靜點。

獨立無限奔跑遊戲開發的一個關鍵組成部分是避免因小問題而絆倒。 開發應用程序時,時間是至關重要的資源,而自動化可以非常節省時間。

但有時,自動化也可能是完美主義和滿足最後期限之間的折衷。 從這個意義上說,完美主義可以成為憤怒的小鳥殺手。

例如,在我目前正在開發的另一個 iOS 遊戲中,我構建了一個框架來使用特殊工具(可在 GitHub 上獲得)創建佈局。 這個框架有其局限性(例如,它在場景之間沒有很好的過渡),但使用它可以讓我在十分之一的時間內製作我的場景。

因此,雖然您無法使用特殊的超級工具構建自己的超級框架,但您仍然可以並且應該盡可能多地自動化這些小任務。

完美主義可能是憤怒的小鳥殺手。 時間是 iOS 遊戲開發的重要資源。
鳴叫

在構建這個無限的跑步者時,自動化再次成為關鍵。 例如,我的藝術家會通過一個特殊的 Dropbox 文件夾向我發送高分辨率圖片。 為了節省時間,我編寫了一些腳本來為 App Store 所需的各種目標分辨率自動構建文件集,同時添加了 -hd 或 @2x(所述腳本基於 ImageMagick)。

在其他工具方面,我發現 TexturePacker 非常有用——它可以將精靈打包到精靈表中,這樣您的應用程序將消耗更少的內存並更快地加載,因為您的所有精靈都將從單個文件中讀取。 它還可以以幾乎所有可能的框架格式導出紋理。 (請注意,TexturePacker 不是免費工具,但我認為物有所值。您還可以查看 ShoeBox 等免費替代品。)

與遊戲物理相關的主要困難是為每個精靈創建合適的多邊形。 換句話說,創建一些形狀不明顯的蜜蜂或花朵的多邊形表示。 甚至不要嘗試手動執行此操作 - 始終使用特殊應用程序,其中有很多。 有些甚至非常……異國情調——比如用 Inkspace 創建矢量蒙版,然後將它們導入到遊戲中。

對於我自己的無盡跑步遊戲開發,我創建了一個工具來自動化這個過程,我稱之為 Andengine Vertex Helper。 顧名思義,它最初是為 Andengine 框架設計的,儘管它現在可以與多種格式一起正常工作。

在我們的例子中,我們需要使用 plist 模式:

 <real>%.5f</real><real>%.5f</real>

接下來,我們創建一個帶有對象描述的 plist 文件:

 <?xml version="1.0" encoding="UTF-8"?> <!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd"> <plist version="1.0"> <dict> <key>jet_ant</key> <dict> <key>vertices</key> <array> <real>-0.18262</real><real>0.08277</real> <real>-0.14786</real><real>-0.22326</real> <real>0.20242</real><real>-0.55282</real> <real>0.47047</real><real>0.41234</real> <real>0.03823</real><real>0.41234</real> </array> </dict> </dict> </plist>

還有一個對象加載器:

 - (void)createBodyAtLocation:(CGPoint)location{ float mass = 1.0; body = cpBodyNew(mass, cpMomentForBox(mass, self.sprite.contentSize.width*self.sprite.scale, self.sprite.contentSize.height*self.sprite.scale)); body->p = location; cpSpaceAddBody(space, body); NSString *path =[[NSBundle mainBundle] pathForResource:@"obj _descriptions" ofType:@"plist"]; // <- load plist NSDictionary *objConfigs = [[[NSDictionary alloc] initWithContentsOfFile:path] autorelease]; NSArray *vertices = [[objConfigs objectForKey:namePrefix] objectForKey:@"vertices"]; shape = [ChipmunkUtil polyShapeWithVertArray:vertices withBody:body width:self.sprite.contentSize.width height:self.sprite.contentSize.height]; shape->e = 0.7; shape->u = 1.0; shape->collision_type = OBJ_COLLISION_TYPE; cpSpaceAddShape(space, shape); }

要測試精靈如何對應於它們的物理身體,請參見此處。

好多了,對吧?

總之,盡可能自動化。 即使是簡單的腳本也可以為您節省大量時間。 而且重要的是,這段時間可以用於編程而不是鼠標點擊。 (為了獲得額外的動力,這裡有一個代幣 XKCD。)

應用內結算

遊戲中收集的吹球作為應用內貨幣,允許用戶為他們的蜜蜂購買新皮膚。 但是,這種貨幣也可以用真錢購買。 關於應用內計費,需要注意的重要一點是您是否需要執行服務器端檢查以確保購買有效性。 由於所有可購買的商品在遊戲性方面基本相同(只是改變了蜜蜂的外觀),因此無需執行服務器檢查購買有效性。 但是,在許多情況下,您肯定需要這樣做。

更多信息,Ray Wenderlich 擁有完美的應用內計費教程。

使用 Game Center 進行多人遊戲

在手機遊戲中,社交不僅僅是添加 Facebook 的“贊”按鈕或設置排行榜。 為了讓遊戲更精彩,我實現了多人遊戲版本。

它是如何工作的? 首先,兩個玩家使用 iOS 遊戲中心的實時匹配進行連接。 由於玩家實際上是在玩同一個無限奔跑遊戲,因此只需要一組遊戲對象。 這意味著一個玩家的實例需要生成對象,而另一個玩家的實例將讀取它們。 換句話說,如果兩個玩家的設備都在生成遊戲對象,則很難同步體驗。

考慮到這一點,在建立連接後,雙方玩家都會互相發送一個隨機數。 編號較大的玩家充當“服務器”,創建遊戲對象。

你還記得關於分塊世界生成的討論嗎? 我們有兩個子層,一個從 0 到 BUF_LEN,另一個從 BUF_LEN 到 2*BUF_LEN? 這種架構並不是偶然使用的——它必須在延遲的網絡上提供流暢的圖形。 當生成對象的一部分時,它會被打包到一個 plist 中並發送給其他玩家。 緩衝區足夠大,即使網絡延遲也可以讓第二個玩家玩。 雙方玩家以半秒的時間互相發送他們當前的位置,同時也立即發送他們的上下動作。 為了平滑體驗,位置和速度每 0.5 秒通過平滑動畫進行校正,因此在實踐中看起來其他玩家正在逐漸移動或加速。

關於多人無盡奔跑遊戲當然還有更多的考慮因素,但希望這能讓你對所涉及的挑戰類型有所了解。

改進空間

遊戲永遠不會結束。 誠然,有幾個方面我想改進自己的,即:

  1. 控制問題:對於喜歡滑動的玩家來說,點擊通常是一種不直觀的手勢。
  2. 使用 CCMoveBy 動作移動世界層。 當世界層的速度恆定時,這很好,因為 CCMoveBy 動作是用 CCRepeatForever 循環的:

     -(void) infiniteMove{ id actionBy = [CCMoveBy actionWithDuration: BUFFER_DURATION position: ccp(-BUFFER_LENGTH, 0)]; id actionCallFunc = [CCCallFunc actionWithTarget:self selector:@selector(requestFillingNextBuffer)]; id actionSequence = [CCSequence actions: actionBy, actionCallFunc, nil]; id repeateForever = [CCRepeatForever actionWithAction:actionSequence]; [self.bufferContainer runAction:repeateForever]; }

    但後來,我添加了世界速度增加,使遊戲更難進行:

     -(void) infiniteMoveWithAccel { float duration = BUFFER_DURATION-BUFFER_ACCEL*self.lastBufferNumber; duration = max(duration, MIN_BUFFER_DURATION); id actionBy = [CCMoveBy actionWithDuration: duration position: ccp(-BUFFER_LENGTH, 0)]; id restartMove = [CCCallFunc actionWithTarget:self selector:@selector(infiniteMoveWithAccel)]; id fillBuffer = [CCCallFunc actionWithTarget:self selector:@selector(requestFillingNextBuffer)]; id actionSequence = [CCSequence actions: actionBy, restartMove, fillBuffer, nil]; [self.bufferContainer runAction:actionSequence]; }

    此更改導致動畫在每次動作重新啟動時破滅。 我試圖解決這個問題,但無濟於事。 但是,我的 beta 測試人員沒有註意到這種行為,所以我推遲了修復。

  3. 一方面,在使用 Game Center 或運行自己的遊戲服務器時,無需編寫自己的多人遊戲授權。 另一方面,它使得創建機器人變得不可能,這是我可能想要改變的。

結論

創建自己的獨立無限亞軍遊戲可能是一種很棒的體驗。 一旦您進入該過程的發布步驟,當您將自己的創作發佈到野外時,這會是一種美妙的感覺。

審查過程可能從幾天到幾週不等。 更多信息,這裡有一個有用的網站,它使用眾包數據來估計當前的審查時間。

此外,我建議使用 AppAnnie 檢查有關 App Store 中所有應用程序的各種信息,並且註冊一些分析服務(如 Flurry Analytics)也會很有幫助。

如果這款遊戲引起了您的興趣,請務必在商店中查看 Bee Race。