【Unity实战教程】unity用PlayerPrefs存档和读档天赋属性

前言

  1. 本内容为本人原创提供
  2. 本内特为学习者提供原创完整的工程源码下载,可参考学习或其他用途。
  3. 本内容从玩家天赋相关属性出发,对数据存取操作,一步一步推进,能直观感受。
  4. 如果后续有其他的存取游戏数据的方式,将继续提供给大家借鉴参考,希望能协助大家完成自己项目。
  5. PlayerPrefs以往只作为存取小数据的方法,区别以往,本文核心用PlayerPrefs实战,教学存取复杂游戏的数据,并且解决存取繁琐的核心问题。

目录

前言

一、什么是PlayerPrefs存取

二、认识PlayerPrefs

三、unity实战

1、数据存取操作的起始工作

2、保存数据

3、读取数据

4、删除数据

四、运行图示

五、总结

六、工程下载地址  


一、什么是PlayerPrefs存取

        PlayerPrefs unity自带的API,用于存取游戏数据而用。

        可用于Windows、Linux 、macOS 、Android 、WebGL 、iOS 平台。

         本文实例为windows平台。

        主要函数

DeleteAll从偏好中删除所有键和值。请谨慎使用。
DeleteKey    从偏好中删除 key 及其对应值。
GetFloat    返回偏好设置文件中与 key 对应的值(如果存在)。
GetInt    返回偏好设置文件中与 key 对应的值(如果存在)。
GetString    返回偏好设置文件中与 key 对应的值(如果存在)。
HasKey    如果 key 在偏好中存在,则返回 true。
Save    将所有修改的偏好写入磁盘。
SetFloat    设置由 key 标识的偏好的值。
SetInt    设置由 key 标识的偏好的值。
SetString    设置由 key 标识的偏好的值。

      注:表格数据源自unity手册

        用放大镜看,发现只能存取寥寥几个 stringintfloat 数据,感觉上 listdictionary 无法存取,要么存取就很复杂,甚至觉得,数据一多,多存档的游戏都不好分类,甚至删除某条存档。

        本文主要就是解锁PlayerPrefs存取复杂游戏数据


二、认识PlayerPrefs

  •     PlayerPrefs.SetFloat("Health", 50.0F)
  •     PlayerPrefs.GetFloat("Health", 0);

         PlayerPrefs为接口,SetFloat为方法函数,Health为key键标识,50.0F为变量。

         这条方法就是保存一条为Health的浮点数据。

        GetInt、GetString、SetInt、SetString 参考上面即可。

  •    DeleteAll 删除所有存档,极为特殊情况用下就好了。
  •    DeleteKey 数据不多情况下可以用下,数据多了很繁琐,不建议多数据游戏中采用。

   主要讲这几个函数方法。


三、unity实战

        本次实战采用游戏中天赋属性单元展开,对天赋属性进行存档和读档操作。

1、数据存取操作的起始工作


        1.1、天赋属性:

        首先,创建一个天赋属性类,把天赋内容属性规范好,当然也能创建结构体。如果数据多,并且比较复杂,可以选择类,操作起来更有优势

[System.Serializable]
public class Talent
{
    public int talent_id;
    public int talent_lv;
    public string talent_name;
    public int talent_needpoint;
    public int talent_needstar;
    public string talent_des;
    public List<Talentskill> map_skilllist;
}

如何在 inspector 面板上显示类的变量,可以用 [System.Serializable]

需要注意的是,这里不用 { get; set; } ,因为用了,在 inspector 面板中不会显示变量

         1.2、游戏管理类

        创建一个游戏管理类,并单例它

    private static Gamemanager _instance;
    public static Gamemanager Instance
    {
        get
        {
            if (_instance == null)
            {
                _instance = new Gamemanager();
            }
            return _instance;
        }
    }

         1.3、文件路径

        与以往的xml、json、excel、二进制存取不同,这里不需要设置文件路径。

        因为 PlayerPrefs 被存储在注册表的         HKEY_CURRENT_USER\Software\Unity\UnityEditor\


