用Swift做個遊戲Lecture07 —— 老闆,來塊記分牌!

  • “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情況最下方。

點擊運行,通過障礙物得分!!!

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章