iOS9 每日學習第12天:GameplayKit 之 Behaviors 和 Goals

發表於2015-11-19

在上一篇我們學習了利用 GameplayKit的 pathfinding API 來計算位於場景中的兩點之間的路徑,並避開指定的障礙物的演算法。

在這一篇中,讓我們來實現一種不同的在場景中移動的效果。GameplayKit 介紹了 Behaviours(行為) 和 Goals(目標) 的概念.他們提供了一種方式,讓你能夠依賴約束和目標把節點的放置在場景中某個特定位置。讓我們先看一下視訊,然後再來詳細看一下。

上面的例子中(我們馬上要建立它),你可以看到一個黃色的盒子代表一個使用者。黃色的盒子隨著使用者點選場景中的任意一處來移動。特別基本的東西,對吧。有趣的是導彈部分,它能夠尋找到player,並且總是試圖通過player節點的中心。

這不需要任何的物理或者自定義程式碼來完成,這完全有一個行為可定址目標來控制。

現在,讓我們通過 Demo瞭解一下 behaviours 和 goals 是怎麼工作的。

Creating a Behavior and Goal Example

使用預設的 SpriteKit 模版建立專案,開啟 GameScene.swift

setup1.png

首先,我們定義一個例項

GKEntity 是一個通用的實體,可以給它新增元件和方法。在我們的例子中,我們有兩個例項,一個代表 player,一個代表導彈。我們馬上來看一下它的細節實現。

我們還需要建立一個元件系統的陣列。這個元件系統是指符合同樣型別的元件的一個集合。我們可以在需要時候的時候,再定義它(lazy var),因為我們僅需初始化它一次。我們有一個元件作為靶子(可以用來追蹤player的位置,並新增冒煙的效果),另一個作為導彈。我們定義的順序,會成為一會兒運動的順序。所以我們先返回targeting 然後是 rendering. 因為我們希望根據目標的變化,來追蹤顯示他們的。

但什麼才是一個 GameKit 元件呢?我們已經討論了在場景中的實體的效果,但沒講具體做了什麼。一個 GKComponent 在特定部分,囊括了資料和邏輯。元件和實體聯絡,一個實體可能對應多個元件。它們為元件提供可重用的行為。它們通過元件模型,來幫助解決大型遊戲中可能出現的複雜而大型的繼承樹問題。

在這個場景中,兩個實體都有渲染元件,導彈實體還有靶子元件。

設定實體

The Player Entity

下面程式碼是 player 類,它是一個簡單的幾成字 NodeEntity的類,擁有唯一一個元件。注意還有一個 GKAgent2D 的屬性.

GKAgent2D 是 GKAgent的一個子類, 呈現為一個根據速度定位的本地座標系統。

在本例中,代理其實是無言的。如果不是使用者手動干預,它不會做任何事情,也不會對位置進行任何變化。我們需要一個代理,因為靶子元件必須有一個代理。

在初始化中,我們新增一個 RenderComponent 和一個PlayerNode. 我們不詳細講 PlayerNode 了,因為非常枯燥。這裡我們僅簡單畫一個黃色的方盒。

我們把代理設為自己,通過把代理新增到實體上去。

我們還需要去生命 GKAgentDelegate 的代理方法。這樣,當代理更新後,Node 的位置會自動更新,同時,當使用者手動更新了位置後,代理也會通過計算更新位置。

The Missile Entity

missile 實體和 PlayerNode 略有不同。我們新增一個目標代理,讓導彈去追蹤。

你可能注意到這個類中沒有 GKAgent2D,這是因為我們使用了 TargetingComponent 來控制實體在場景中的移動。稍後,我們會討論 TargetingComponent. 現在,我們需要知道,我們已經提供了 targetAgent ,我們啟動代理的方法。

我們需要生命 agentDidUpdate 和 agentWillUpdate兩個代理方法。這和Player類中有什麼不同呢?在這個類中,我們還需要為方法提供 Z 軸的數值。

The Targeting Component

到目前為止,所有的類相對都是輕便的。你可能都忘了還需要在靶子元件中完成邏輯程式碼
幸運的是, 得益於 GameplayKit,在本例中,我們僅需要寫20行程式碼就可以。

這段程式碼簡單的不需解釋。你可以看到他繼承自 GKAgent2D, 建立了一個GKGoal.然後通過這個goal 建立了CKBehavior物件。如果你有多個 goal,例如去追蹤一個目標同時要避開某個目標,你就可以建立多個 GKGoal。 你甚至還可以分別GKGoal 的 weight 屬性,這樣可以設定避開某個 goal 比追逐某個 goal 的權重更重一些。

我們同時也設定了一些其他的屬性:maxSpeed,maxAcceleration 和 mass. 這些屬性需要根據你的實際場景進行設定,這裡設定成這樣對我來說是合適的。剛開始的時候我使用了預設值,然後以為那裡出來毛病。後來發現是預設值太低了,導致移動非常慢,完全看不出效果。

The Missile Node

現在 Missile entity 建立好了,我們需要給它新增一個node,以在場景中顯示。這個node 是SKNode的子類,有一個單獨的方法。

你可以看到setupEmitters 方法建立了兩個 SKEmitter nodes.把 target node 設定為了場景,如果不設定的話,那麼就不會出現跟蹤導彈並冒煙的效果。你可以開啟 MissileFire.sks 和 MissileSmoke.sks 兩個檔案,檢視具體內容,這裡我們不詳細解釋了。

Combining the Parts

現在我們的nodes, entities 和 components都已經建立好了,我們回到 GameScene.swift檔案中,把它們組合起來。 我們需要過載 didMoveToView方法。

我們已經在初始化是建立了 player,所以我們新增player.node到場景中。

對於missile, 我們也必須要在這裡建立好。

然後我們為 missile 新增setupEmitters方法,讓煙霧可以根據目標移動並擴散,而非只是動一下。

最後,所有的entities建立好後,我們新增它的components到我們的元件系統中。

現在在update.currentTime方法中,為元件的更新時間陣列,新增增量時間。這會使的重新計算時間並進行渲染。

這就是全部我們做的了。現在執行一下游戲,你會看到一個導彈始終跟隨著playe。在這裡我們並沒有新增碰撞和爆炸效果,如果你感興趣可以自己做一下。為什麼不呢?

延伸閱讀

想要了解更多關於 GameplayKit的特性,推薦觀看WWDC 2015的session 608, Introducing GameplayKit. 別忘了,可以在Git中找到本文的示例程式碼。

備註:本文譯者對 iOS戲比較陌生,如有翻譯錯誤,還望大家在評論中指出。

相關文章