Unity 数据持久化【PlayerPrefs】

1、数据持久化

文章目录

概念

c 复制代码
将【变量数据】在内存和硬盘之间的存储或读取

内容

c 复制代码
PlayerPrefs的基本方法
PlayerPrefs在不同平台的存储位置
利用反射结合PlayerPrets制作通用存诸工具

PlayerPrefs基本方法

1、PlayerPrefs概念

c 复制代码
是unity提供的可以用于存储读取玩家数据的公共类

2、存储相关

csharp 复制代码
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、读取相关

csharp 复制代码
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、删除数据

csharp 复制代码
删除指定键值对
    PlayerPrefs.DeleteKey("myAge");

删除所有
    PlayerPrefs.DeleteAll();

思考 信息的存储和读取

csharp 复制代码
玩家信息类,装备信息类
用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相同,会造成数据丢失

思考 排行榜功能

cs 复制代码
排行榜主要记录玩家名,玩家得分,玩家通关时间
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、必备知识点-反射知识小补充

csharp 复制代码
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

csharp 复制代码
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成员、自定义类成员

csharp 复制代码
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

csharp 复制代码
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

csharp 复制代码
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、加密思路

csharp 复制代码
//为int数据加密
int rValue = (int)value;
rValue += 10;
PlayerPrefs.SetInt(keyName, rValue);
//解密
return PlayerPrefs.GetInt(keyName, 0) - 10;

5、生成资源包

Export Package

相关推荐
め.18 小时前
UIFramework
ui·unity
fqbqrr19 小时前
2606d,用d语言构建游戏引擎
游戏引擎·d
℡枫叶℡1 天前
Unity - Import Activity Window 资源导入诊断信息窗口
unity·资源导入诊断
TO_ZRG1 天前
Unity 证书校验
unity·游戏引擎
mxwin1 天前
Unity Shader 切线空间数据是如何计算出来的
unity·游戏引擎·shader
mxwin1 天前
Unity Shader 法线贴图跟切线空间有什么关系
unity·游戏引擎·贴图·shader
mxwin1 天前
Unity Shader 贴图和采样的关系 如何保证贴图清晰
unity·游戏引擎·贴图·shader
心前阳光2 天前
Unity之使用火山引擎实现文字提问流式回复
unity·游戏引擎·火山引擎
mxwin2 天前
Unity Shader 什么是球谐光照 原理是什么
unity·游戏引擎·shader