iT邦幫忙

2021 iThome 鐵人賽

DAY 24
0
Mobile Development

從零開始的8-bit迷宮探險!Swift SpriteKit 遊戲開發實戰系列 第 24

從零開始的8-bit迷宮探險【Level 24】誰才是高玩?紀錄本機最高得分

「你只要懂水晶,水晶就會幫助你。」
回村後,山姆賣出了所有水晶,瞬間成為村子裡最富有的人。
其他城鎮的寶石商人也聞名而來,想瞧瞧黑森林的樣貌。
「想要我的水晶嗎?去找吧!全部都在黑森林裡!」
從此之後山姆有了另一個名字:「黑森林水晶實價分析師,山大王,山姆」

今日目標

  • 在遊戲中顯示最高得分
  • 將最高得分存在本機內,下次開啟遊戲時還能看到之前的最高得分紀錄

PS. 這裡是開發 iOS 手機遊戲的系列文,如果還沒看過之前 劇情 文章的朋友,歡迎先點這邊回顧唷!


新增最高得分文字

延續昨天的顯示得分文字,我們繼續在遊戲畫面的右上方加上最高得分的文字節點,一樣放在 scoreNode 裡面

新增最高得分的文字節點 (SKLabelNode)

  • 命名為 labelBest,讓他顯示的文字為 Best: \(bestScore),將標題與分數組合起來顯示
  • 繼續調整原本的 if-let,再加上 let labelBest = self.labelBest
  • fontColor:設定文字顏色
  • fontSize:設定文字大小
  • fontName:設定字體
  • position:設定位置
  • horizontalAlignmentMode:設定水平對齊方式
  • verticalAlignmentMode:設定垂直對齊方式
  • labelBest 加到 scoreNode

新增最高得分的變數

  • 命名為 bestScore
  • GameScene.swift
class GameScene: SKScene {
    ...
    var labelBest: SKLabelNode?
    var bestScore: Int = 0
    
    override func didMove(to view: SKView) {
        ...
        self.labelBest = SKLabelNode(text: "Best: \(bestScore)")
        if let scoreNode = self.scoreNode, let labelScore = self.labelScore, let labelBest = self.labelBest {
            ...
            labelBest.fontColor = UIColor.white
            labelBest.fontSize = CGFloat(24)
            labelBest.fontName = "Copperplate"
            labelBest.position = CGPoint(x: self.size.width - 10, y: 0)
            labelBest.horizontalAlignmentMode = .right
            labelBest.verticalAlignmentMode = .center
            scoreNode.addChild(labelBest)
        }
    }
}

執行結果

畫面右上方顯示了最高得分文字
https://imgur.com/kbO2dQq.gif


將分數存在本機中

Property list 是一種 XML 格式的檔案,可以用來儲存一些設定值,使用 key-value 的方式
我們可以發現專案內有一個 Info.plist 檔案,它紀錄了許多 app 的設定值
為了與原本專案設定做區別,我們額外新增另一個 .plist 檔案來儲存最高分
請先新增一個 setting.plist 檔案

新增 .plist 檔案

  • 首先點選新增檔案
    https://imgur.com/DyRgPfA.png

  • 選擇 Property List -> Next
    https://imgur.com/FNvD9wR.png

  • 將檔案命名為 setting,點擊 Create 按鈕,完成 .plist 檔的新增

  • 點擊 setting.plist,按下 + 新增一個新的欄位
    https://imgur.com/fsbv7hj.png

  • Key 取名為 bestScore,Type 設定為 Number,Value 設定為 0
    https://imgur.com/uGnsrBT.png

資料格式

回到程式碼,新增一個結構 (struct),命名為 Setting,內部新增一個參數命名為 bestScore (與剛剛建立的 setting.plist 內容格式一樣)。
讓這個結構是 Codable 的類型,即遵循 Decodable 與 Encodable 協定。

  • GameScene.swift
struct Setting: Codable {
    var bestScore: Int
}

將最高得分存到變數中

在增加分數時,判斷如果目前得分大於最高得分,就將目前得分存進 bestScore,並且改變 labelBest 的文字顯示

  • GameScene.swift
class GameScene: SKScene {
    ...
    func addScore(add: Int) {
        self.score += add
        self.labelScore?.text = "Score: \(self.score)"
        if self.score > bestScore {
            self.bestScore = self.score
            self.labelBest?.text = "Best: \(self.bestScore)"
        }
    }
}

儲存最高得分

接著來寫儲存最高得分的方法,請新增 setBestScore 方法

  • 參數命名為 setting,讓外部帶想要儲存的資料進來
  • 使用 Bundle.main.url 取得 setting.plist 的檔案位置
  • 使用 PropertyListEncoder().encode 把資料轉成 property list
  • 將資料寫入指定位置裡
  • GameScene.swift
class GameScene: SKScene {
    ...
    func setBestScore(setting: Setting) {
        if let path = Bundle.main.url(forResource: "setting", withExtension: "plist") {
            do {
                let data = try PropertyListEncoder().encode(setting)
                try data.write(to: path)
            } catch {
                print(error)
            }
        }
    }
}

怪物與主角碰觸時,呼叫儲存最高得分方法

新增好儲存方法後,就可以在想要的時機點儲存最高分了,我們先設定在怪物與主角碰觸時,存入最高分

  • 新增 Setting 實體 settingbestScore 帶入最高得分
  • 呼叫 setBestScore 方法,並且把 setting 帶入
  • GameScene.swift
class GameScene: SKScene {
    ...
    func gameStop() {
        ...
        let setting = Setting(bestScore: self.bestScore)
        self.setBestScore(setting: setting)
    }
}

取得最高得分

有了儲存方法後,也需要有取得最高得分紀錄的方法,請新增 getBestScore 方法

  • 使用 Bundle.main.path 取得 setting.plist 檔案位置
  • 使用 FileManager.default.contents 取得檔案的內容
  • 使用 PropertyListDecoder().decode 將 property list 的內容依照 Setting 結構解析出來
  • 將解析出來的值,寫入 self.bestScore
  • GameScene.swift
class GameScene: SKScene {
    ...
    func getBestScore() {
        if  let path = Bundle.main.path(forResource: "setting", ofType: "plist"),
            let file = FileManager.default.contents(atPath: path),
            let setting = try? PropertyListDecoder().decode(Setting.self, from: file)
        {
            self.bestScore = setting.bestScore
        }
    }
}

新增文字節點前,先呼叫取得最高得分方法

在遊戲一開始時,先取得本機內的最高得分紀錄,再接著之前寫的新增文字節點,這樣就可以顯示本機內儲存的分數紀錄了

  • GameScene.swift
class GameScene: SKScene {
    ...
    override func didMove(to view: SKView) {
        ...
        self.getBestScore()
        ...
    }
}

執行結果

可以看到畫面上顯示當前的最高得分,並且在重新開啟遊戲後,還是存有之前的紀錄
https://imgur.com/e3xKXHi.gif


參考來源:
Information Property List
Codable
encode(_:)
contents(atPath:)
decode(_:from:)
How To: Working with Plist in Swift


上一篇
從零開始的8-bit迷宮探險【Level 23】長老,這個水晶值多少錢?
下一篇
從零開始的8-bit迷宮探險【Level 25】今天又是嶄新的一天,回到原點
系列文
從零開始的8-bit迷宮探險!Swift SpriteKit 遊戲開發實戰30

尚未有邦友留言

立即登入留言