- “Hey!我昨天Flappy Bird得了100分!!!”
- “我葉良辰表示不服!”
Lecture06課時完畢,我們已經初步完成遊戲的主體,可惜卻沒有一個衡量得分的標準。類似FlappyBird遊戲,當然是誰通過的障礙物越多,就越牛逼。不如我們設定如下規則:
- 通過一對障礙物得1分。
- 觸碰地面或者障礙物判定失敗,結算分數。
當前任務主要分爲:
- 顯示分數牌
- 如何判斷通過障礙物。
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)
}
}
})
}
講解:
- 起初場景中產生的障礙物都是攜帶的[]空字典內容。
- Player從一對障礙物的左側穿越到右側,纔算”Passed”,計分一次。
- 檢測方法很簡單,只需要循環遍歷worldNode節點中的所有障礙物,檢查它的userData是否包含了Passed鍵值。兩種情況:1.包含意味着當前障礙物已經經過且計算過分數了,所以無須再次累加,直接返回即可;2.當前障礙物爲[],說明還未被穿越過,因此需要通過位置檢測(Player當前位置位於障礙物右側?),來判斷是否穿越得分,是就分數累加且設置當前障礙物爲已經”Passed”,否則什麼都不處理,返回。
請將updateScore()添加到update()方法中.Play情況最下方。
點擊運行,通過障礙物得分!!!