简介

数据持久化 就是将内存中的数据模型转换为存储模型,以及将存储模型转换为内存中的数据模型的统称,也就是将游戏数据存储到硬盘,硬盘中数据读取到游戏中,即是传统意义上的存档

PlayerPrefs

PlayerPrefs基本方法

PlayerPrefs:是unity提供的用于存储读取玩家数据的公共类

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
//存储相关
//PlayerPrefs的数据存储 类似于键值对存储 一个键对应一个值
//提供了存储3中数据的方法 int float string
//键:string类型
//值:int float string 对应三种API

PlayerPrefs.SetInt("myAge",18);
PlayerPrefs.SetFloat("MyHeight",77.5f);
PlayerPrefs.SetString("myNmae","田鼠浩二");
//直接调用Set相关方法 只会把数据存到内存里
//游戏结束时 Unity会自动把数据存到硬盘中
//游戏非正常结束的话 不会存到硬盘中
//只要调用该方法 马上存储到硬盘中
PlayerPrefs.Save();

//PlayerPrefs的局限性 只能存三种类型的数据
//如果想存别的数据类型 只能降低精度 或者上升精度来存储
bool sex=true;
PlayerPrefs.SetInt("Sex",sex?1:0);
//不同类型存储同一个键名会直接覆盖 直接变成默认值

//读取相关
//注意 运行时 只要set了对应的键值对
//即使没有马上存储save在本地
//也能够读取出信息

//int
int age=PlayerPrefs.GetInt("myAge");
//前提是 如果找不到myAge对应的值 就会返回函数的第二个参数 默认值
age=PlayerPrefs.GetInt("myAge",100);
print(age);

//float
//省略了

//string
//省略了

//第二个参数 默认值 的作用
//就是在得到没有的数据的时候 就可以用它进行基础数据的初始化

//判断数据是否存在
if(PlayerPrefs.HasKey("myName"))
{
print("该数据存在");
}

//删除数据
//删除指定键值对
PlayerPrefs.DeleteKey("myAge");
//删除所有存储的信息 清空
PlayerPrefs.DeleteAll();

不同平台的存储位置

Windows: 存储在Software项下的注册表上

Android: data/data/包名/share_prefs/pkg-name.xml

ios: /Library/Preferences/[应用id].plist

反射补充

Type 用于获取类的所有信息 字段 属性 方法等等

Assembly 用于获取程序集 通过程序获取Type

Activator 用于快速实例化对象

判断一个类型的对象是否可以让另一个类型为自己分配空间

父类装子类

是否可以让某一个类型的对象 为自己分配空间

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
using System;

public class Father
{

}

public class Son:Father
{

}

Type FatherType=typeof(Father);
Type SonType=typeof(Son);
//通过该方法进行判断 判断是否可以通过传入的类型为自己分配空间
if(fatherType.InAssignableFrom(SonType))
{
print("可以装");
Father f=Activator.CreateInstance(SonType) as Father;
print(f);
}
else
{
print("不能装");
}

//通过反射获取泛型类型
List<string> list=new List<string>();
Type listType=list.GetType();

Type [] types = listType.GetGenericArguments();
print(types[0]);

补充

1
2
//命名习惯
keyName_数据类型_字段类型_字段名

数据存储

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
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
public class Player
{
public string name = "田鼠浩二";

public int age = 24;

public float num = 114.514f;

public bool sex = true;

public List<Item> items = new List<Item>(){new Item(1919,114514),new Item(193,615)};

public void showMsg()
{
Debug.Log(this.name);
Debug.Log(this.age);
Debug.Log(this.num);
Debug.Log(this.sex);
}
}

public class Item
{
public int id;

public int count;

public Item()
{

}
public Item(int id,int count)
{
this.id = id;

this.count = count;
}
}


//数据管理类
//利用单例模式防止外部的实例化
public class PlayerDataMgr
{
private static PlayerDataMgr instance=new PlayerDataMgr();

public static PlayerDataMgr Instance
{
get
{
return instance;
}
}

private PlayerDataMgr()
{

}

//保存数据
//判断子类的方法:type.isAssignableFrom(otherType);
//如果otherType继承了type,则会返回true,否则返回false
public void SaveData(System.Object obj,string key)
{
//先通过传入的数据获取类型
//在通过反射获取该类中的字段
Type FieldType = obj.GetType();

FieldInfo[] infos = FieldType.GetFields();

string keyName = "";

//遍历字段
//保存数据
for(int i=0;i<infos.Length;i++)
{
FieldInfo info = infos[i];

keyName = key + "_" + FieldType.Name + "_" + info.FieldType.Name + "_" + info.Name;

SaveValue(info.GetValue(obj),keyName);
}
}

//储存数据的函数
private void SaveValue(System.Object value,string KeyName)
{
//通过传入的数据获取对应的类型
Type FieldType = value.GetType();

//判断类型
if(FieldType==typeof(int))
{
Debug.Log(value+"_int_"+"存储成功");
PlayerPrefs.SetInt(KeyName,(int)value);
}

else if(FieldType == typeof(float))
{
Debug.Log(value + "_float_" + "存储成功");
PlayerPrefs.SetFloat(KeyName,(float)value);
}

else if(FieldType==typeof(string))
{
Debug.Log(value + "_string_" + "存储成功");
PlayerPrefs.SetString(KeyName,value.ToString());
}

else if(FieldType==typeof(bool))
{
Debug.Log(value + "_bool_" + "存储成功");
PlayerPrefs.SetInt(KeyName,(bool)value?1:0);
}

//复杂类型
//通过其共同继承的接口进行判断
//如List都继承了IList接口,Dictionary都继承了IDictionary接口
else 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++;
}
}

else 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++;
}
}

//如果是自定义的类型,递归调用savedata函数反复读取字段进行存储
else
{
SaveData(value,KeyName);
}
}

//和储存数据的思想差不多
//获取某一个类中泛型的方法:type.GetGenericArguments()[index],中括号中的index是第几个泛型类型
public System.Object LoadData(Type type,string key)
{
FieldInfo[] infos = type.GetFields();

System.Object data = Activator.CreateInstance(type);

string keyName = "";

for(int i=0;i<infos.Length;i++)
{
FieldInfo info = infos[i];

keyName = key + "_" + type.Name + "_" + info.FieldType.Name + "_" + info.Name;

info.SetValue(data,LoadValue(info.FieldType,keyName));
}

return data;
}

private System.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 if (FieldType==typeof(bool))
{
return PlayerPrefs.GetInt(KeyName, 0) == 1 ? true : false;
}

else if(typeof(IList).IsAssignableFrom(FieldType))
{
IList list = Activator.CreateInstance(FieldType) as IList;

int count = PlayerPrefs.GetInt(KeyName,0);

for(int i=0;i<count;i++)
{
list.Add(LoadValue(FieldType.GetGenericArguments()[0],KeyName+i));
}

return list;
}

else if(typeof(IDictionary).IsAssignableFrom(FieldType))
{
IDictionary dic = Activator.CreateInstance(FieldType) as IDictionary;

int count = PlayerPrefs.GetInt(KeyName,0);

Type[] vxType = FieldType.GetGenericArguments();

for(int i=0;i<count;i++)
{
dic.Add(LoadValue(vxType[0], KeyName + "_key_" + i), LoadValue(vxType[1],KeyName+"_value_"+i));
}

return dic;
}

else
{
return LoadData(FieldType,KeyName);
}
}

}



public class Test : MonoBehaviour
{
private void Start()
{
PlayerPrefs.DeleteAll();
}

}

XML

JSON

2进制