提示:文章写完后,目录可以自动生成,如何生成可参考右边的帮助文档
文章目录
- 前言
- [什么是 Unity 持久化数据?](#什么是 Unity 持久化数据?)
-
- 1.游戏内存数据的特性
- [2. 持久化数据定义](#2. 持久化数据定义)
- [3. 哪些数据需要做持久化?](#3. 哪些数据需要做持久化?)
- [4. Unity 持久化存储通用规范](#4. Unity 持久化存储通用规范)
- [一、PlayerPrefs(Unity 原生自带这套存储)](#一、PlayerPrefs(Unity 原生自带这套存储))
- [二、Json 序列化存档](#二、Json 序列化存档)
- [三、XML 序列化存档](#三、XML 序列化存档)
- [四、二进制 Binary 存档](#四、二进制 Binary 存档)
- 避坑
前言
本章介绍Unity 数据持久化全解:PlayerPrefs、XML、JSON、二进制四种方案。
游戏开发中数据持久化是刚需:玩家等级、金币、皮肤、关卡进度、设置参数都需要本地保存,重启游戏不丢失。
Unity 主流 4 种本地持久化方案:
PlayerPrefs (Unity 原生轻量键值对)
Json (可读性高,开发调试首选)
XML (老式配置存档,复杂层级文本存储)
二进制 Binary (加密、体积最小,大型游戏商用首选)
本文会逐一讲解原理、完整代码、适用场景、优缺点对比,全部代码可直接复制到 Unity 运行,文末附带选型对照表。
提示:以下是本篇文章正文内容,下面案例可供参考
什么是 Unity 持久化数据?
1.游戏内存数据的特性
Unity 游戏运行时,我们创建的角色、金币、关卡进度、音效设置等所有变量、类对象,全部存储在程序内存(RAM) 中。
内存数据有致命特点:
游戏关闭、闪退、手机切后台杀进程后,内存会直接清空,所有数据全部丢失;
切换场景时,不做特殊处理的临时数据也会被销毁。
举个例子:你游戏里通关拿到 1000 金币,直接关闭游戏再重新打开,金币归零 ------ 这就是非持久化临时内存数据。
2. 持久化数据定义
Unity 持久化数据,就是将游戏运行中产生的玩家数据,从内存写入本地磁盘文件 / 系统注册表,实现游戏关闭后数据不丢失,下次启动游戏可以重新读取还原的存储技术。
简单说:把数据存在电脑 / 手机硬盘里,而不是只存在游戏运行内存里,做到重启游戏数据保留。
3. 哪些数据需要做持久化?
玩家存档类:等级、金币、钻石、装备、已解锁关卡、角色皮肤;
游戏设置类:背景音乐音量、画质等级、帧率开关、触控灵敏度;
进度缓存类:剧情阅读进度、新手引导完成状态、本地离线记录;
配置缓存:离线本地活动数据、本地排行榜。
4. Unity 持久化存储通用规范
所有本地存档,官方统一推荐存储在 Application.persistentDataPath 路径:
Windows:C 盘用户目录下的隐藏文件夹,不会和游戏安装包混在一起;
Android/IOS:系统分配专属沙盒目录,拥有完整读写权限,游戏更新不会删除;
禁止使用StreamingAssets(只读,无法写入)、Application.dataPath(打包后无写入权限)存储存档。
一、PlayerPrefs(Unity 原生自带这套存储)
1.诞生初衷:解决超轻量简单数值存储需求
Unity 早期就内置PlayerPrefs,专门用来存储少量、简单的基础类型数据(int/string/float),不用引入任何文件流、序列化代码,零学习成本。
2.选用它的理由
无需创建文件、不需要处理路径、不用引入 IO 命名空间,几行代码就能读写音量、开关这类简单配置;
跨平台底层封装完成,Windows、安卓、IOS、主机全部兼容,不用适配不同平台文件系统;
读写速度极快,适合游戏启动瞬间快速读取设置。
3.不适合复杂存档的原因
底层是系统注册表 / Plist 键值数据库,不支持数组、自定义玩家类、复杂集合,大量数据存储会臃肿混乱,且明文极易被修改。
代码实列:
csharp
using UnityEngine;
/// <summary>
/// 轻量键值对存档工具,存储游戏设置、少量数值
/// </summary>
public static class PlayerPrefsManager
{
#region 保存
public static void SaveInt(string key, int value)
{
PlayerPrefs.SetInt(key, value);
PlayerPrefs.Save();
}
public static void SaveString(string key, string value)
{
PlayerPrefs.SetString(key, value);
PlayerPrefs.Save();
}
public static void SaveFloat(string key, float value)
{
PlayerPrefs.SetFloat(key, value);
PlayerPrefs.Save();
}
#endregion
#region 读取
public static int GetInt(string key, int defaultValue = 0)
{
return PlayerPrefs.GetInt(key, defaultValue);
}
public static string GetString(string key, string defaultValue = "")
{
return PlayerPrefs.GetString(key, defaultValue);
}
public static float GetFloat(string key, float defaultValue = 0f)
{
return PlayerPrefs.GetFloat(key, defaultValue);
}
#endregion
// 清空全部本地数据
public static void DeleteAllData()
{
PlayerPrefs.DeleteAll();
PlayerPrefs.Save();
}
// 删除单个key数据
public static void DeleteSingleKey(string key)
{
PlayerPrefs.DeleteKey(key);
PlayerPrefs.Save();
}
}
二、Json 序列化存档
1.诞生初衷:平衡开发效率与数据可读性
Json 是全球通用轻量文本格式,Unity 内置JsonUtility开箱即用,不需要额外第三方插件,完美弥补 PlayerPrefs 无法存储自定义类、数组的短板。
2.选用它的理由
存档文件是纯文本,记事本就能打开查看、调试,开发阶段排查存档 BUG 极其方便;
支持完整自定义实体类、数组、列表,一套实体类就能存玩家所有存档信息;
格式通用,可以和后端服务器互通 Json 数据,本地存档和网络接口数据结构统一;
代码简洁,泛型工具类一次封装,全项目通用。
3.短板存在的原因
明文文本无加密,玩家手动修改文件就能篡改金币、等级;文本格式占用体积比二进制更大,海量数据读写性能一般。
代码示例:
csharp
using UnityEngine;
using System.IO;
/// <summary>
/// Json文本序列化存档工具,调试友好,独立游戏首选
/// </summary>
public static class JsonSaveManager
{
// 拼接完整存档路径
private static string GetFullPath(string fileName)
{
return Path.Combine(Application.persistentDataPath, $"{fileName}.json");
}
/// <summary>
/// 保存自定义类对象到本地Json文件
/// </summary>
/// <typeparam name="T">数据实体类(必须加[Serializable])</typeparam>
/// <param name="data">存档数据对象</param>
/// <param name="fileName">存档文件名</param>
public static void SaveData<T>(T data, string fileName = "SaveData")
{
string jsonText = JsonUtility.ToJson(data, prettyPrint: true);
string savePath = GetFullPath(fileName);
File.WriteAllText(savePath, jsonText);
Debug.Log($"Json存档路径:{savePath}");
}
/// <summary>
/// 读取Json文件还原数据对象
/// </summary>
public static T LoadData<T>(string fileName = "SaveData")
{
string path = GetFullPath(fileName);
if (!File.Exists(path))
{
Debug.LogWarning($"Json文件不存在:{path}");
return default;
}
string json = File.ReadAllText(path);
T data = JsonUtility.FromJson<T>(json);
return data;
}
/// <summary>
/// 删除本地存档文件
/// </summary>
public static void DeleteSave(string fileName = "SaveData")
{
string path = GetFullPath(fileName);
if (File.Exists(path))
{
File.Delete(path);
}
}
}
三、XML 序列化存档
1.诞生初衷:早期标准结构化文本存储方案
Json 普及前,XML 是行业通用分层数据格式,C# 原生提供完整 XML 序列化库,适合多层级嵌套的复杂配置文件,早年端游、编辑器配置全部基于 XML。
2.选用它的理由
严格层级标签,对树状、多层级数据描述能力更强;
老旧 Unity 项目、老后端接口只支持 XML 交互,只能选用该方案;
自带标准校验语法,配置文件格式错误能精准捕获。
3.现在逐步被 Json 替代的原因
冗余闭合标签多,相同数据下文件体积比 Json 大 30% 以上;代码书写繁琐,可读性差,现代游戏开发基本不再作为首选存档方案。
代码示例:
csharp
using UnityEngine;
using System.IO;
using System.Xml.Serialization;
/// <summary>
/// XML序列化存档工具,老旧项目/多层级配置使用
/// </summary>
public static class XmlSaveManager
{
private static string GetFullPath(string fileName)
{
return Path.Combine(Application.persistentDataPath, $"{fileName}.xml");
}
public static void SaveData<T>(T data, string fileName = "SaveData")
{
string path = GetFullPath(fileName);
using (FileStream fs = new FileStream(path, FileMode.Create))
{
XmlSerializer xmlSerializer = new XmlSerializer(typeof(T));
xmlSerializer.Serialize(fs, data);
}
Debug.Log($"XML存档路径:{path}");
}
public static T LoadData<T>(string fileName = "SaveData")
{
string path = GetFullPath(fileName);
if (!File.Exists(path))
{
Debug.LogWarning($"XML文件不存在:{path}");
return default;
}
using (FileStream fs = new FileStream(path, FileMode.Open))
{
XmlSerializer xmlSerializer = new XmlSerializer(typeof(T));
return (T)xmlSerializer.Deserialize(fs);
}
}
public static void DeleteSave(string fileName = "SaveData")
{
string path = GetFullPath(fileName);
if (File.Exists(path))
{
File.Delete(path);
}
}
}
四、二进制 Binary 存档
1.诞生初衷:解决明文存档篡改、大容量数据、性能三大商用痛点
Json、XML 都是明文文本,玩家轻易就能修改存档数值破坏游戏平衡;二进制直接将对象转为字节流存储,文件打开全是乱码,天然防篡改,同时文件体积最小、读写性能最强。
2.选用它的理由
安全:二进制字节文件无明文,普通玩家无法直接修改存档,配合 MD5/AES 加密后几乎无法破解;
性能:字节流读写速度远高于文本读写,存储海量角色、关卡数据不会卡顿;
体积优势:相同玩家数据,二进制文件体积只有 Json 的 1/3,节省手机本地存储空间;
完整支持所有 C# 复杂类型,字典、多维数组、嵌套类都能序列化。
3.开发劣势成因
文件无法可视化查看,调试存档数据极麻烦;原生BinaryFormatter存在版本兼容问题,更新游戏新增字段后旧存档会读取失败,需要额外做版本兼容逻辑。
代码示例:
csharp
using UnityEngine;
using System.IO;
using System.Runtime.Serialization.Formatters.Binary;
/// <summary>
/// 二进制序列化存档工具,商用游戏防篡改,体积最小
/// </summary>
public static class BinarySaveManager
{
private static string GetFullPath(string fileName)
{
return Path.Combine(Application.persistentDataPath, $"{fileName}.dat");
}
public static void SaveData<T>(T data, string fileName = "SaveData")
{
string path = GetFullPath(fileName);
BinaryFormatter bf = new BinaryFormatter();
using (FileStream fs = new FileStream(path, FileMode.Create))
{
bf.Serialize(fs, data);
}
Debug.Log($"二进制存档路径:{path}");
}
public static T LoadData<T>(string fileName = "SaveData")
{
string path = GetFullPath(fileName);
if (!File.Exists(path))
{
Debug.LogWarning($"二进制文件不存在:{path}");
return default;
}
BinaryFormatter bf = new BinaryFormatter();
using (FileStream fs = new FileStream(path, FileMode.Open))
{
return (T)bf.Deserialize(fs);
}
}
public static void DeleteSave(string fileName = "SaveData")
{
string path = GetFullPath(fileName);
if (File.Exists(path))
{
File.Delete(path);
}
}
}
避坑
1.Json / 二进制序列化实体类必须加 Serializable,否则读取数据全部为空
2.不要使用 Application.dataPath 做存档路径,打包后无写入权限
3.PlayerPrefs 不支持数组、自定义类,复杂存档不要选用
4.BinaryFormatter 高版本.NET 存在安全警告,商用项目推荐 Protobuf 替代
5.Xml 序列化不支持私有字段,所有存档变量必须写 public