1、数据持久化
文章目录
- 1、数据持久化
- PlayerPrefs基本方法
- 1、PlayerPrefs概念
- 2、存储相关
- 3、读取相关
- 4、删除数据
- 思考 信息的存储和读取
- PlayerPrefs存储位置
- 1、PlayerPrefs存储的数据在哪个位置
- 2、PlayerPrefs 数据唯一性
- 思考 排行榜功能
- 2、Playerprefs实践
- 1、必备知识点-反射知识小补充
- 2、需求分析 Playerprefs数据管理类创建
- 3、反射存储数据-常用成员、List成员、Dic成员、自定义类成员
- 4、反射读取数据-常用成员、List成员、Dic成员、自定义类成员
- 4、加密思路
- 5、生成资源包
概念
将【变量数据】在内存和硬盘之间的存储或读取
内容
PlayerPrefs的基本方法
PlayerPrefs在不同平台的存储位置
利用反射结合PlayerPrets制作通用存诸工具
PlayerPrefs基本方法
1、PlayerPrefs概念
是unity提供的可以用于存储读取玩家数据的公共类
2、存储相关
PlaverPrefs的数据存储类似于键值对存储一个键对应一个值
提供了存储3种数据的方法int float string
键:string类型
值:int float string对应3种API
PlayerPrefs.SetInt("myAge", 18);
PlayerPrefs.SetFloat("myHeight", 183.5f);
PlayerPrefs.SetString("myName", "姓名");
PlayerPrefs.Save();
bool sex = true; //sex若为true存1,sex若为flase则存1
PlayerPrefs.SetInt("sex", sex ? 1 : 0);
如果用同一个键名,会进行覆盖
PlayerPrefs.SetFloat("myAge", 18.3f);
//键名是唯一的
3、读取相关
Set之后也能读取信息,读取顺序为:内存 > 硬盘
1、int
int age = PlayerPrefs.GetInt("myAge");
print(age); //0 被覆盖
age = PlayerPrefs.GetInt("myAge", 18); //18 设置的初始默认值
print(age);
2、float
float height = PlayerPrefs.GetFloat("myHeight", 100f);
print(height);
3、string
string name = PlayerPrefs.GetString("myName");
print(name);
是否存在的键名
if (PlayerPrefs.HasKey("myName"))
{
print("存在myName对应的键值对数据");
}
4、删除数据
删除指定键值对
PlayerPrefs.DeleteKey("myAge");
删除所有
PlayerPrefs.DeleteAll();
思考 信息的存储和读取
玩家信息类,装备信息类
用List存储装备信息
添加标识,存储多个玩家信息
将信息存储读取
using System.Collections.Generic;
using UnityEngine;
public class Item
{
public int id;
public int num;
}
public class Player
{
public string name;
public int age;
public int atk;
public int def;
public List<Item> itemList;
//用于存储或读取的标识
private string keyName;
/// <summary>
/// 存储数据
/// </summary>
public void Save()
{
PlayerPrefs.SetString(keyName + "_name", name);
PlayerPrefs.SetInt(keyName + "_age", age);
PlayerPrefs.SetInt(keyName + "_atk", atk);
PlayerPrefs.SetInt(keyName + "_def", def);
PlayerPrefs.SetInt(keyName + "_ItemNum", itemList.Count);
for (int i = 0; i < itemList.Count; i++)
{
PlayerPrefs.SetInt(keyName + "_itemID" + i, itemList[i].id);
PlayerPrefs.SetInt(keyName + "_itemNum" + i, itemList[i].num);
}
PlayerPrefs.Save();
}
/// <summary>
/// 读取数据
/// </summary>
public void Load(string keyName)
{
//记录传入的标识
this.keyName = keyName;
name = PlayerPrefs.GetString(keyName + "_name", "未命名");
age = PlayerPrefs.GetInt(keyName + "_age", 18);
atk = PlayerPrefs.GetInt(keyName + "_atk", 20);
def = PlayerPrefs.GetInt(keyName + "_def", 2);
//获取数量
int num = PlayerPrefs.GetInt(keyName + "_ItemNum", 0);
//初始化容器
itemList = new List<Item>();
Item item;
for (int i = 0; i < num; i++)
{
item = new Item();
item.id = PlayerPrefs.GetInt(keyName + "_itemID" + i);
item.num = PlayerPrefs.GetInt(keyName + "_itemNum" + i);
itemList.Add(item);
}
}
}
public class Test1_Exercises : MonoBehaviour
{
void Start()
{
Player p = new Player();
p.Load();
//print(p.name);
//print(p.age);
//print(p.atk);
//print(p.def);
print("不同类型道具数量:"+p.itemList.Count);
for (int i = 0; i < p.itemList.Count; i++)
{
print("道具id:" + p.itemList[i].id);
print("道具数量:" + p.itemList[i].num);
}
Item item = new Item();
item.id = 1;
item.num = 99;
p.itemList.Add(item);
item = new Item();
item.id = 2;
item.num = 99;
p.itemList.Add(item);
p.Save();
//其他玩家
Player p2 = new Player();
p2.Load("Player2");
p2.Save();
}
}
PlayerPrefs存储位置
1、PlayerPrefs存储的数据在哪个位置
不同平台存储位置不同
Windows
PlayerPrefs 存储在
HKCU\Software\[公司名称]\[产品名称]项下的注册表中
其中公司和产品名称是在"Project Settings"中设置的名称
运行 regedit
HKEY_CURRENT_USER
SOFTWARE
Unity
UnityEditor
公司名称
产品名称
Android
/data/data/应用程序包名/shared_prefs/pkg-name.xml
IOS
/Library/Preferences/[应用ID].plist
2、PlayerPrefs 数据唯一性
PlayerPrefs 中不同数据的唯一性
由key决定,不同key决定了不同的数据
同一项目中,如果不同数据key相同,会造成数据丢失
思考 排行榜功能
排行榜主要记录玩家名,玩家得分,玩家通关时间
using System.Collections.Generic;
using UnityEngine;
public class RankListInfo
{
public List<RankInfo> rankList;
public RankListInfo() { Load(); }
public void Add(string name, int score, int time)
{
rankList.Add(new RankInfo(name, score, time));
}
public void Save()
{
PlayerPrefs.SetInt("rankListNum", rankList.Count);
for (int i = 0; i < rankList.Count; i++)
{
RankInfo info = rankList[i];
PlayerPrefs.SetString("rankInfo" + i, info.playerName);
PlayerPrefs.SetInt("rankInfo" + i, info.playerScore);
PlayerPrefs.SetInt("rankInfo" + i, info.playerTime);
}
}
private void Load()
{
int num = PlayerPrefs.GetInt("rankListNum", 0);
rankList = new List<RankInfo>();
for (int i = 0;i < num;i++)
{
RankInfo info = new RankInfo(PlayerPrefs.GetString("rankInfo" + i), PlayerPrefs.GetInt("rankScore" + i), PlayerPrefs.GetInt("rankTIme" + i));
rankList.Add(info);
}
}
}
public class RankInfo
{
public string playerName;
public int playerScore;
public int playerTime;
public RankInfo(string name, int score, int time)
{
playerName = name;
playerScore = score;
playerTime = time;
}
}
public class Test2_Exercises : MonoBehaviour
{
void Start()
{
RankListInfo rankList = new RankListInfo();
print(rankList.rankList.Count);
for (int i = 0; i < rankList.rankList.Count; i++)
{
print("姓名" + rankList.rankList[i].playerName);
print("分数" + rankList.rankList[i].playerScore);
print("时间" + rankList.rankList[i].playerTime);
}
rankList.Add("玩家", 100, 88);
rankList.Save();
}
}
2、Playerprefs实践
1、必备知识点-反射知识小补充
Type 用于获取类的所有信息:字段、属性、方法 等
Assembly 用于获取程序集,通过程序集获取Type
Activator 用于快速实例化对象
using System;
using System.Collections.Generic;
using UnityEngine;
public class Father { }
public class Son : Father { }
public class Reflection : MonoBehaviour
{
void Start()
{
1、判断A对象是否让B对象分配空间
//父类装子类
//是否可以从某一个类型的对象为自己分配空间
Type fatherType = typeof(Father);
Type sonType = typeof(Son);
//调用者通过IsAssignableFrom方法进行判断,是否可以通过传入的类型为自己分配空间
if (fatherType.IsAssignableFrom(sonType))
{
print("可以分配");
Father f = Activator.CreateInstance(sonType) as Father;
print(f);
}
else
{
print("不能分配");
}
2、通过反射获取泛型类型
List<string> list = new List<string>();
Type listType = list.GetType();
//通过得到泛型类的类型,得到泛型具体的类型
Type[] types = listType.GetGenericArguments();
print(types[0]);
Dictionary<string,float> dict = new Dictionary<string,float>();
Type dicType = dict.GetType();
types = dicType.GetGenericArguments();
print(types[0]);
print(types[1]);
}
}
2、需求分析 Playerprefs数据管理类创建
Test
using System.Collections.Generic;
using UnityEngine;
public class PlayerInfo
{
public int age = 10;
public string name = "姓名";
public float height = 188.5f;
public bool sex = true;
public List<int> list = new List<int>() { 1, 2, 3, 4, };
public Dictionary<int, string> dic = new Dictionary<int, string>()
{
{1,"123" },
{1,"123" }
};
public ItemInfo itemInfo = new ItemInfo(3, 33);
public List<ItemInfo> items = new List<ItemInfo>() { new ItemInfo(1, 99), new ItemInfo(2, 22) };
public Dictionary<int, ItemInfo> itemDic = new Dictionary<int, ItemInfo>() { { 4, new ItemInfo(4, 44) }, { 5, new ItemInfo(5, 55) } };
}
public class ItemInfo
{
public int num;
public int id;
public ItemInfo() { }
public ItemInfo(int id, int num)
{
this.id = id;
this.num = num;
}
}
public class Test : MonoBehaviour
{
void Start()
{
PlayerInfo p = new PlayerInfo();
PlayerPrefsDataMgr.Instance.SaveData(p, "Player1");
}
}
3、反射存储数据-常用成员、List成员、Dic成员、自定义类成员
using System;
using System.Collections;
using System.Reflection;
using UnityEngine;
/// <summary>
/// PlayerPrefs数据管理类,同一管理数据的存储和读取
/// </summary>
public class PlayerPrefsDataMgr
{
private static PlayerPrefsDataMgr instance = new PlayerPrefsDataMgr();
public static PlayerPrefsDataMgr Instance
{
get { return instance; }
}
private PlayerPrefsDataMgr() { }
/// <summary>
/// 存储数据
/// </summary>
/// <param name="data">数据对象</param>
/// <param name="keyName">数据对象的唯一key,自己控制</param>
public void SaveData(object data, string keyName)
{
//通过Type得到传入数据对象的所有字段,结合PlayerPrefs来存储
//1、获取传入数据对象的所有字段
Type dataType = data.GetType();
//得到所有字段
FieldInfo[] infos = dataType.GetFields();
//2、自己定义一个key的规则,进行数据存储
//keyName_数据类型_字段类型_字段名
//3、遍历字段,进行数据存储
string saveKeyNmae = "";
FieldInfo info;
for (int i = 0; i < infos.Length; i++)
{
//对每一个字段,进行数据存储
//得到具体的字段信息
info = infos[i];
//通过FieldInfo可以直接获取到字段的类型和字段的名字
// 字段的类型 info.FieldType.Name
// 字段的名字 info.Name
//根据key生成key
// Player1_PlayerInfo_Int32_age
saveKeyNmae = keyName + "_" + dataType.Name + "_" + info.FieldType.Name + "_" + info.Name;
//获取值
//info.GetValue(data)
SaveData(info.GetValue(data), saveKeyNmae);
}
}
/// <summary>
///
/// </summary>
/// <param name="value">类型</param>
/// <param name="keyName">存储的key名称</param>
private void SaveValue(object value, string keyName)
{
Type fieldType = value.GetType();
if (fieldType == typeof(int))
{
Debug.Log("存储int" + keyName);
PlayerPrefs.SetInt(keyName, (int)value);
}
else if (fieldType == typeof(float))
{
Debug.Log("存储float" + keyName);
PlayerPrefs.SetFloat(keyName, (float)value);
}
else if (fieldType == typeof(string))
{
Debug.Log("存储string" + keyName);
PlayerPrefs.SetString(keyName, value.ToString());
}
else if (fieldType == typeof(bool))
{
Debug.Log("存储bool" + keyName);
PlayerPrefs.SetInt(keyName, (bool)value ? 1 : 0);
}
//通过IList判断这个类是否为List
else if (typeof(IList).IsAssignableFrom(fieldType))
{
Debug.Log("存储List" + keyName);
IList list = value as IList;
//存储List数量
PlayerPrefs.SetInt(keyName, list.Count);
int index = 0;
foreach (object obj in list)
{
SaveValue(obj, keyName + index);
index++;
}
}
else if (typeof(IDictionary).IsAssignableFrom(fieldType))
{
Debug.Log("存储Dictionary" + keyName);
IDictionary dic = value as IDictionary;
//存储字典长度
PlayerPrefs.SetInt(keyName,dic.Count);
//遍历存储Dic里面的具体值
int index = 0;
foreach (object key in dic.Keys)
{
SaveValue(key, keyName + "_key_" + index);
SaveValue(dic[key], keyName + "_value_" + index);
index++;
}
}
else
{
SaveData(value, keyName);
}
}
/// <summary>
/// 读取数据
/// </summary>
/// <param name="type">想要读取数据的数据类型</param>
/// <param name="keyName">数据对象的唯一key</param>
/// <returns></returns>
public object LoadData(Type type, string keyName)
{
//只需要传入一个Type,typeof(Player),就可以在内部动态创建一个对象返回出来
//根据传入的类型和keyName,依据存储数据时key的拼接规则来进行数据的获取赋值,然后返回出去
return null;
}
}
4、反射读取数据-常用成员、List成员、Dic成员、自定义类成员
test
using System.Collections.Generic;
using UnityEngine;
public class PlayerInfo
{
public int age;
public string name = "姓名";
public float height;
public bool sex;
public List<int> list;
public Dictionary<int, string> dic;
public ItemInfo itemInfo;
public List<ItemInfo> itemList;
public Dictionary<int, ItemInfo> itemDic;
}
public class ItemInfo
{
public int num;
public int id;
public ItemInfo() { }
public ItemInfo(int id, int num)
{
this.id = id;
this.num = num;
}
}
public class Test : MonoBehaviour
{
void Start()
{
//PlayerInfo p = new PlayerInfo();
将数据对象的信息存储到硬盘
//PlayerPrefsDataMgr.Instance.SaveData(p, "Player1");
//PlayerPrefs.DeleteAll();
//读取数据
PlayerInfo p = PlayerPrefsDataMgr.Instance.LoadData(typeof(PlayerInfo), "Player1") as PlayerInfo;
//游戏逻辑
p.age = 18;
p.name = "姓名";
p.height = 183;
p.sex = true;
p.itemList.Add(new ItemInfo(1,99));
p.itemList.Add(new ItemInfo(2,22));
p.itemDic.Add(3, new ItemInfo(3, 33));
p.itemDic.Add(4, new ItemInfo(4, 44));
//游戏数据存储
PlayerPrefsDataMgr.Instance.SaveData(p, "player1");
}
}
PlayerPrefsDataMgr
using System;
using System.Collections;
using System.Reflection;
using UnityEngine;
/// <summary>
/// PlayerPrefs数据管理类,同一管理数据的存储和读取
/// </summary>
public class PlayerPrefsDataMgr
{
private static PlayerPrefsDataMgr instance = new PlayerPrefsDataMgr();
public static PlayerPrefsDataMgr Instance
{
get { return instance; }
}
private PlayerPrefsDataMgr() { }
/// <summary>
/// 存储数据
/// </summary>
/// <param name="data">数据对象</param>
/// <param name="keyName">数据对象的唯一key,自己控制</param>
public void SaveData(object data, string keyName)
{
//通过Type得到传入数据对象的所有字段,结合PlayerPrefs来存储
//1、获取传入数据对象的所有字段
Type dataType = data.GetType();
//得到所有字段
FieldInfo[] infos = dataType.GetFields();
//2、自己定义一个key的规则,进行数据存储
//keyName_数据类型_字段类型_字段名
//3、遍历字段,进行数据存储
string saveKeyNmae = "";
FieldInfo info;
for (int i = 0; i < infos.Length; i++)
{
//对每一个字段,进行数据存储
//得到具体的字段信息
info = infos[i];
//通过FieldInfo可以直接获取到字段的类型和字段的名字
// 字段的类型 info.FieldType.Name
// 字段的名字 info.Name
//根据key生成key
// Player1_PlayerInfo_Int32_age
saveKeyNmae = keyName + "_" + dataType.Name + "_" + info.FieldType.Name + "_" + info.Name;
//获取值
//info.GetValue(data)
SaveData(info.GetValue(data), saveKeyNmae);
}
PlayerPrefs.Save();
}
/// <summary>
///
/// </summary>
/// <param name="value">类型</param>
/// <param name="keyName">存储的key名称</param>
private void SaveValue(object value, string keyName)
{
Type fieldType = value.GetType();
if (fieldType == typeof(int))
{
Debug.Log("存储int" + keyName);
PlayerPrefs.SetInt(keyName, (int)value);
}
else if (fieldType == typeof(float))
{
Debug.Log("存储float" + keyName);
PlayerPrefs.SetFloat(keyName, (float)value);
}
else if (fieldType == typeof(string))
{
Debug.Log("存储string" + keyName);
PlayerPrefs.SetString(keyName, value.ToString());
}
else if (fieldType == typeof(bool))
{
Debug.Log("存储bool" + keyName);
PlayerPrefs.SetInt(keyName, (bool)value ? 1 : 0);
}
//通过IList判断这个类是否为List
else if (typeof(IList).IsAssignableFrom(fieldType))
{
Debug.Log("存储List" + keyName);
IList list = value as IList;
//存储List数量
PlayerPrefs.SetInt(keyName, list.Count);
int index = 0;
foreach (object obj in list)
{
SaveValue(obj, keyName + index);
index++;
}
}
else if (typeof(IDictionary).IsAssignableFrom(fieldType))
{
Debug.Log("存储Dictionary" + keyName);
IDictionary dic = value as IDictionary;
//存储字典长度
PlayerPrefs.SetInt(keyName,dic.Count);
//遍历存储Dic里面的具体值
int index = 0;
foreach (object key in dic.Keys)
{
SaveValue(key, keyName + "_key_" + index);
SaveValue(dic[key], keyName + "_value_" + index);
index++;
}
}
else
{
SaveData(value, keyName);
}
}
/// <summary>
/// 读取数据
/// </summary>
/// <param name="type">想要读取数据的数据类型</param>
/// <param name="keyName">数据对象的唯一key</param>
/// <returns></returns>
public object LoadData(Type type, string keyName)
{
//只需要传入一个Type,typeof(Player),就可以在内部动态创建一个对象返回出来
//根据传入的类型和keyName,依据存储数据时key的拼接规则来进行数据的获取赋值,然后返回出去
//根据传入的Type创建一个对象,用于存储数据
object data = Activator.CreateInstance(type);
//要在这个new出来的对象中存储数据
FieldInfo[] infos = type.GetFields();
//用于拼接key的字符串
string loadKeyName = "";
//用于存储单个字段信息的对象
FieldInfo info;
for (int i = 0; i < infos.Length; i++)
{
info = infos[i];
loadKeyName = keyName + "_" + type.Name + "_" + info.FieldType.Name + "_" + info.Name;
//填充数据到data中
info.SetValue(data, LoadValue(info.FieldType, loadKeyName));
}
return data;
}
/// <summary>
/// 得到单个数据的方法
/// </summary>
/// <param name="fieldType">字段类型,用于判断API的读取</param>
/// <param name="keyName">唯一key,获取Value</param>
/// <returns></returns>
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 if(fieldType == typeof(bool))
{
return PlayerPrefs.GetInt(keyName, 0) == 1 ? true : false;
}
else if (typeof(IList).IsAssignableFrom(fieldType))
{
//得到List长度
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).IsAssignableFrom(fieldType))
{
//得到字典dictionary的长度
int count = PlayerPrefs.GetInt(keyName, 0);
//实例化字典对象
IDictionary dic = Activator.CreateInstance(fieldType) as IDictionary;
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);
}
}
}
4、加密思路
//为int数据加密
int rValue = (int)value;
rValue += 10;
PlayerPrefs.SetInt(keyName, rValue);
//解密
return PlayerPrefs.GetInt(keyName, 0) - 10;
5、生成资源包
Export Package