本文使用的是Xcode8.0,語言是Swift3.0。
我們繪制好精靈后,精靈只是一張靜態的圖,作為游戲,我們需要精靈有動作。動作就是開發者相對場景所做的改變的對象,當創建好動作后,只需要告訴精靈運行動作,SpriteKit自動動態的改變精靈的位置等想要精靈執行的動作,直到動作完成。
1 動作概要
每一個動作是一個不透明的(opaque)對象,描述你相對場景、精靈等節點做的改變。動作由SKAction類表示,各種類型的動作都使用類方法來實例化,動作可以做的常見事情有:
- 改變節點的位置和方向
- 改變節點的尺寸或縮放屬性
- 改變節點的可視性和透明度
- 改變精靈節點的內容,以便它可以通過一系列的紋理動起來
- 給精靈節點著色
- 播放簡單的聲音
- 從節點樹種移除一個節點
- 自定義動作調用一個塊或調用對象上的選擇器
一旦動作被創建,動作的類型就不能被改變,動作具有不可變的性質。所以,當游戲中要反復使用相同的動作時,可以創建出一個動作實例,當有節點需要執行動作時直接使用。
動作分為瞬時和非瞬時,瞬時動作表示在一幀動畫內開始并完成,非瞬時則會有一個動畫效果的持續時間,動畫會一幀一幀執行。
2 單個動作使用
2.1 創建動作
最簡單的動作創建方式:
//飛船添加一個往下飛行的動作
func addAction(ship: SKSpriteNode) {
let move = SKAction.moveTo(y: 0, duration: 1)
ship.run(move)
}
動畫有一個持續時間,duration表示持續的時間。當動作完成后,動作就會從節點中移除,無需手動移除。
可以在任何時候運行動作,但是如果運行動作時,場景正在處理動作,新的動作可能不會立即執行。
一個節點可以同時運行多個動作,即使那些動作在不同時間執行,場景會自動跟蹤每個動作要多久完成,并且計算出動作對節點產生的影響。所以,如果設置一個大小相等,方向相反的移動動作,則節點會保持不變。
由于動作是和場景綁定的,節點只有呈現時,節點的動作才會被處理。所以,我可以創建一個節點,并且設置好動作,等到需要的時候,再添加到場景,一旦添加到場景,則會自動執行動作。
如下:
//添加一個新的飛船,自帶放大動作,當點擊屏幕后,將該飛船添加到場景,添加后,飛船會自動執行放大動作。
func createScaleShip() {
scaleShip = createShip()
let scale = SKAction.scale(to: 2, duration: 1)
scaleShip.run(scale)
}
如果一個節點正在運行任何動作hasActions=true
2.2 動作取消
可以取消節點正在運行的動作,調用節點的removeAllActions()
方法即可。如果節點正在運行動作,則動作會立馬取消,已經做出的改變保持不變,但是不會執行后續的改變。
2.3 動作完成回調
節點的該方法run(action: SKAction, completion: () -> Void)
會在動作執行結束后,執行回調,如果動作被移除,則回調不會執行。
2.4 動作命名
通常情況下,我們不會移除全部動作,可能只是移除掉某一個動作,這時,我們就應該給動作命名,然后通過該名稱來識別動作,然后對動作執行啟動、移除、更換等操作。
run(action: SKAction, withKey: String)
該方法運行動作,會給動作添加上名稱標識,如果已經存在相同名稱,則該已經存在的動作會被先移除。
action(forKey: String)
該方法用于確定,是否已經有一個動作正在使用該名稱。
removeAction(forKey: String)
該方法用于移除動作。
//我們在點擊方法內,給移動飛船添加一個帶名稱Move的動作,每次點擊后會移動到點擊的位置,同時,如果飛船正在移動,則會移除上一次正在移動的動作,執行新動作。
override func touchesBegan(_ touches: Set<UITouch>, with event: UIEvent?) {
let touch = touches.first
let move = SKAction.move(to: touch!.location(in: self), duration: 1)
moveShip.run(move, withKey: "Move")
}
3 復合動作使用
上面是單個動作的用法,SpriteKit提供了很多的單個動作類型,但很多復雜的動作,就需要將一個一個單個動作組合到一起使用。復合動作有三種類型:
- 序列動作:多個子動作組成一個序列,依次執行。
- 組動作:多個子動作組成一個組,在同一時間執行。
- 重復動作:一個子動作,重復不停的執行。
3.1 序列動作
//添加一個序列動作的飛船
func addListAction() {
let listShip = createShip()
listShip.position = CGPoint(x: 0, y: 0)
addChild(listShip)
let move = SKAction.move(to: CGPoint(x: view!.frame.width/2, y: view!.frame.height/2), duration: 1)
let zoom = SKAction.scale(to: 4, duration: 1)
let wait = SKAction.wait(forDuration: 1)
let fade = SKAction.fadeOut(withDuration: 1)
let remove = SKAction.removeFromParent()
let sequence = SKAction.sequence([move,zoom,wait,fade,remove])
listShip.run(sequence)
}
- wait:延時,控制序列的定時。
- removeNode:瞬時動作,不會花費時間來執行。
3.2 組動作
組動作表示一組同時執行的動作集合。
//添加組動作的飛船
func addGroupAction() {
let groupShip = createShip()
groupShip.position = CGPoint(x: 0, y: 0)
addChild(groupShip)
let move = SKAction.move(to: CGPoint(x: view!.frame.width, y: view!.frame.height), duration: 1)
let rotate = SKAction.rotate(toAngle: CGFloat(M_PI*2), duration: 1)
let group = SKAction.group([move,rotate])
groupShip.run(group)
}
- 在組動作執行時,開始是同時開始,但是結束要等到最后一個動作執行后才會結束。
3.3 重復動作
重復動作允許循環另一個動作,所以可以被重復執行多次或者無限次。
//添加重復動作
func repeatAction() {
let repeatShip = createShip()
repeatShip.position = CGPoint(x: view!.frame.width/2, y: view!.frame.height/2+100)
addChild(repeatShip)
let fadIn = SKAction.fadeIn(withDuration: 0.5)
let fadOut = SKAction.fadeOut(withDuration: 0.5)
let list = SKAction.sequence([fadIn, fadOut])
let repeatA = SKAction.repeatForever(list)
repeatShip.run(repeatA)
}
重復動作,組動作,序列動作之間是可以組合使用的,就是說組動作內可以包含組動作、序列動作、重復動作的組合。
4 動作配置
4.1 動畫計時
默認情況下,動作的持續時間內,動畫是線性變化的,但是可以調整:
- timingMode屬性可以選擇動作模式,比如開始快速動作,后續減速。
- speed屬性可以改變動作執行速率,默認為1.0,如果設置成2.0,則動作執行時,速度快一倍。設置0則暫停動作。如果動作內包含別的動作組合,則會全部應用到設置的值。
- 節點的speed屬性與動作的speed屬性具有相同效果。
4.2 動作存儲
當一個動作會被多次執行時,我們只需要創建一次,然后保存該動作,動作我們可以保存到如下位置:
/**
An optional dictionary that can be used to store your own data in a node. Defaults to nil.
*/
open var userData: NSMutableDictionary?
- 節點userData屬性
- 父節點userData屬性
- 場景的userData屬性
- 子類的userData屬性
4.3 多個節點組合
多個節點的動作可以同時執行,他們是互不干擾的。
5 結語
通過上面的講解,我們可以隨心所欲的設置自己想要的動作,達到想要的效果,但是有一點需要注意,創建并執行動作是有成本的,如果打算在動畫的每一幀都改變節點的屬性,而這些變化在每一幀都需要重新計算,那么最好的辦法是直接改變節點,而不使用動作。
本文代碼(game03):https://github.com/flywo/SwiftGame