> 原文:http://www.jianshu.com/p/bd109ba65dd6
> 作者:[PMST](http://www.jianshu.com/users/596f2ba91ce9/latest_articles)
* “Hey!我昨天*Flappy Bird*得了100分!!!”
* “我葉良辰表示不服!”

Lecture06課時完畢,我們已經初步完成游戲的主體,可惜卻沒有一個衡量得分的標準。類似*FlappyBird*游戲,當然是誰通過的障礙物越多,就越牛逼。不如我們設定如下規則:
* 通過一對障礙物得1分。
* 觸碰地面或者障礙物判定失敗,結算分數。
當前任務主要分為:
1. 顯示分數牌
2. 如何判斷通過障礙物。
## 01.顯示分數牌
像*Flappy Bird*的小游戲,我們不妨僅用*SKLabelNode*來顯示分數,就類似平常我們所用的*UILabel*。請在`var gameState: GameState = .Play`語句下方添加對記分牌的聲明`var scoreLabel: SKLabelNode!`,同時我們還需要用一個變量存儲分數,繼續在下方添加`var score = 0`;此外對于這些顯示額外幫主內容的,我們還需要添加一個`UI`層,請修改早前的`Layer`枚舉:
~~~
enum Layer: CGFloat {
case Background
case Obstacle
case Foreground
case Player
case UI //新內容
}
~~~
類似早前*setupBackground()*,*setupForeground()*那樣,我們依葫蘆畫瓢設置記分牌,請添加一個方法,如下:
~~~
func setupLabel() {
scoreLabel = SKLabelNode(fontNamed: "AmericanTypewriter-Bold")
scoreLabel.fontColor = SKColor(red: 101.0/255.0, green: 71.0/255.0, blue: 73.0/255.0, alpha: 1.0)
scoreLabel.position = CGPoint(x: size.width/2, y: size.height - 20)
scoreLabel.text = "0"
scoreLabel.verticalAlignmentMode = .Top
scoreLabel.zPosition = Layer.UI.rawValue
worldNode.addChild(scoreLabel)
}
~~~
注意到在設置字體名字為`AmericanTypewriter-Bold`過長且之后可能還需要用到,不妨新增一個常量`let kFontName = "AmericanTypewriter-Bold"`(在`kEverySpawnDelay`下方即可),另外`size.height - 20`中的20是一個頁邊距,也是一個常量,不妨也一并替換掉,聲明一個常量`let kMargin: CGFloat = 20.0`。**注意:新增的兩個常量都是在GameScene類中作為全局變量。**
現在`setupLabel()`函數改為:
~~~
func setupLabel() {
scoreLabel = SKLabelNode(fontNamed: kFontName)//改動1
scoreLabel.fontColor = SKColor(red: 101.0/255.0, green: 71.0/255.0, blue: 73.0/255.0, alpha: 1.0)
scoreLabel.position = CGPoint(x: size.width/2, y: size.height - kMargin)//改動2
scoreLabel.text = "0"
scoreLabel.verticalAlignmentMode = .Top
scoreLabel.zPosition = Layer.UI.rawValue
worldNode.addChild(scoreLabel)
}
~~~
點擊運行,不出意外屏幕正中間靠上已經顯示一個大大的"0",可惜無論你經過多少個障礙物,還是鴨蛋,那是因為還未實現計分功能。

## 02.實現計分
**思路:**
在`update()`方法中,每隔大約33毫秒時間檢測一次*Player*是否過了障礙物,倘若過了就得一分,不過這里又有一個問題,倘若已經得知過了第一個障礙物,但緊隨33毫秒后之后,仍然只過了第一個障礙物,難道還得分??顯然不是!為此我們需要為已經過了一次的障礙物添加一個[Passed]標志,而沒有過的障礙物是沒有標志位為[]。如下圖:

圖中的設置了障礙物的標志位:["Passed"]或者[]兩種。那么問題來了,哪里存儲這些標志位呢?答案是*Sprite*中的`userData`屬性,其類型是`NSMutableDictionary`可變字典,請在`func createObstacle()->SKSpriteNode{}`方法中找到`sprite.zPosition = Layer.Obstacle.rawValue`語句下添加一條新語句:
~~~
//...
sprite.userData = NSMutableDictionary()
//...
~~~
注意到一開始`userData`是一個空字典[],倘若執行`userData["Passed"] = NSNumber(bool: true)`,就新增了一個鍵為`Passed`,值為`true`的元素。
理解完這些,開始構思咱們的`updateScore()`方法:
~~~
func updateScore() {
worldNode.enumerateChildNodesWithName("BottomObstacle", usingBlock: { node, stop in
if let obstacle = node as? SKSpriteNode {
if let passed = obstacle.userData?["Passed"] as? NSNumber {
if passed.boolValue {
return
}
}
if self.player.position.x > obstacle.position.x + obstacle.size.width/2 {
self.score++
self.scoreLabel.text = "\(self.score)"
self.runAction(self.coinAction)
obstacle.userData?["Passed"] = NSNumber(bool: true)
}
}
})
}
~~~
**講解:**
1. 起初場景中產生的障礙物都是攜帶的[]空字典內容。
2. *Player*從一對障礙物的左側穿越到右側,才算"Passed",計分一次。
3. 檢測方法很簡單,只需要循環遍歷*worldNode*節點中的所有障礙物,檢查它的*userData*是否包含了*Passed*鍵值。兩種情況:1.包含意味著當前障礙物已經經過且計算過分數了,所以無須再次累加,直接返回即可;2.當前障礙物為[],說明還未被穿越過,因此需要通過位置檢測(*Player*當前位置位于障礙物右側?),來判斷是否穿越得分,是就分數累加且設置當前障礙物為已經"Passed",否則什么都不處理,返回。
請將*updateScore()*添加到*update()*方法中**.Play**情況最下方。
點擊運行,通過障礙物得分!!!