Unity學習筆記--資料持久化之PlayerPrefs的使用

2023-11-19 21:00:21

資料持久化

PlayerPrefs相關

PlayerPrefs是Unity遊戲引擎中的一個類,用於在遊戲中儲存和存取玩家的偏好設定和資料。它可以用來儲存玩家的遊戲進度、設定選項、最高分數等資訊。PlayerPrefs將資料儲存在本地檔案中,因此可以在遊戲重新啟動時保持資料的永續性。

//PlayerPrefs的資料儲存 類似於鍵值對儲存 一個鍵對應一個值
//提供了儲存3種資料的方法 int float string
//鍵: string型別 
//值:int float string 對應3種API

PlayerPrefs.SetInt("myAge", 18);
PlayerPrefs.SetFloat("myHeight", 177.5f);
PlayerPrefs.SetString("myName", "TonyChang");

//直接呼叫Set相關方法 只會把資料存到記憶體裡
//當遊戲結束時 Unity會自動把資料存到硬碟中
//如果遊戲不是正常結束的 而是崩潰 資料是不會存到硬碟中的
//只要呼叫該方法 就會馬上儲存到硬碟中
PlayerPrefs.Save();

//PlayerPrefs是有侷限性的 它只能存3種型別的資料
//如果你想要儲存別的型別的資料 只能降低精度 或者上升精度來進行儲存
bool sex = true;
PlayerPrefs.SetInt("sex", sex ? 1 : 0);

//如果不同型別用同一鍵名進行儲存 會進行覆蓋
PlayerPrefs.SetFloat("myAge", 20.2f);

//注意 執行時 只要你Set了對應鍵值對
//即使你沒有馬上儲存Save在本地
//也能夠讀取出資訊

//int
int age = PlayerPrefs.GetInt("myAge");
print(age);
//前提是 如果找不到myAge對應的值 就會返回函數的第二個引數 預設值
age = PlayerPrefs.GetInt("myAge", 100);
print(age);

//float
float height = PlayerPrefs.GetFloat("myHeight", 1000f);
print(height);

//string
string name = PlayerPrefs.GetString("myName");
print(name);

//第二個引數 預設值 對於我們的作用
//就是 在得到沒有的資料的時候 就可以用它來進行基礎資料的初始化

//判斷資料是否存在
if( PlayerPrefs.HasKey("myName") )
{
    print("存在myName對應的鍵值對資料");
}

//刪除指定鍵值對
PlayerPrefs.DeleteKey("myAge");
//刪除所有儲存的資訊
PlayerPrefs.DeleteAll();

PlayerPrefs中儲存的資料儲存在哪裡?

PC端: PlayerPrefs 儲存在 HKCU\Software[公司名稱][產品名稱] 項下的登入檔中
其中公司和產品名稱是 在「Project Settings」中設定的名稱。

安卓: /data/data/包名/shared_prefs/pkg-name.xml

PlayerPrefs中資料的唯一性,PlayerPrefs中資料的唯一性是由key決定的,不同的key決定了不同的資料,同一個專案中如果不同資料key相同會造成資料丟失,要保證資料名稱命名的唯一性規則。

優點:使用簡單

缺點:儲存資料型別有限、安全性差(直接找到在裝置上的儲存的位置檢視設定)

PlayerPrefs儲存工具類:

為了方便進行資料的儲存,使用PlayerPrefs中進行儲存方法的設定的存取!

主要實現功能是資料的讀和資料的取~ 通過反射進行資料型別的獲取,利用PlayerPrefs進行資料儲存。

using System;
using System.Collections;
using System.Reflection;
using UnityEngine;

namespace Framwork
{
    /// <summary>
    /// Playerprefs 儲存類
    /// </summary>
    public class PlayerPrefsManager
    {
        private static PlayerPrefsManager instance=new PlayerPrefsManager();

        public static PlayerPrefsManager Instance => instance;

        private PlayerPrefsManager()
        {
           
        }

        /// <summary>
        /// 存取資料的方法
        /// </summary>
        /// <param name="obj">資料實體</param>
        /// <param name="name">資料名稱</param>
        public void SaveData(object data, string keyName)
        {
            Type type = data.GetType();
            FieldInfo[] infos = type.GetFields();
            string tempKey="null";
            FieldInfo tempInfo = null;
            for (int i = 0; i < infos.Length; i++)
            {
                //獲取資料資料型別
                tempInfo = infos[i];
                Debug.Log("Types==="+tempInfo);
                //類的名字+類的型別 + 資料內容名字+資料型別
                //作為儲存的keyName鍵
                tempKey = keyName + "_" + type.Name + "_" + tempInfo.Name
                            + "_" + tempInfo.FieldType.Name;
                SaveValue(tempInfo.GetValue(data),tempKey);
            }
            //進行值的獲取
           //tempInfo.GetValue(data);
            PlayerPrefs.Save();
        }
        /// <summary>
        /// 讀取資料的型別
        /// </summary>
        /// <param name="type">要讀取的資料型別</param>
        /// <param name="name">要讀取的資料名稱</param>
        /// <returns>返回資料實體</returns>
        public object LoadData(Type type, string name)
        {
            //獲取資料中的型別
            FieldInfo[] infos = type.GetFields();
            //建立儲存資料資訊的實體
            object data = Activator.CreateInstance(type);
            string tempName = null;
            FieldInfo tempInfo = null;
            for (int i = 0; i < infos.Length; i++)
            {
                tempInfo = infos[i];//資料結構中的資料名稱
                tempName = name + "_" + type.Name + "_" +tempInfo.Name+"_"
                    +tempInfo.FieldType.Name;//資料結構中的資料名稱型別
                //裝載的容器  容器中的資料 
                //進行資料裝載
                tempInfo.SetValue(data,LoadValue(tempInfo.FieldType,tempName));
            }
            return data;
        }

