PlayerPrefs概述
什么是数据持久化
数据持久化就是将内存中的数据模型转换为存储模型,以及将存储模型转换为内存中的数据模型的统称。即将游戏数据存储到硬盘,硬盘中数据读取到游戏中,也就是传统意义上的存盘。
基本方法
PlayerPrefs是什么
是Unity
提供的可以用于存储读取玩家数据的公共类
存储相关PlayerPrefs
的数据存储,类似于键值对存储,一个键对应一个值。
提供了存储3中数据的方法:int、float、string
键:string
类型
值:int float string
对应3种API
PlayerPrefs.SetInt("myAge",18);
PlayerPrefs.SetString("myHeight",177f);
PlayerPrefs.SetString("myName","123");
直接调用Set相关方法,只会把数据存到内存里。当游戏结束时,Unity会自动把数据存到硬盘中。如果游戏不是正常结束的,而是崩溃,数据是不会存到硬盘中的。
只要调用该方法,就会马上存储到硬盘中。
PlayerPrefs.Save();
PlayerPrefs
是有局限性的,它只能存3种类型的数据,如果想要存储别的类型的数据,只能降低精度,或者上升精度来进行存储。
如果不同类型用同一键名进行存储,会进行覆盖。
PlayerPrefs.SetFloat("myAge",20.2f);
读取相关
运行时,只要使用了Set相关方法设置了对应键值对,即使没有马上存储在Save在本地,也可以读取出信息。
//int
int age = PlayerPrefs.GetInt("myAge");
age = PlayerPrefs.GetInt("myAge",100);
//float
float height = PlayerPrefs.GetFloat("myHeight",1000f);
//string
string name = PlayerPrefs.GetString("myName");
第二个参数默认值的作用就是在得到没有的数据的时候,就可以用它来进行基础数据的初始化。
判断数据是否存在。
if(PlayerPrefs.HasKey("myName"))
{
}
删除数据
删除指定键值对
PlayerPrefs.DeleteKey("myAge");
删除所有存储的信息
PlayerPrefs.DeleteAll();
PlayerPrefs存储位置
PlayerPrefs存储的数据存在哪里?
1、Windows
PlayerPrefs
存储在:HKCU\Software\[公司名称]\[产品名称]项下的注册表中
其中公司和产品名称是在“Project Settings
”中设置的名称
查找过程:
运行regedit
HKEY_CURRENT_USER
SOFTWARE
Unity
UnityEditor
公司名称
产品名称
2、Android/data/data/包名/shared_prefs/pkg-name.xml
3、IOS/Library/Preferences/[应用ID].plist
PlayerPrefs数据唯一性
PlayerPrefs中不同数据的唯一性,是由key决定的,不同的key决定了不同的数据。同一项目中,如果不同数据key相同,会造成数据丢失。要保证数据不丢失就要建立一个保证key唯一的规则。
实践小项目
反射知识补充
反射相关内容请移步:C#进阶学习
反射知识回顾
反射3剑客
1、Type
:用于获取类的所有信息、字段、属性、方法等等。
2、Assembly
:用于获取程序集,通过程序集获取Type
。
3、Activator
:用于快速实例化对象。
判断一个类型的对象是否可以让另一个类型为自己分配空间(父类装子类)
public class Father
{
}
public class Son:Father
{
}
Type fatherType = typeof(Father);
Type sonType = typeof(Son);
//判断是否可以通过传入的类型为自己分配空间
if(fatherType.IsAssignableFrom(sonType))
{
}
通过反射获取泛型类型
List<string> list = new List<string>();
Type listType = list.GetType();
Type[] types = listType.GetGenericArguments();//获取到泛型类型string
Dictionary<string,float> dic = new Dictionary<string,float>();
Type dicType = dic.GetType();
types = dicType.GetGenericArguments();
数据管理类的创建
//PlayerPrefs数据管理类,统一管理数据的存储和读取
public class PlayerPrefsDataMgr
{
private static PlayerPrefsDataMgr instance = new PlayerPrefsDataMgr();
public static PlayerPrefsDataMgr Instance
{
get
{
return instance;
}
}
private PlayerPrefsDataMgr(){}//私有构造
//存储数据
public void SaveData(object data,string keyName)
{
}
//读取数据
public object LoadData(Type type,string keyName)
{
return null;
}
}
反射存储数据----常用成员
//存储数据
//通过Type得到传入数据对象的所有的字段,然后结合PlayerPrefs来进行存储
public void SaveData(object data,string keyName)
{
//获取传入数据对象的所有字段
Type dataType = data.GetType();
FieldInfo[] infos = dataTypes.GetFields();
//自己定义一个key规则,进行数据存储
//keyName_数据类型_字段类型_字段名
//遍历这些字段进行数据存储
string saveKeyName = "";
FieldInfo info;
for(int i = 0;i < infos.Length;i++)
{
//字段的类型:info.FieldType.Name
//字段的名字:info.Name
info = infos[i];//得到具体的字段信息
saveKeyName = keyName + "_" + dataType.Name + "_" + info.FieldType.Name + "_" + info.Name;
//封装一个方法进行存储
//获取值:info.GetValue(data)
SaveValue(info.GetValue(data),saveKeyName);
}
}
private void SaveValue(object value,string keyName)
{
//PlayerPrefs只支持3种类型存储
//判断数据类型是什么类型,然后调用具体的方法来存储
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成员
private void SaveValue(object value,string keyName)
{
//PlayerPrefs只支持3种类型存储
//判断数据类型是什么类型,然后调用具体的方法来存储
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);
}
//判断字段是不是IList的子类
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;
}
反射存储数据----Dictionary成员
private void SaveValue(object value,string keyName)
{
//PlayerPrefs只支持3种类型存储
//判断数据类型是什么类型,然后调用具体的方法来存储
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);
}
//判断字段是不是IList的子类
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);
//遍历存储Dic里面的具体值
int index = 0;
foreach(object key in dic)
{
SaveValue(key,keyName + "_key_" + index);
SaveValue(dic[key],keyName + "_value_" + index);
++index;
}
}
反射存储数据----自定义类成员
private void SaveValue(object value,string keyName)
{
//PlayerPrefs只支持3种类型存储
//判断数据类型是什么类型,然后调用具体的方法来存储
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);
}
//判断字段是不是IList的子类
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);
//遍历存储Dic里面的具体值
int index = 0;
foreach(object key in dic)
{
SaveValue(key,keyName + "_key_" + index);
SaveValue(dic[key],keyName + "_value_" + index);
++index;
}
}
else
{
SaveData(value,keyName);
}
反射读取数据----常用成员
public object LoadData(Type type,string keyName)
{
//根据你传入的Type,创建一个对象用于存储数据
object data = Activator.CreateInstance(type);
//得到所有字段
FieldInfo[] infos = type.GetFields();
//用于拼接key的字符串
string loadKeyName = "";
//用于存储单个字段信息的对象
FieldInfo info;
for(int i = 0;i < infos.Length;i++)
{
info = infos[i];
//key的拼接规则一定是和存储时一模一样,这样才能找到对应数据
loadKeyName = keyName + "_" + type.Name + "_" + info.FieldType.Name + "_" + info.Name;
//有key就可以结合,PlayerPrefs来读取数据
info.SetValue(data,LoadValue(info.FieldType,loadKeyName));
}
return data;
}
private object LoadValue(Type fieldType,string keyName)
{
//根据字段类型判断用哪个API读取
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))
{
//自定义存储bool值规则
return PlayerPrefs.GetInt(keyName,0) == 1 ? true : false;
}
return null;
}
反射读取数据----List成员
private object LoadValue(Type fieldType,string keyName)
{
//根据字段类型判断用哪个API读取
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))
{
//自定义存储bool值规则
return PlayerPrefs.GetInt(keyName,0) == 1 ? true : false;
}
else if(typeof(IList).IsAssignableFrom(fieldType))
{
//得到长度
int count = PlayerPrefs.GetInt(keyName,0);
//实例化一个List对象进行赋值
IList list = Activator.CreateInstance(fieldType) as IList;
for(int i = 0;i < count;i++)
{
//得到List中泛型的类型
list.Add(LoadValue(fieldType.GetGenericArguments()[0],keyName + i));
}
return list;
}
return null;
}
反射读取数据----Dic成员
private object LoadValue(Type fieldType,string keyName)
{
//根据字段类型判断用哪个API读取
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))
{
//自定义存储bool值规则
return PlayerPrefs.GetInt(keyName,0) == 1 ? true : false;
}
else if(typeof(IList).IsAssignableFrom(fieldType))
{
//得到长度
int count = PlayerPrefs.GetInt(keyName,0);
//实例化一个List对象进行赋值
IList list = Activator.CreateInstance(fieldType) as IList;
for(int i = 0;i < count;i++)
{
//得到List中泛型的类型
list.Add(LoadValue(fieldType.GetGenericArguments()[0],keyName + i));
}
return list;
}
else if(typeof(IDictionary).IsAsssignableFrom(fieldType))
{
//得到字典的长度
int count = PlayerPrefs.GetInt(keyName,0);
//实例化一个字典对象,用父类装子类
IDictionary dic = Activator.CreateInstance(fieldType);
Type[] kvType = fieldType.GetGenericArguments();
for(int i = 0;i < count;i++)
{
dic.Add(LoadValue(kvType[0],keyName + "_key_" + i),LoadValue(kvType[1],keyName + "_value_" + i));
}
return dic;
}
return null;
}
反射读取数据----自定义类成员
private object LoadValue(Type fieldType,string keyName)
{
//根据字段类型判断用哪个API读取
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))
{
//自定义存储bool值规则
return PlayerPrefs.GetInt(keyName,0) == 1 ? true : false;
}
else if(typeof(IList).IsAssignableFrom(fieldType))
{
//得到长度
int count = PlayerPrefs.GetInt(keyName,0);
//实例化一个List对象进行赋值
IList list = Activator.CreateInstance(fieldType) as IList;
for(int i = 0;i < count;i++)
{
//得到List中泛型的类型
list.Add(LoadValue(fieldType.GetGenericArguments()[0],keyName + i));
}
return list;
}
else if(typeof(IDictionary).IsAsssignableFrom(fieldType))
{
//得到字典的长度
int count = PlayerPrefs.GetInt(keyName,0);
//实例化一个字典对象,用父类装子类
IDictionary dic = Activator.CreateInstance(fieldType);
Type[] kvType = fieldType.GetGenericArguments();
for(int i = 0;i < count;i++)
{
dic.Add(LoadValue(kvType[0],keyName + "_key_" + i),LoadValue(kvType[1],keyName + "_value_" + i));
}
return dic;
}
else
{
return LoadData(fieldType,keyName);
}
return null;
}
加密思路
找不到
1、把存在硬盘上的内容放在一个不容易找到的地方
2、多层文件夹包裹
3、名字辨识度低
但是对于PlayerPrefs不太适用,因为位置已经固定了,改不了。
看不懂
让数据的key和value让别人看不懂,俗称加密,为key和value加密。
解不出
不让别人获取到你加密的规则。
注意: 单机游戏加密只是提高别人修改你数据的门槛,只要别人获取到你的源代码,知道你的加密规则,一切都没有任何意义。但是对于一般玩家来说几乎是不可能的事情。文章来源:https://www.toymoban.com/news/detail-827933.html
生成资源包
文章来源地址https://www.toymoban.com/news/detail-827933.html
到了这里,关于Unity数据持久化之PlayerPrefs的文章就介绍完了。如果您还想了解更多内容,请在右上角搜索TOY模板网以前的文章或继续浏览下面的相关文章,希望大家以后多多支持TOY模板网!