Skip to content

3.持久化本地存储

dingxiaowei edited this page Jan 29, 2019 · 1 revision

前言

Unity开发数据本地保存也是重要的一个基础功能,单机游戏肯定要将数据存储在本地,网络游戏可以以服务器存储为主,但也有的数据需要存在本地,例如存储玩家账号,Unity也为我们提供了一个数据持久化存储方案PlayerPrefas,当然也能满足我们基本存账号的需求,但他的不足也很明显,就是Editor上存储在注册表上,会污染我们本地的注册表,其次就是存储的数据类型有限,只能存一些很基本的数据类型,后者我还可以忍,但前者我总感觉用他不爽,所以就相出一个替代方案,自己写文件保存到本地,一开始想的是如何用一个文件去存储那么多Key Value,而且要保证读写的高效,我首先想到的是sqllite数据库,但还要引入外部插件感觉麻烦,如果用txt存可以确保数据的读写是能够存的,但又不太好确保读写的高效性,看到网上有EasySave插件实现了我想要的功能,我就看了下他的实现,给了我启发,于是我就借鉴他的思路自己去实现了一个数据持久化存储方案,这样就不要用Unity内置的PlayerPrefs,但我又纳闷为啥Unity那么多大牛做引擎开发的,怎么不借鉴EasySave的实现方式去改造PlayerPrefs。

实现思路

我们知道Unity有一个内置的可读写的文件存储路径是Application.persidentDataPath,我们可以将玩家存储的一个数据就保存成一个二进制文件,例如保存一个int类型12的数据,它对应的数据名叫“myint”,那么我们在这个路径创建一个名为myint的二进制文件,里面内存存储为12,但我们读的时候如何知道他是一个int值呢,这也是PlayerPrefs存储的值都是string的原因,他没有处理数据类型,全都当做string去存储,让开发者自己去强制转换成他想要的数据类型,但我们这里可以提供一个方案,就是再创建一个二进制文件,文件名就叫myint.int32,文件后缀是数据类型,这样我们保存值的时候就生成数据类型文件,获取值的时候去读取数据类型文件的后缀然后将值强转成对应的数值类型给开发者。

代码

using GF.Debug;
using System;
using System.IO;
using System.Runtime.Serialization.Formatters.Binary;
using UnityEngine;

namespace GF
{
    /// <summary>
    /// 数据存储
    /// </summary>
    public class DataSave
    {
        public static string DataExt(string dataName)
        {
            string filePathData = string.Format("/Extensions/{0}.dat", dataName);
            string fileExt = "";
            if (File.Exists(Application.persistentDataPath + filePathData))
            {
                BinaryFormatter bf = new BinaryFormatter();
                FileStream loadData = File.Open(Application.persistentDataPath + filePathData, FileMode.Open);
                fileExt = (string)bf.Deserialize(loadData);
                loadData.Close();
            }
            return fileExt;
        }

        /// <summary>
        /// 保存数据
        /// </summary>
        /// <typeparam name="T">数据类型</typeparam>
        /// <param name="fileName">数据存储Key</param>
        /// <param name="data">数据源</param>
        public static void Save<T>(string dataName, T data)
        {
            Type dataType = data.GetType();
            var extensionPath = string.Format("{0}/Extensions", Application.persistentDataPath);
            var dataTypePath = string.Format("{0}/DataType", Application.persistentDataPath);
            if (!Directory.Exists(extensionPath))
                Directory.CreateDirectory(extensionPath);
            if (!Directory.Exists(dataTypePath))
                Directory.CreateDirectory(dataTypePath);
            BinaryFormatter formatter = new BinaryFormatter();
            string filePathData = "/Extensions/" + dataName + ".dat";
            FileStream saveStreamData = File.Create(Application.persistentDataPath + filePathData);
            string dat = dataType.Name;
            if (dat.Contains("`")) dat = dat.Split('`')[0];
            if (dat.Contains("[]")) dat = "Array";
            formatter.Serialize(saveStreamData, dat);
            saveStreamData.Close();

            BinaryFormatter bf = new BinaryFormatter();
            string filePath = string.Format("/DataType/{0}.{1}", dataName, dat);
            FileStream saveStream = File.Create(Application.persistentDataPath + filePath);
            bf.Serialize(saveStream, data);
            saveStream.Close();
        }

        public static T Load<T>(string dataName)
        {
            T result = default(T);
            string filePathData = string.Format("/Extensions/{0}.dat", dataName);
            string fileExt = DataExt(dataName);
            if (string.IsNullOrEmpty(fileExt))
                return result;

            string filePath = string.Format("/DataType/{0}.{1}", dataName, fileExt);
            if (File.Exists(Application.persistentDataPath + filePath))
            {
                BinaryFormatter bf = new BinaryFormatter();
                FileStream fs = File.Open(Application.persistentDataPath + filePath, FileMode.Open);
                result = (T)bf.Deserialize(fs);
                fs.Close();
            }
            else
            {
                Debugger.LogError("Doesn't Exit:" + filePath);
            }
            return result;
        }

        /// <summary>
        /// 检查数据是否存在
        /// </summary>
        /// <param name="dataName"></param>
        /// <returns></returns>
        public static bool Exit(string dataName)
        {
            string filePathData = string.Format("{0}/Extensions/{1}.dat", Application.persistentDataPath, dataName);
            if (File.Exists(filePathData))
                return true;
            else
                return false;
        }

        /// <summary>
        /// 删除某个数据
        /// </summary>
        /// <param name="dataName">数据名</param>
        public static void Delete(string dataName)
        {
            string filePathData = string.Format("{0}/Extensions/{1}.dat", Application.persistentDataPath, dataName);
            if (File.Exists(filePathData))
            {
                File.Delete(filePathData);
            }
            string dataExt = DataExt(dataName);
            if (!string.IsNullOrEmpty(dataExt))
            {
                string filePath = string.Format("{0}/DataType/{1}.{2}", Application.persistentDataPath, dataName, dataExt);
                if (File.Exists(filePath))
                {
                    File.Delete(filePath);
                }
            }
        }

        /// <summary>
        /// 删除所有数据
        /// </summary>
        public static void DeleteAllData()
        {
            var path = string.Format("{0}/Extensions", Application.persistentDataPath);
            if (Directory.Exists(path))
            {
                foreach (string file in Directory.GetFiles(path))
                {
                    File.Delete(file);
                }
            }
            path = string.Format("{0}/DataType", Application.persistentDataPath);
            if (Directory.Exists(path))
            {
                foreach (var file in Directory.GetFiles(path))
                {
                    File.Decrypt(file);
                }
            }
        }
    }
}

用法

基本上可以满足需求,能够存储绝大多数数据类型,包括视频,音频也是可以的哦!

可扩展

  • 可以添加数据加密
  • 可以继续扩展往EasySave功能上靠
Clone this wiki locally