運用一個優化過的動畫系統、物理引擎和事件處理機制創作以精靈為基礎的2D游戲。
Overview
SpriteKit是一個圖形渲染和動畫基礎庫,你可以用它給任意的紋理圖片(或者說精靈)做動畫。SpriteKit提供一個傳統的渲染循環在決定幀內容和渲染幀之間交替進行。幀的內容和這些內容怎么變化由你決定。SpriteKit則利用圖形硬件高效的渲染這些幀。SpriteKit為在你的內容上應用任意的動畫和改變做了優化。這個設計讓SpriteKit更適合那些要求更加靈活的動畫處理的游戲和App。
精靈內容的繪制是通過在一個精靈View的里面呈現
Scenes
動畫和渲染是通過一個SKView對象運行的。你把這個view放在一個window里面,然后在它上面渲染內容。因為它是一個view,它的內容能夠在視圖層級里和其他view相結合。
你游戲里面的內容被組織到一些scene里面去,這些scene通過SKScene對象呈現。一個scene持有一些精靈和其他將要渲染的內容。一個scene也實現每幀邏輯和內容處理。在任意給定的時間,視圖呈現一個scene。只要一個scene被呈現出來,它的動畫和每幀邏輯也自動執行。
為了使用SpriteKit創建一個游戲或者app,你可以創建一個SKScene類的子類,或者創建一個scene代理來執行主要的游戲相關任務。例如,你可能創建單獨的scene類來展示一個主菜單,游戲界面和游戲結束后的內容。你可以在窗口中輕松的使用單獨的SKView對象并且在不同的scene之間切換。當你切換場景時,你看可以使用SKTransition類在兩個場景之間做動畫。
一個節點樹定義了在一個場景中出現什么
SKScene類是SKNode類的后代。當使用SpriteKit時,node是構成一切內容的基石,scene對象是一個節點對象樹的根節點。scene和它的后代們決定繪制哪些內容,以及怎么渲染。
每個節點在坐標系里的位置都被它的父節點指定。一個節點也應用其他屬性到它的內容和它子節點的內容上面。例如,當一個節點被旋轉,它所有的子節點也會被旋轉。你可以用節點樹制作一個復雜的圖像,然后通過調整最高層節點的屬性來旋轉,縮放,混合整個圖像。
SKNode類不會繪制任何東西,但是它把它的一些屬性應用到它的后代上。每種類型的可繪制內容通過一個不同的SpriteKit子類表現。一些其他的節點子類不繪制它們自己的內容,但是修改它們的后代的行為。例如,你可以用SKEffectNode對象來應用一個CoreImage濾鏡到一個場景里的一整個節點樹上。通過精確地控制節點樹的結構,你可以決定節點被渲染的順序。
所有的節點對象都是可響應對象,繼承自UIResponder或者NSResponder,因此你可以繼承任何節點類,并且創建新的類來接受用戶輸入。視圖類自動擴展響應鏈來包含場景中的節點樹。
想要了解更多信息,自己查看SKNode文檔吧。
紋理(Textures,或者叫做貼圖)持有可復用的圖形數據
紋理由SKTexture對象表示,是一些用來渲染精靈的共享圖像。無論什么時候你需要應用同樣的圖像到多個精靈上,就使用紋理。通常你通過加載存儲在你的app bundle的圖像文件創建紋理。然而,SpriteKit也可以在運行時從其他資源為你創建紋理,包括Core Graphics圖像或者通過渲染一個節點樹到一個紋理上。
SpriteKit通過處理加載紋理的底層代碼從而使他們對圖形硬件可用來簡化紋理管理。紋理管理被SpriteKit自動管理。然而,如果你的游戲使用大量圖像,你可以通過掌控這個過程中的一部分來提升性能。你主要通過明確的告知SpriteKit加載一個紋理來實現。
一個紋理圖冊是一組被用于你的游戲里的相關紋理。例如,你可能用一個紋理圖冊來存儲所有需要給一個角色做動畫的紋理或者所有需要用于渲染一個游戲背景的瓷磚。SpriteKit用紋理圖冊來提升渲染性能。
想要了解更多信息,自己查看SKTexture和SKTextureAtlas文檔吧。
節點執行action(動作)來給內容做動畫
一個場景的內容通過action來做動畫。每一個action都是一個SKAction類的對象,你告訴node去執行action。然后,當場景處理動畫幀的時候,action被執行。一些動作在一個動畫幀之內就完成了,其他的一些動作在完成之前會在多個動畫幀內造成變化。action最常見的用途就是對節點的屬性進行動態變化。例如,你可以創建一些動作,它們能夠移動一個節點,拉伸或者旋轉它,或者讓它透明。然而,action也可以改變節點樹,播放聲音,或者執行自定義代碼。
action非常有用,但是你也可以組合一些action來創造更復雜的效果。你可以創建幾組動作同時運行或者按照一定的順序運行。你也可以讓這些動作自動重復。
場景(scenes)也可以執行自定義的每幀處理。你重寫你的場景子類的一些方法來執行額外的游戲任務。例如,如果一個節點每幀都需要移動,你可能每幀都直接調整它的一些屬性,而不是用一個動作來完成這件事。
想要了解更多信息,自己查看SKAction文檔吧。
在你的場景里添加剛體(Physics Bodies)和結合點來模仿物理效果
盡管你可以控制你場景里每一個節點的確切位置,你還常常想要這些節點之間相互作用,相互碰撞,并且在這個過程中帶來速度的變化。你可能也希望做一些沒有被動作系統處理的事情,例如模仿重力和其他力。為了這個,你創建剛體(SKPhysicsBody)并且綁定到你的場景里面的節點上去。每一個剛體都被定義了形狀,大小,質量和其他的一些物理特征。場景在一個綁定的SKPhysicsWorld對象里定義了全局的物理模擬特征。你用這個物理世界對象為整個模擬定義重力和速度。
當剛體被包含在場景里面時,這個場景會在這些剛體上模擬物理規律。一些力,例如摩擦力和重力,是被自動應用的。通過添加SKFieldNode對象到場景中,其他的一些力可以被自動應用到多個剛體上。你也可以通過直接修改它的速度或者添加力或者脈沖到它上面來直接影響一個區域體。每一個剛體的加速度和速度都經過計算,剛體相互碰撞。然后,模擬結束之后,相關節點的位置和旋轉都被更新。
你可以精確控制物理效果相互作用。例如,你可以指定一個特殊的物理區域節點只影響場景里面的一小部分剛體。你也能夠決定哪些剛體能相互碰撞,并且單獨地決定哪些交互能引起你的App做出響應。你用這些回調來添加游戲邏輯。例如,當一個節點的剛體被其他的剛體擊中的時候,你的游戲可能摧毀這個節點。
你也可以用物理世界去找到場景中的剛體,并且用一個連接點(SKPhysicsJoint)來把這些剛體連在一塊兒。被連起來的剛體根據連接點的種類不同在一起模擬不同的樣式。
想要了解更多信息,請查看Simulating Physics
。
開始使用SpriteKit
SpriteKit把內容作為一個分級的節點樹實現。一個節點樹由一個作為根節點的場景和其他提供內容的節點組成。一個場景的每一幀都被處理并渲染到一個視圖上去。場景執行動作并且模擬物理規律,這都會改變樹的內容。然后場景被SpriteKit高效的渲染。
開始學習SpriteKit之前,你因該以下面的順序看看這些類,然后再去看庫里的其他類:
創建你的第一個場景
SpriteKit內容被置于一個window里面,就像其他可見內容一樣。SpriteKit內容由SKView類渲染。一個SKView對象渲染的內容被稱為一個場景,也就是一個SKScene對象。場景參與響應鏈并且擁有其他能夠使他們適用于游戲的特色。
因為SpriteKit內容由一個視圖對象渲染,你可以把這個視圖和視圖層級里的其他視圖結合起來。例如,你可以使用標準的控制按鈕并且把他們放在你的SpriteKit視圖上面。或者,你可以給精靈添加互動來實現你自己的按鈕,怎么選擇由你決定。這個例子后面,你將會看到如何在場景上面實現互動。
一個SKView對象可以當做子視圖添加到一個UIView對象上去,或者你可以通過storyboard,使用自定義類,或者代碼,明確地把你的view controller的視圖轉換成一個SceneKit視圖。下面的列表向你展示則怎么重寫一個view controller的viewDidLoad()方法來把它的視圖轉換成一個SKView對象:
- 把一個view controller的view轉換成一個SKView
override func viewDidLoad() {
super.viewDidLoad()
view = SKView()
}
var skView: SKView {
return view as! SKView
}
SpriteKit視圖創建之后,展示內容的下一步就是創建一個場景。正常情況下,你會為你需要的每一個場景創建SKScene子類,但是為了簡便,下面的代碼僅僅實例化一個被view展現的新的場景對象:
- 創建并展示一個場景
let scene = SKScene(size: CGSize(width: 1024, height: 768))
override func viewWillAppear(_ animated: Bool) {
skView.presentScene(scene)
}
為了在SpriteKit里展示內容,相關的節點被添加到這個場景或者它的子場景里。這個例子中的最后一步是展示一個label,創建一個SKLabelNode對象,然后把它添加到場景中去:
- 添加一個label到場景中
let label = SKLabelNode(text: "SpriteKit")
label.position = CGPoint(x: scene.size.width / 2,
y: scene.size.height / 2)
scene.addChild(label)