2、保存数据

         很多人觉得 PlayerPrefs 存取无法做到多存档,其实它可以做多存档,因为游戏数据被存在注册表,所以我们不能以之前的存取思路来做存取功能。

         2.1、声明一个头文件名

    private string dataTitlestr = "d_" + "data1";

        这一步很关键,声明 string 变量,是在多存档时,这个变量可作为删除某条存档的标识符号,至于怎么删除,后面会单独讲。

         2.2、PlayerPrefs 常规用法

        PlayerPrefs存数据时,都是按变量的排序存,这点和二进制存取有点类似。 

    //PlayerPres常规存
    public void NorSavePlayerPrefsDoc()
    {
        PlayerPrefs.SetString(dataTitlestr, "存档一");
        for (int i = 0; i < Gamemanager.Instance.talentList.Count; i++)
        {
            Talent talent = Gamemanager.Instance.talentList[i].myTalent;
            PlayerPrefs.SetInt(dataTitlestr + "talent_id" + i, talent.talent_id);
            PlayerPrefs.SetInt(dataTitlestr + "talent_lv" + i, talent.talent_lv);
            PlayerPrefs.SetString(dataTitlestr + "talent_name" + i, talent.talent_name);
            PlayerPrefs.SetInt(dataTitlestr + "talent_needpoint" + i, talent.talent_needpoint);
            PlayerPrefs.SetInt(dataTitlestr + "talent_needstar" + i, talent.talent_needstar);
            PlayerPrefs.SetString(dataTitlestr + "talent_des" + i, talent.talent_des);
            for (int j = 0; j < talent.map_skilllist.Count; j++)
            {
                Talentskill skill = talent.map_skilllist[j];
                PlayerPrefs.SetInt(dataTitlestr + "skill_id" + i + "_" + j, skill.skill_id);
                PlayerPrefs.SetInt(dataTitlestr + "skill_name" + i + "_" + j, skill.skill_id);
                PlayerPrefs.SetFloat(dataTitlestr + "skill_atc" + i + "_" + j, skill.skill_id);
            }
        }

    }

        注意: + i + "_" + j key键 的唯一标识,如果不加序列,会有重复的 key,那么保存时后面的数据会取代前面的数据。
        这个序号用 i 即可。如果时字典,则不用担心,因为字典的key本身就是唯一标识。

         2.3、PlayerPrefs 特殊用法

        先保存存档抬头,作为多存档的标识

     PlayerPrefs.SetString(dataTitlestr, "存档一");

        其后遍历某项游戏数据,这是天赋数据

    for (int i = 0; i < Gamemanager.Instance.talentList.Count; i++)
    {
        ...
    }

       循环 内部有,获取天赋类,并获取内部遍历集合

         Talent talent = Gamemanager.Instance.talentList[i].myTalent;
         Type type = talent.GetType();
         FieldInfo[] fieldTypes = type.GetFields();

        用反射方法,反射类中变量存

        最终读取代码为

 public void SavePlayerPrefsDoc()
 {
     PlayerPrefs.SetString(dataTitlestr, "存档一");
     for (int i = 0; i < Gamemanager.Instance.talentList.Count; i++)
     {
         Talent talent = Gamemanager.Instance.talentList[i].myTalent;
         Type type = talent.GetType();
         FieldInfo[] fieldTypes = type.GetFields();

         foreach (FieldInfo field in fieldTypes)
         {  
             //判断是否为list
             if (field.Name.Contains("list"))
             {
                 for (int j = 0; j < talent.map_skilllist.Count; j++)
                 {
                     Talentskill skill = talent.map_skilllist[j];
                     Type childtype = skill.GetType();
                     FieldInfo[] childfieldTypes = childtype.GetFields();
                     foreach (var item in childfieldTypes)
                     {
                         //这里不能用 field.GetType();
                         if (item.FieldType == typeof(int))
                         {
                             int num = (int)item.GetValue(skill);
                             PlayerPrefs.SetInt(dataTitlestr + item.Name + i + "_" + j, (int)item.GetValue(skill));
                         }
                         else if (item.FieldType == typeof(string))
                         {
                             string str = item.GetValue(skill).ToString();
                             PlayerPrefs.SetString(dataTitlestr + item.Name + i + "_" + j, item.GetValue(skill).ToString());
                         }
                         else if (item.FieldType == typeof(float))
                         {
                             string str = item.GetValue(skill).ToString();
                             PlayerPrefs.SetFloat(dataTitlestr + item.Name + i + "_" + j, (float)item.GetValue(skill));
                         }
                     }
                 }              
             }
             else if (field.Name.Contains("dic"))
             {
                 //...略
             }
             else
             {
                 //这里不能用 field.GetType();
                 if (field.FieldType == typeof(int))
                 {
                     int num = (int)field.GetValue(talent);
                     PlayerPrefs.SetInt(dataTitlestr + field.Name + i, (int)field.GetValue(talent));
                 }
                 else if (field.FieldType == typeof(string))
                 {
                     string str = field.GetValue(talent).ToString();
                     PlayerPrefs.SetString(dataTitlestr + field.Name + i, field.GetValue(talent).ToString());
                 }
                 else if (field.FieldType == typeof(float))
                 {
                     PlayerPrefs.SetFloat(dataTitlestr + field.Name + i, (int)field.GetValue(talent));
                 }
             }
         }
     }   
 }

        这样做,就算大量声明,只需要简单判断类似是 int、string、float、数组,字典,就能批量储存。

        比如玩家有30个变量声明,物品有20变量声明,武功有20个声明,统统只需要判断类似是int、string、float、数组,字典即可,不用一条条去保存了


