知识点来源:总结人间自有韬哥在, 唐老狮,豆包
1.PlayerPrefs
1.1.PlayerPrefs是什么
PlayerPrefs是Unity提供的可以用于存储读取玩家数据的公共类。
1.2.PlayerPrefs的数据存储
PlayerPrefs的数据存储类似于键值对存储,一个键对应一个值。
提供了存储3种数据的方法 int float string。
键: string类型。
值:int float string 对应3种API。
1.3.PlayerPrefs的核心方法
分类 |
方法 |
说明 |
存储数据 |
SetInt(key, value) |
设置由key标识的整数值 |
存储数据 |
SetFloat(key, value) |
设置由key标识的浮点数值 |
存储数据 |
SetString(key, value) |
设置由key标识的字符串值 |
存储数据 |
Save() |
将所有修改的偏好写入磁盘 |
读取数据 |
GetInt(key, defaultValue) |
返回偏好设置文件中与key对应的值(如果存在),若不存在则返回defaultValue |
读取数据 |
GetFloat(key, defaultValue) |
返回偏好设置文件中与key对应的值(如果存在),若不存在则返回defaultValue |
读取数据 |
GetString(key, defaultValue) |
返回偏好设置文件中与key对应的值(如果存在),若不存在则返回defaultValue |
判断数据是否存在 |
HasKey(key) |
如果key在偏好中存在,则返回true |
删除数据 |
DeleteKey(key) |
从偏好中删除key及其对应值 |
删除数据 |
DeleteAll() |
从偏好中删除所有键和值 |
2.XML
2.1.读取 XML 文件
操作 |
关键类/方法 |
代码示例 |
说明 |
创建文档对象 |
XmlDocument 构造函数 |
XmlDocument xmlDoc = new XmlDocument(); |
初始化 XML 文档对象 |
加载字符串 XML |
XmlDocument.LoadXml() |
xmlDoc.LoadXml(textAsset.text); |
从 Resources 加载文本字符串(如 TextAsset ) |
加载路径 XML |
XmlDocument.Load() |
xmlDoc.Load(Application.streamingAssetsPath + "/TestXml.xml"); |
从 StreamingAssets 加载文件路径(打包后可用) |
获取根节点 |
XmlDocument.SelectSingleNode() |
XmlNode root = xmlDoc.SelectSingleNode("Root"); |
通过 XPath 路径获取根节点(如 "Root" ) |
获取子节点内容 |
XmlNode.InnerText |
string name = root.SelectSingleNode("name").InnerText; |
获取节点包裹的文本(如 <name>林文韬</name> ) |
获取属性值(中括号) |
XmlNode.Attributes["属性名"].Value |
string id = node.Attributes["id"].Value; |
通过属性名直接获取属性值(如 id="1" ) |
获取属性值(方法) |
XmlNode.Attributes.GetNamedItem() |
string num = node.Attributes.GetNamedItem("num").Value; |
通过方法获取属性值(适合动态属性名) |
获取同名节点列表 |
XmlNode.SelectNodes() |
XmlNodeList friends = root.SelectNodes("Friend"); |
获取指定名称的所有子节点(如多个 <Friend> ) |
遍历节点列表 |
foreach /for 循环 |
foreach (XmlNode friend in friends) { Debug.Log(friend.SelectSingleNode("name").InnerText);} |
|
2.2.存储 XML 文件
步骤 |
关键类/方法 |
代码示例 |
说明 |
选择存储路径 |
Application.persistentDataPath |
string path = Application.persistentDataPath + "/Player.xml"; |
唯一全平台可读可写路径(推荐) |
创建文档对象 |
XmlDocument 构造函数 |
XmlDocument xmlDoc = new XmlDocument(); |
初始化空文档 |
添加版本声明 |
XmlDocument.CreateXmlDeclaration() |
XmlDeclaration dec = xmlDoc.CreateXmlDeclaration("1.0", "UTF-8", ""); |
添加 XML 头部声明(<?xml version="1.0" encoding="UTF-8"?> ) |
创建根节点 |
XmlDocument.CreateElement() |
XmlElement root = xmlDoc.CreateElement("Root"); |
创建根节点(如 <Root> ) |
添加子节点及属性 |
XmlElement.SetAttribute() |
|
|
保存文件 |
XmlDocument.Save() |
xmlDoc.Save(path); |
写入磁盘(需权限,persistentDataPath 自动获得) |
2.3.修改 XML 文件
操作 |
关键类/方法 |
代码示例 |
说明 |
判断文件存在 |
File.Exists() |
if (File.Exists(path)) { ... } |
避免空文件异常 |
加载现有文件 |
XmlDocument.Load() |
XmlDocument xmlDoc = new XmlDocument(); xmlDoc.Load(path); |
读取已存在的 XML 文件 |
移除节点 |
XmlNode.RemoveChild() |
|
|
保存修改 |
XmlDocument.Save() |
xmlDoc.Save(path); |
覆盖原文件(谨慎操作,建议备份) |
2.4.路径选择对比
路径 |
可读 |
可写 |
打包后访问 |
适用场景 |
Resources |
✔️ |
❌ |
❌(加密) |
只读配置(如初始 XML) |
StreamingAssets |
✔️ |
❌(部分平台) |
✔️ |
只读资源(如内置 XML) |
Application.persistentDataPath |
✔️ |
✔️ |
✔️ |
玩家存档(推荐) |
Application.dataPath |
✔️ |
❌ |
❌ |
编辑器调试(勿用于发布) |
3.Json
3.1.JsonUtility
操作 |
关键类/方法 |
代码示例 |
说明 |
文件存读字符串 |
File.WriteAllText |
File.WriteAllText(Application.persistentDataPath + "/Test.json", "存储内容"); |
将字符串存储到指定路径的文件中,路径需确保文件夹存在 |
文件存读字符串 |
File.ReadAllText |
string str = File.ReadAllText(Application.persistentDataPath + "/Test.json"); |
从指定路径的文件中读取字符串 |
序列化对象 |
JsonUtility.ToJson |
string jsonStr = JsonUtility.ToJson(t); |
将类对象序列化为JSON格式的字符串 |
反序列化对象 |
JsonUtility.FromJson |
MrTao t2 = JsonUtility.FromJson(jsonStr, typeof(MrTao)) as MrTao; |
将JSON字符串反序列化为类对象 |
3.2.LitJson
3.2.1.核心方法对比
操作 |
JsonUtility |
LitJson |
代码示例 |
序列化对象 |
JsonUtility.ToJson(对象) |
JsonMapper.ToJson(对象) |
json = JsonMapper.ToJson(player); |
|
|
|
|
反序列化对象 |
JsonUtility.FromJson<T>(json) |
JsonMapper.ToObject<T>(json) |
PlayerInfo player = JsonMapper.ToObject(json); // LitJson |
|
|
|
|
序列化集合/数组 |
需包裹对象(如 [RoleData { List<Role> }] ) |
直接支持 List<T> /T[] |
List roles = JsonMapper.ToObject<List>(json); // LitJson |
|
|
|
|
序列化字典 |
不支持(需转换为类) |
支持(键建议为 string) |
Dictionary<string, int> dic = JsonMapper.ToObject<Dictionary<string, int>>(json); // LitJson |
|
|
|
|
3.2.2关键注意事项
场景 |
JsonUtility |
LitJson |
说明 |
特性要求 |
必须 [Serializable] + [SerializeField] (私有成员) |
无强制特性(私有成员无法序列化) |
LitJson 仅序列化公共字段 |
私有成员 |
需 [SerializeField] 才能序列化 |
忽略私有成员(无法序列化) |
私有字段直接跳过 |
字典支持 |
不支持(需手动转换) |
支持(键为 string 最佳) |
Json 标准键为字符串,数值键会被转为字符串 |
集合/数组 |
需包裹对象 |
直接支持 List<T> /T[] |
无需额外包装,与 Json 数组直接对应 |
null 处理 |
存储为默认值(如 0 /"" ) |
保留 null (需类成员允许) |
反序列化 null 时,类成员保持 null (需无参构造) |
构造函数 |
无要求 |
必须有无参构造函数(否则反序列化报错) |
复杂对象需提供无参构造(如 public Class() {} ) |
编码格式 |
必须 UTF-8 |
必须 UTF-8 |
非 UTF-8 编码会导致乱码或加载失败 |
中文序列化 |
自动转义(如 \u4E2D ) |
保留原始中文(无需转义) |
LitJson 默认保留 Unicode 字符,可读性更好 |
4.二进制
4.1.数据类型 ↔ 字节数组转换
操作 |
类/方法 |
代码示例 |
说明 |
数值转字节 |
BitConverter.GetBytes() |
byte[] bytes = BitConverter.GetBytes(999); |
支持 int /float /bool 等,默认小端序(低位在前) |
字节转数值 |
BitConverter.ToXX() |
int i = BitConverter.ToInt32(bytes, 0); |
需指定起始索引,确保字节长度匹配(如 int 需 4 字节) |
字符串转字节(UTF-8) |
Encoding.UTF8.GetBytes() |
byte[] strBytes = Encoding.UTF8.GetBytes("林文韬"); |
中文需用 UTF-8,避免乱码 |
字节转字符串(UTF-8) |
Encoding.UTF8.GetString() |
string str = Encoding.UTF8.GetString(strBytes); |
支持指定范围:GetString(bytes, startIndex, length) |
注意事项 |
- 小端序(低位在前) - 字符串需先存长度(如 int 表示字节数) |
网络传输常用大端序,需手动反转字节顺序 |
|
4.2.文件操作(File
类)
操作 |
方法 |
代码示例 |
说明 |
创建文件 |
File.Create(path) |
FileStream fs = File.Create(Application.persistentDataPath + "/data.bin"); |
创建空文件,路径需含文件名 |
写入字节 |
File.WriteAllBytes() |
File.WriteAllBytes(path, bytes); |
覆盖写入,适合小文件 |
写入文本 |
File.WriteAllText() |
File.WriteAllText(path, "文本内容", Encoding.UTF8); |
自动处理编码,默认 UTF-8 |
读取字节 |
File.ReadAllBytes() |
byte[] data = File.ReadAllBytes(path); |
一次性读取,适合小文件 |
判断存在 |
File.Exists(path) |
if (File.Exists(path)) { ... } |
避免操作不存在的文件 |
删除文件 |
File.Delete(path) |
File.Delete(path); |
不可恢复,谨慎操作 |
复制文件 |
File.Copy(src, dest, true) |
File.Copy(oldPath, newPath, true); |
第三个参数 true 覆盖已存在文件 |
适用场景 |
简单文件读写(非流式) |
适合配置文件、小数据存储 |
|
4.3.文件流操作(FileStream
类)
操作 |
方法 |
代码示例 |
说明 |
打开/创建流 |
new FileStream(...) |
using (FileStream fs = new FileStream(path, FileMode.OpenOrCreate, FileAccess.ReadWrite)) {} |
FileMode.OpenOrCreate 常用,using 自动释放资源 |
写入字节 |
fs.Write(bytes, 0, len) |
fs.Write(BitConverter.GetBytes(strBytes.Length), 0, 4); // 先写字符串长度fs.Write(strBytes, 0, strBytes.Length); // 再写字符串 |
按顺序写入,需自定义协议(如先长度后内容) |
读取字节 |
fs.Read(buffer, 0, len) |
byte[] buffer = new byte[4];fs.Read(buffer, 0, 4); // 读 int:int len = BitConverter.ToInt32(buffer, 0);buffer = new byte[len];fs.Read(buffer, 0, len); // 读字符串` |
严格按写入顺序读取,避免索引错位 |
刷新缓冲区 |
fs.Flush() |
fs.Flush(); // 强制写入磁盘 |
防止数据丢失(如程序崩溃前) |
关闭流 |
fs.Close() / using |
fs.Close(); // 或用 using 块 |
未关闭可能导致文件被占用,无法删除 |
适用场景 |
流式读写(大文件/复杂结构) |
适合玩家存档、二进制日志 |
|
4.4.文件夹操作(Directory
类)
操作 |
方法 |
代码示例 |
说明 |
创建文件夹 |
Directory.CreateDirectory(path) |
Directory.CreateDirectory(Application.persistentDataPath + "/logs"); |
递归创建多级目录(如 /a/b/c ) |
删除文件夹 |
Directory.Delete(path, true) |
Directory.Delete(folderPath, true); // 强制删除非空目录 |
第二个参数 true 递归删除子文件/夹 |
判断存在 |
Directory.Exists(path) |
if (Directory.Exists(folderPath)) { ... } |
检查目录是否存在 |
获取子文件 |
Directory.GetFiles(folder) |
string[] files = Directory.GetFiles(folder, "*.bin", SearchOption.AllDirectories); |
支持通配符(如 *.bin ),SearchOption 控制是否搜索子目录 |
移动文件夹 |
Directory.Move(oldPath, newPath) |
Directory.Move("旧路径", "新路径"); |
目标路径不可存在同名目录 |
目录信息 |
DirectoryInfo |
DirectoryInfo di = new DirectoryInfo(folderPath);print(di.FullName); // 全路径print(di.CreationTime); // 创建时间 |
用于获取详细信息(创建时间、大小等) |
适用场景 |
目录管理、文件组织 |
适合资源分类、存档管理 |
|
4.5.核心注意事项
场景 |
陷阱 |
解决方案 |
字节顺序 |
小端序(C# 默认)与大端序冲突 |
网络传输时手动反转字节(如 Array.Reverse(bytes) ) |
字符串存储 |
未存长度导致读取失败 |
先存 int 表示字节长度,再存字符串字节数组 |
文件流未关闭 |
数据丢失或文件被占用 |
强制使用 using 块包裹 FileStream |
编码格式 |
中文乱码(非 UTF-8) |
统一使用 Encoding.UTF8 ,避免 ANSI 等本地编码 |
文件夹删除 |
非空目录删除失败 |
传入 true 递归删除(Directory.Delete(path, true) ) |
路径拼接 |
平台路径格式错误(如 \ vs / ) |
使用 Path.Combine() (如 Path.Combine(folder, "file.bin") ) |
`