Unity日记:存档系统
引言
在游戏开发的世界中,存档系统是不可或缺的一部分,它让玩家的游戏进度得以保存,增强了游戏的可玩性和用户体验。今天分享一下如何在Unity中实现游戏存档的功能,特别是PlayerPrefs和JSON存档这两种常见方式,帮助开发者理解它们的适用场景、优缺点以及如何有效使用它们来构建强大的存档机制。
随着游戏复杂度的提升,存档系统的设计和实现变得尤为重要。合理的存档策略能够显著提升玩家的满意度和忠诚度。然而,Unity自带的PlayerPrefs虽然简单易用,但在处理复杂数据或大量数据时显得力不从心。而JSON作为一种轻量级的数据交换格式,因其易于人阅读和编写,以及良好的跨平台特性,在游戏存档中得到了广泛应用。因此,本文将简单介绍这两种方法,帮助开发者根据项目需求做出最佳选择。
Unity存档系统的用途:
- 保存游戏进度:让玩家能够在退出游戏后重新加载之前的游戏状态。
- 设置和配置保存:存储玩家的游戏设置,如音量、难度等。
- 成就和解锁内容:记录玩家的成就和已解锁的内容,作为游戏进度的一部分。
- 跨平台同步:在支持云存档的情况下,实现不同设备间的游戏进度同步。
PlayerPrefs
PlayerPrefs存储数据
PlayerPrefs
是Unity提供的一个简单的数据持久化系统,用于保存和加载玩家偏好设置和游戏数据。它仅支持存储和检索整型(int)
、浮点型(float)
和字符串(string)
数据。
Unity的PlayerPrefs
是Unity引擎提供的一个用于存储和检索玩家偏好设置和游戏状态数据的简单数据持久化系统。它允许开发者跨会话(即游戏运行之间)保存和加载简单的数据类型,如整型(int
)、浮点型(float
)和字符串(string
)。这些数据通常存储在设备的本地存储中,如注册表(在Windows上)或偏好设置文件(在macOS和iOS上)等,具体取决于目标平台。
PlayerPrefs的特点
- 简单易用:
PlayerPrefs
API 非常直观,易于上手。只需几行代码就可以实现数据的保存和加载。 - 跨平台:
PlayerPrefs
数据在不同的Unity支持平台上具有高度的可移植性。无论游戏在哪个平台上运行,PlayerPrefs
都能以相同的方式工作。 - 限制:尽管
PlayerPrefs
非常方便,但它也有一些限制。首先,它只支持整型、浮点型和字符串数据类型。其次,由于数据存储在一个统一的命名空间中,不同游戏或应用可能会无意中覆盖彼此的PlayerPrefs
数据(尽管这可以通过在键名中包含唯一标识符来避免)。最后,PlayerPrefs
数据的存储量也有限制,虽然这个限制对于大多数用途来说都足够大,但在处理大量数据时可能会成为问题。
优点 | 缺点 |
---|---|
使用简单,无需额外配置。 | 数据类型有限,不支持复杂数据结构(如数组、列表、字典等)。 |
跨平台兼容性好,适用于大多数Unity支持的平台。 | 存储容量有限,不适合存储大量数据。 |
安全性较低,数据容易被用户修改。 |
PlayerPrefs的基本用法
保存数据
使用PlayerPrefs.SetInt
、PlayerPrefs.SetFloat
和PlayerPrefs.SetString
等静态方法将数据保存到PlayerPrefs
中。调用PlayerPrefs.Save()
可以手动保存更改,但通常这不是必需的,因为Unity会在游戏结束时自动保存PlayerPrefs
数据。
1 | public static void SetInt(string key, int value); // key 键 value 值 |
1 | PlayerPrefs.SetInt("score", 50); |
加载数据
使用PlayerPrefs.GetInt
、PlayerPrefs.GetFloat
和PlayerPrefs.GetString
等静态方法从PlayerPrefs
中检索数据。这些方法需要一个键名作为参数,并返回与该键名关联的数据。如果指定的键名不存在,则可以提供一个默认值作为第二个参数。
1 | public static int GetInt(string key); |
1 | int highScore = PlayerPrefs.GetInt("HighScore", 0); // 如果HighScore不存在,则返回0 |
删除数据
使用PlayerPrefs.DeleteKey
方法可以删除PlayerPrefs
中的单个键值对。PlayerPrefs.DeleteAll
方法会删除PlayerPrefs
中的所有数据。
1 | PlayerPrefs.DeleteKey("HighScore"); // 删除HighScore键值对 |
示例
1 | using UnityEngine; |
注意事项
- 由于
PlayerPrefs
数据存储在设备的本地存储中,因此它可能受到设备存储空间或用户隐私设置的影响。 - 在使用
PlayerPrefs
时,建议为键名添加前缀或后缀,以确保它们在不同游戏或应用之间保持唯一性。 - 虽然
PlayerPrefs
提供了基本的数据持久化功能,但对于需要处理复杂数据结构或大量数据的游戏来说,可能需要考虑使用更高级的数据存储解决方案,如数据库或文件系统。
适用范围
PlayerPrefs适合用来存储暂时性数据,如:
- 玩家设定偏好
- 简单的数据
- 游戏原型制作时暂时的存储方案
相关资料 -> https://docs.unity3d.com/ScriptReference/PlayerPrefs.html
JSON
JSON
是JavaScript Object Notation(JavaScript对象注释/表示法)
的简称,是一种轻量级的数据交换格式,易于人阅读和编写,同时也易于机器解析和生成。在Unity中,我们可以通过序列化/反序列化对象到JSON字符串的方式来实现存档功能。
参考资料 -> https://www.json.org/json-zh.html
格式
JSON是一种基于文本的格式,用于表示数据。它使用键值对来表示对象,使用数组来表示一组值。例如,一个表示玩家的JSON对象可能如下所示:
1 | { |
特点
优点
- 支持复杂数据结构,如数组、列表、字典等。
- 数据量大小无限制(受限于系统存储空间)。
- 安全性较高,可通过加密等手段保护数据。
- 跨平台兼容性好,JSON是标准的文本格式,易于在不同平台间交换数据。
缺点
- 需要编写额外的序列化/反序列化代码。
- 相对于PlayerPrefs,使用上稍显复杂。
与PlayerPrefs对比
- JSON更适合存储复杂数据结构和大量数据。
- JSON提供了更高的灵活性和可扩展性。
- 在处理简单数据类型和少量数据时,PlayerPrefs可能更便捷。
JsonUtility
在Unity中,要将对象保存为JSON字符串,需要对其进行序列化;同样,要从JSON字符串恢复对象,需要对其进行反序列化。Unity提供了JsonUtility
类来简化这一过程。
- 序列化:使用
JsonUtility.ToJson()
方法将对象转换为JSON字符串。 - 反序列化:使用
JsonUtility.FromJson<T>()
方法将JSON字符串转换回对象。
使用JsonUtility进行存档
定义可序列化的类
在Unity中,只有标记为[Serializable]
的类才能被JsonUtility
序列化。因此,需要为要存档的数据定义可序列化的类。
1 | [ ] |
需要注意,并不是所有数据都可以被正常的序列化:
序列化并保存数据
在Unity中,可以通过以下步骤将PlayerData
对象序列化为JSON字符串,并保存到文件中:
1 | public void SavePlayerData(PlayerData playerData, string fileName) |
读取并反序列化数据
当需要加载存档时,可以从文件中读取JSON字符串,并使用JsonUtility.FromJson<T>()
方法将其反序列化为PlayerData
对象。
1 | public PlayerData LoadPlayerData(string fileName) |
示例
首先创建一个公有的静态类:
1 | using System.IO; |
再其他脚本中使用SaveSystemTutorial
命名空间并实现存储数据函数:
1 | using UnityEngine; |
之后通过输出的文件路径就可以找到我们的存档啦:
注意事项
- 数据安全性:直接以明文形式保存JSON文件可能会带来数据泄露的风险。在需要保护用户数据的情况下,应考虑对JSON数据进行加密。
- 跨平台兼容性:
Application.persistentDataPath
会自动处理不同平台上的文件路径差异,使得存档系统能够跨平台工作。 - 性能考虑:对于大型游戏或频繁存档的场景,需要关注序列化/反序列化操作对性能的影响。
- 错误处理:在文件读写过程中,应添加适当的错误处理逻辑,以应对文件不存在、磁盘空间不足等异常情况。
使用JSON文件而不是PlayerPrefs的原因
前面提到过PlayerPrefs可以存储字符串类型的数据,而JSON数据实际上就是字符串,所以PlayerPrefs实际上也可以存储JSON数据。
不建议用PlayerPrefs存JSON的原因主要有以下几点:
存储效率与灵活性
PlayerPrefs的存储方式相对简单直接,但这也限制了其存储效率和灵活性。当需要存储大量数据时,PlayerPrefs可能会因为频繁地读写磁盘而导致性能下降。此外,由于PlayerPrefs只支持简单的数据类型,因此在处理复杂数据结构时,往往需要将数据转换为字符串进行存储,这在读取时又需要反序列化回原始数据结构,增加了额外的处理步骤和可能的性能开销。相比之下,直接使用JSON文件或其他数据库系统存储复杂数据,可以更加高效和灵活地管理数据。
安全性问题
虽然PlayerPrefs本身在数据安全方面并没有直接的漏洞,但由于其存储的数据类型简单且易于访问,因此如果游戏或应用程序中涉及敏感信息(如用户密码、支付信息等),则不建议使用PlayerPrefs进行存储。此外,由于PlayerPrefs的数据通常存储在用户设备的文件系统中,因此如果设备被恶意软件攻击或用户拥有足够的权限,那么存储在PlayerPrefs中的数据就有可能被非法读取或篡改。对于需要保护用户数据安全的场景,建议使用更加安全的数据存储方案。
可扩展性和可维护性
随着游戏或应用程序的不断发展,可能需要存储的数据量也会不断增加,数据结构也会变得更加复杂。如果一直使用PlayerPrefs来存储这些数据,那么随着数据的增加和数据结构的复杂化,代码的可读性、可维护性和可扩展性都会受到影响。相比之下,使用JSON文件或其他数据库系统来存储数据,可以更加方便地管理数据结构和数据变更,提高代码的可读性、可维护性和可扩展性。
所以,虽然PlayerPrefs在存储简单数据方面具有一定的优势,但在需要处理复杂数据结构、大量数据或敏感信息时,建议使用更加高效、灵活和安全的数据存储方案。对于JSON数据的存储和读取,建议使用Unity内置的JsonUtility类或其他第三方JSON库来实现序列化和反序列化操作,并将数据存储在文件系统或数据库中以便后续管理和使用。
适用范围
联网
- 优秀的网络数据交换载体
- 云存档
本地存储
- 非敏感而需要大量读取的数据,如:Mod数据
- 玩家的偏好设置等
相关资料 -> https://docs.unity3d.com/ScriptReference/JsonUtility.html
尾声
通过本文章的介绍,我们深入了解了Unity中的两种主要存档方式——PlayerPrefs和JSON存档。每种方法都有其独特的优势和适用场景。开发者应根据项目的具体需求、数据复杂度以及目标平台的特性来选择最合适的存档策略。无论是追求简单快捷的PlayerPrefs,还是注重数据复杂性和安全性的JSON存档,都能在Unity中找到实现方法,为玩家带来更加流畅和丰富的游戏体验。
—end—