- 游戏数据在内存和硬盘间的双向传输过程
- 存储方向:游戏运行时内存数据 → 硬盘存储(传统意义上的”存盘”)
- 读取方向:硬盘存储数据 → 游戏内存加载
作用: 保存进度
PlayerPrefs
存储
类似于键值对存储 提供了存储3种数据的方法 int float string
键string 值int float string对应三种API
PlayerPrefs.SetInt(“myAge”, 18);
PlayerPrefs.SetFloat(“myHeight”, 177.5f);
PlayerPrefs.SetString(“myName”, “John”);
直接调用Set相关方法 只会把数据存到内存里
游戏结束时 Unity会自动把数据存到硬盘中
游戏不是正常结束 数据不会存硬盘
PlayerPrefs.Save();
调用这个方法就会马上储存在硬盘当中
局限性:只能存三种类型的数据
如果想要存储别的类型的数据 只能降低精度 或者上升精度来进行储存
不做加密的话可以被直接修改
如果不同类型用同一键名进行存储 会进行覆盖
读取
int
如果键没有值或者值被覆盖了会默认为0
int age = PlayerPrefs.GetInt(“myAge”);
如果找不到键对应的值,那么就会返回函数的第二个参数 作为默认值 其他两个的Get方法也相同
age = PlayerPrefs.GetInt(“myAge”, 100);
float
float height = PlayerPrefs.GetFloat(“myHeight”, 1000f);
string
string name = PlayerPrefs.GetString(“myName”);
判断数据是否存在
PlayerPrefs.HasKey(“myName”);
删除数据
删除指定键值对
PlayerPrefs.DeleteKey(“myAge”);
删除所有存储的信息
PlayerPrefs.DeleteAll();
数据管理类创建
单例模式:
优势:避免重复创建对象,保证数据操作的一致性
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33
| public class PlayerPrefsDataMgr { private static PlayerPrefsDataMgr instance = new PlayerPrefsDataMgr(); public static PlayerPrefsDataMgr Instance { get { return instance; } } private PlayerPrefsDateMgr() { } public void SaveData(object data, string keyname) { } public object LoadData( Type type, string keyname) { return null; } }
|
结合反射基础数据类型存储
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52
| public void SaveData(object data, string keyName) { Type dataType = data.GetType(); FieldInfo[] infos = dataType.GetFields(); string saveKeyName = ""; FieldInfo info; for(int i = 0; i < info.length; i ++) { info = infos[i]; saveKeyName = keyName + "_" + dataType.Name + "_" info.FieldType.Name + "_" + info.Name; SaveValue(info.GetValue(data), saveKeyName); } private void SaveValue(object value, string keyName) { Type fieldType = value.GetType(); if(fieldType == typeof(int)) { PlayerPrefs.SetInt(KeyName, (int)value); } else if(fieldType == typeof(float)) { PlayerPrefs.SetFloat(KeyName, (float)value); } else if(fieldType == typeof(string)) { PlayerPrefs.SetString(KeyName, value.ToString()); } else if(fieldType == typeof(bool)) { PlayerPrefs.SetInt(KeyName, (bool)value ? 1 : 0); } } }
|
结合反射List数据类型存储
使用typeof(IList).IsAssignableFrom()判断是否为List类型
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16
| if(typeof(Ilist).IsAssignableFrom(fieldType)) { Ilist list = value as Ilist; PlayerPrefs.SetInt(keyName, list.Count); int index = 0; foreach(object obj in list) { SaveValue(obj, keyName + index); ++ index; } }
|
结合反射Dictionary数据类型存储
思路和list差不多
typeof(IDictionary).IsAssignableFrom(fieldType)判断是不是dictionary
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15
| if(typeof(IDictionary).IsAssignableFrom(fieldType)) { IDictionary dic = value as IDictionary; PlayerPrefs.SetInt(keyName, dic.Count); int index = 0; foreach(object key in dic.Keys) { SaveValue(key, keyName + "_key_" + index); SaveValue(dic[key], keyName + "_value_" + index); ++index; } }
|
结合反射自定义类成员存储
不是递归 重新走一遍存储
1 2 3
| else { SaveData(value, keyname); }
|
结合反射读取常用数据类型
外部代码:
1
| PlayerInfo p2 = PlayerPrefsDataMgr.Instance.LoadData(typeof(PlayerInfo), "Player1") as PlayerInfo;
|
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24
| public object LoadData( Type type, string keyname) { object data = Activator.CreateInstance(type); FieldInfo[] infos = type.GetFields(); string loadKeyName = ""; FieldInfo info; for(int i = 0; i < infos.length; i ++) { info = infos[i]; loadKeyName = keyName + "_" + type.Name + "_" + info.FieldType.Name + "_" + info.Name; info.SetValue(data, LoadValue(info.FieldType, loadKeyName)); } return data; }
|
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20
| private object LoadValue(Type fieldType, string keyname) { if(fieldType == typeof(int)) { return PlayerPrefs.GetInt(keyName, 0); } else if(fieldType == typeof(float)) { return PlayerPrefs.Getfloat(keyName, 0); } else if(fieldType == typeof(string)) { return PlayerPrefs.GetString(keyName, ""); } else else if(fieldType == typeof(bool)) { return PlayerPrefs.GetInt(keyName, 0) == 1 ? true : false; } }
|
结合反射读取List数据类型
读取流程类似存储倒过来
1 2 3 4 5 6 7 8 9 10 11 12 13
| else if(typeof(IList).IsAssignableFrom(fieldType)) { int count = PlayerPrefs.GetInt(keyName, 0); Ilist list = Activator.CreateInstance(fieldType) as list; for(int i = 0; i < count; i ++) { list.Add(LoadValue(fieldType.GetGenericArguments()[0], keyName + i)); } return list; }
|
结合反射读取Dictionary数据类型
//流程与list差不多
1 2 3 4 5 6 7 8 9 10 11 12 13
| else if(typeof(IDictionary).IsAssignableFrom(fieldType)) { int count = PlayerPrefs.GetInt(keyName, 0); IDictionary dic = Activator.CreateInstance(firldType); Type[] kvType = field.GetGenericArguments(); for(int i = 0; i < count; i ++) { dic.Add(LoadValue(kvType[0], keyName + "_key_" + i), LoadValue(kvType[1], keyName + "_key_" + i)); } return dic; }
|
结合反射读取自定义类数据类型
1 2 3 4
| else { return LoadData(fieldType, keyName); }
|
加密思路
让Key和Value让别人看不懂
但是让别人获取到你的源码 知道了你的加密规则之后一切没有意义