        /// <summary>
        /// 進行具體的型別資料的儲存
        /// </summary>
        /// <param name="data"></param>
        /// <param name="keyName"></param>
        private void SaveValue(object value, string keyName)
        {
            Type fieldType = value.GetType();
            if (fieldType == typeof(int))
            {
                Debug.Log("儲存int"+value);
                PlayerPrefs.SetInt(keyName,(int)value);
            }else if (fieldType == typeof(float))
            {
                Debug.Log("儲存float"+value);
                PlayerPrefs.SetFloat(keyName,(float)value);
            }else if (fieldType == typeof(string))
            {
                Debug.Log("儲存string"+value);
                PlayerPrefs.SetString(keyName,value.ToString());
            }
            //對於List儲存的設定
            //根據儲存的欄位型別和IList是否是父子關係
            else if(typeof(IList).IsAssignableFrom(fieldType))
            {
                //父類別裝子類
                IList list=value as IList;
                //儲存元素數量
                PlayerPrefs.SetInt(keyName,list.Count);
                Debug.Log("儲存List長度為"+list.Count);
                int index = 0;
                foreach (var obj in list)
                {
                    //儲存list列表中元素內容
                    //命名形式是 list名字+索引編號
                    //遞迴呼叫儲存
                    SaveValue(obj,keyName+index);
                    index++;
                }
            }else if (typeof(IDictionary).IsAssignableFrom(fieldType))
            {
                IDictionary dictionary = value as IDictionary;
                //儲存資料個數
                PlayerPrefs.SetInt(keyName,dictionary.Count);
                Debug.Log("儲存Dic長度為"+dictionary.Count);
                int index = 0;
                foreach (var key in dictionary.Keys)
                {
                    //儲存鍵
                    SaveValue(key,keyName+"_key_"+index);
                    //儲存值 
                    SaveValue(dictionary[key],keyName+"_value_"+index);
                    index++;
                }
            }//自定義資料型別的儲存 進行解析
            else 
            {
                SaveData(value,keyName);
            }
        }

        private object LoadValue(Type type, string name)
        {
            if (type == typeof(int))
            {
                return PlayerPrefs.GetInt(name,0);
            }else if (type == typeof(float))
            {
                return PlayerPrefs.GetFloat(name,0.0f);
            }else if (type == typeof(string))
            {
                return PlayerPrefs.GetString(name,"");
            }else if (typeof(IList).IsAssignableFrom(type))
            {
                //讀取列表
                int count = PlayerPrefs.GetInt(name);
                IList tempList=Activator.CreateInstance(type) as IList;
                for (int i = 0; i < count; i++)
                {
                    //獲取List中儲存元素的型別 type.GetGenericArguments()[0]
                    tempList.Add(LoadValue(type.GetGenericArguments()[0],name+i));
                }
                return tempList;
            }else if (typeof(IDictionary).IsAssignableFrom(type))
            {
                //進行對字典的讀取
                int count = PlayerPrefs.GetInt(name);
                IDictionary tempDictionary=Activator.CreateInstance(type) as IDictionary;
                for (int i = 0; i < count; i++)
                {
                    tempDictionary.Add(LoadValue(type.GetGenericArguments()[0], name + "_key_" + i),
                        LoadValue(type.GetGenericArguments()[1], name + "_value_" + i));
                }
                return tempDictionary;
            }
            else
            {
                //讀取自定義類成員的設定
                return LoadData(type, name);
            }
        }
    }
}

附:

測試指令碼

using System.Collections.Generic;
using UnityEngine;

namespace Framwork
{
    //注意:
    //1 自定義資料結構型別中要有有效的無參建構函式
    
    public class PlayerInfo
    {
        public int age;
        public string name;
        public float height;
        public int sex;//0是女 1是男

        public ItemInfo ItemInfo;
        //list儲存測試
        public List<int> list;
        public Dictionary<int, string> dic;
        
    }

    public class ItemInfo
    {
        public int stu_no;//學號
        public int stu_class;//班級

        public ItemInfo()
        {
            
        }
        public ItemInfo(int no,int classNo)
        {
            stu_no = no;
            stu_class = classNo;
        }
    }
    /// <summary>
    /// 測試類
    /// </summary>
    public class TestPlayerPrefsTest:MonoBehaviour
    {
        private PlayerInfo playerInfo;
        private PlayerInfo playerInfo1;
        private void Start()
        {
             //讀取資料
             playerInfo = new PlayerInfo();         
            // Type fieldType = playerInfo.GetType();
             playerInfo.age = 18;
             playerInfo.name = "TonyChang";
             playerInfo.height = 175.8f;
             playerInfo.sex = 1;
             playerInfo.ItemInfo = new ItemInfo(2001, 2);

             playerInfo.list = new List<int>(){1,5,6,8};
             playerInfo.dic = new Dictionary<int, string>();
             playerInfo.dic.Add(1,"Tony");
             playerInfo.dic.Add(2,"Jeny");
             playerInfo.dic.Add(3,"JayChou");

             //進行資料儲存
             PlayerPrefsManager.Instance.SaveData(playerInfo,"Player1");
             
             playerInfo1 = PlayerPrefsManager.Instance.LoadData(typeof(PlayerInfo), "Player1") as PlayerInfo;

             Debug.Log("age=="+playerInfo1.age);
             Debug.Log("name=="+playerInfo1.name);
             Debug.Log("sex=="+playerInfo1.sex);
             Debug.Log("List[1]=="+playerInfo1.list[1]);
             Debug.Log("Dic[1]=="+playerInfo1.dic[1]);
        }
    }
}