从零开始的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


<<:  DAY 20 『 连接 API 实作 - 天气 APP 』Part2

>>:  Day 18 - Using ASCX File to Create Pagination Function with ASP.NET Web Forms C# 建立使用者控制项 - 制作分页功能

软件开发流程 需求蒐集法3 - 焦点团体

来到了需求蒐集的第3篇 - 焦点团体访谈。 之前因为专案需求,有机会举办一场焦点团体访谈,常见的焦点...

C# 入门笔记04(继承)

物件导向 这单元主要是让大家了解物件导向的基本实作 物件导向有三大特性: 封装 继承 多型 封装 类...

Rust-所有权(一)

所有权可以说是Rust核心概念,这让Rust不需要垃圾回收(garbage collector)就可...

测试iT邦帮忙,记录学习历程

##测试文,透过iT邦帮忙记录学习历程 Console.Write("Hello iT邦帮...

【Day 8】节点间的共识(Consensus)

准备出游,6.2 的 Raft 之後再补 5.3 提到 state machine replica...