3、读取数据

        如果读取的是数组,先声明一个临时数组

        List<Talentattribute> temptalentList = new List<Talentattribute>(Gamemanager.Instance.talentList);

        主要目的是防止读取时,防止读取时原数组值被新读取的值替换,造成数据错乱。

List<Talentattribute> temptalentList = new List<Talentattribute>(Gamemanager.Instance.talentList);
for (int i = 0; i < temptalentList.Count; i++)
{
    Talent talent = Gamemanager.Instance.talentList[i].myTalent;
    Type type = talent.GetType();
    FieldInfo[] fieldTypes = type.GetFields();  
    foreach (FieldInfo field in fieldTypes)
    {
        if (field.Name.Contains("list"))
        {
            for (int j = 0; j < talent.map_skilllist.Count; j++)
            {
                Talentskill skill = talent.map_skilllist[j];
                Type childtype = skill.GetType();
                FieldInfo[] childfieldTypes = childtype.GetFields();
                foreach (var item in childfieldTypes)
                {
                    //这里不能用 field.GetType();
                    if (item.FieldType == typeof(int))
                    {
                        item.SetValue(skill, PlayerPrefs.GetInt(dataTitlestr + item.Name + i + "_" + j));
                    }
                    else if (item.FieldType == typeof(string))
                    {
                        item.SetValue(skill, PlayerPrefs.GetString(dataTitlestr + item.Name + i + "_" + j));
                    }
                    else if (item.FieldType == typeof(float))
                    {
                        item.SetValue(skill, PlayerPrefs.GetFloat(dataTitlestr + item.Name + i + "_" + j));
                    }
                }
            }
        }
        else if (field.Name.Contains("dic"))
        {
            //...略
        }
        else
        {

            if (field.FieldType == typeof(int))
            {
                int num = PlayerPrefs.GetInt(dataTitlestr + field.Name + i);
                field.SetValue(talent, PlayerPrefs.GetInt(dataTitlestr + field.Name + i));
            }
            else if (field.FieldType == typeof(string))
            {
                field.SetValue(talent, PlayerPrefs.GetString(dataTitlestr + field.Name + i));
            }
            else if (field.FieldType == typeof(float))
            {
                field.SetValue(talent, PlayerPrefs.GetFloat(dataTitlestr + field.Name + i));
            }
        }
    }
}

        读取时,也需要加序列号 + i + "_" + j,不然依然会只会读取同类型同一个值
这个序列号就是 PlayerPrefs 存取数组类型的key键的唯一标识

        这么设计,就是为了让 PlayerPrefs 也能存取大量数据,它与 二进制 的区别就是,二进制存取可以根据变量排序直接读取。
        PlayerPrefs 正因为没有 二进制 这个功能,那么我们就设计一个排序功能,根据 list index 排序,将其保存到 key 中,从而得到唯一标识。

        这是 PlayerPrefs 保存和读取大量数据的最关键点。


4、删除数据

        如果是一条数据,可以用DeleteAll。

        如果是少量数据删除某个游戏数据,可以用 DeleteKey

        如果是很多很多数据,请用

    public void DeletePlayerPrefsDoc()
    {
        PlayerPrefs.SetString(dataTitlestr, string.Empty);
    }


       这里只要根据 dataTitlestr,将其设置为空值,就可以将该条数据之下所有数据不在明化

        什么意思?

        打个比方,如果dataTitlestr 键的 值 为空,那么我们就隐藏这条存档的UI,从而达到等同删除的效果。

        也可用以用 HasKey 判断删除数据,不过用省力方法达到一样效果就好啦。


四、运行图示


五、总结

  •    与 xml/jsong/excel/二进制存取数据,不同,PlayerPrefs 不用设置存取路径
  •    PlayerPrefs 存取数据必须设置key的唯一标识符。
  •    与二进制存取一样,数组,字典都需要有限存取它们的count。
  •    与二进制存取不同的是,不需要保证变量存取顺序。

写着写着就这么多了,可能不是特别全,不介意费时就看看吧。有时间还会接着更新。

六、工程下载地址  

【unity实战案例】PlayerPrefs存取工程下载

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

学与用

原创作者,在线要饭

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值