> 原文:http://www.jianshu.com/p/595e11f4528b
作者:[PMST](http://www.jianshu.com/users/596f2ba91ce9/latest_articles)
Flappy Bird整個項目臨近尾聲,要做的只是對游戲體驗的優化,本文先解決兩個,分別是:
1. 實現Player 靜態時的動畫,修改早前掉落時直上直下的問題。
2. Player撞擊障礙物時,給出一個shake搖晃動畫。
游戲最后實現的效果是這樣的:

## Player動畫實現
當游戲狀態為.Tutorial的時候,Player是靜態呈現在教程界面上的,為此我們想要實現一個動畫,讓其揮動翅膀。而實現方法也很簡單,動畫由多張圖片組成,指定一定時間播放完畢,具體用**SKTexture**實例化每一個圖片,然后放到數組中;緊接著調用**animateWithTextures(_:timePerFrame:)**播放動畫。

找到setupTutorial()方法,再其下方新增一個方法:
~~~
func setupPlayerAnimation() {
var textures: Array<SKTexture> = []
// 我們有4張圖片
for i in 0..<4 {
textures.append(SKTexture(imageNamed: "Bird\(i)"))
}
// 4=3-1
for i in 3.stride(through: 0, by: -1) {
textures.append(SKTexture(imageNamed: "Bird\(i)"))
}
let playerAnimation = SKAction.animateWithTextures(textures, timePerFrame: 0.07)
player.runAction(SKAction.repeatActionForever(playerAnimation))
}
~~~
正如前面所說,我們采用`for-in`循環實例化了4個`SKTexture`實例存儲于數組中,接著調用方法播放動畫。現在請將該方法添加到`switchToMainMenu()`以及`switchToTutorial()`方法中的最后,點擊運行,看看Player是否揮動翅膀了。
在玩游戲的時候我們會注意到Player掉落時是直上直下,有些呆板,這里需要替換掉,動畫效果如圖:

在開始實現Player旋轉機制前,先定義幾個常量以及變量,請在`GameScene()`類中添加如下屬性
~~~
// 新增常量
let kMinDegrees: CGFloat = -90 // 定義Player最小角度為-90
let kMaxDegrees: CGFloat = 25 // 定義Player最大角度為25
let kAngularVelocity: CGFloat = 1000.0 // 定義角速度
// 新增變量
var playerAngularVelocity: CGFloat = 0.0 // 實時更新player的角度
var lastTouchTime: NSTimeInterval = 0 // 用戶最后一次點擊時間
var lastTouchY: CGFloat = 0.0 // 用戶最后一次點擊坐標
~~~
請找到`flapPlayer`方法,這個方法是在游戲狀態下,用戶點擊一次屏幕需要調用的方法(具體請跳到`touchesBegan`方法),為此我們將在這里進行`lastTouchTime`與`lastTouchY`變量的更新,替換后的內容如下:
~~~
func flapPlayer(){
// 發出一次煽動翅膀的聲音
runAction(flapAction)
// 重新設定player的速度!!
playerVelocity = CGPointMake(0, kImpulse)
//===========新增內容============
playerAngularVelocity = kAngularVelocity.degreesToRadians()
lastTouchTime = lastUpdateTime
lastTouchY = player.position.y
//==============================
// 使得帽子下上跳動
let moveUp = SKAction.moveByX(0, y: 12, duration: 0.15)
moveUp.timingMode = .EaseInEaseOut
let moveDown = moveUp.reversedAction()
sombrero.runAction(SKAction.sequence([moveUp,moveDown]))
}
~~~
如此每次用戶點擊一次屏幕,就會重新計算Player應該旋轉多少。那么什么時候去真正更新Player的狀態呢?答案是`update()`方法。這里我們要更新的是Player的信息,請找到`updatePlayer()`方法,新增如下內容到最后:
~~~
if player.position.y < lastTouchY {
playerAngularVelocity = -kAngularVelocity.degreesToRadians()
}
// Rotate player
let angularStep = playerAngularVelocity * CGFloat(dt)
player.zRotation += angularStep
player.zRotation = min(max(player.zRotation, kMinDegrees.degreesToRadians()), kMaxDegrees.degreesToRadians())
~~~
點擊運行!不出意味應該和預期效果一樣。
## Shake動畫
先前說到Player撞擊障礙物后要有一個搖晃的動畫以及閃爍的小鍋,那樣顯得更有真實感不是嗎,這里需要調用screenShakeWithNode來實現,搖晃對象是誰?自然是**worldNode**嘍。
由于內容簡單,請直接定位到`switchToFalling()`方法,替換早前內容:
~~~
enum Layer: CGFloat {
case Background
case Obstacle
case Foreground
case Player
case UI
case Flash //新增一個層
}
func switchToFalling() {
gameState = .Falling
// Screen shake
let shake = SKAction.screenShakeWithNode(worldNode, amount: CGPoint(x: 0, y: 7.0), oscillations: 10, duration: 1.0)
worldNode.runAction(shake)
// Flash
let whiteNode = SKSpriteNode(color: SKColor.whiteColor(), size: size)
whiteNode.position = CGPoint(x: size.width/2, y: size.height/2)
whiteNode.zPosition = Layer.Flash.rawValue
worldNode.addChild(whiteNode)
whiteNode.runAction(SKAction.removeFromParentAfterDelay(0.01))
runAction(SKAction.sequence([
whackAction,
SKAction.waitForDuration(0.1),
fallingAction
]))
player.removeAllActions()
stopSpawning()
}
~~~
哦對了,請注釋掉GameViewController.swift中的幾行代碼,去掉所有調試信息,這樣才是一個完整的游戲;
~~~
// 4.設置一些調試參數
//skView.showsFPS = true // 顯示幀數
//skView.showsNodeCount = true // 顯示當前場景下節點個數
//skView.showsPhysics = true // 顯示物理體
//skView.ignoresSiblingOrder = true // 忽略節點添加順序
~~~
點擊運行,享受你的勞動果實吧!
##結尾
這個游戲系列文章終于連載完成,當時可能是一時興起,最后還是堅持下來了。文章更多是在敘述整個游戲是如何開發出來,并未在一些基礎知識以及實現原理上細說,這是之后我要補充的,最后謝謝大家的支持。如果覺得不錯,請點擊喜歡并關注我,同時將我的文章推薦給你的朋友。8~