适用于QF的存档系统
存档系统
今天分享一个适用于QF的存档系统
这个系统的优点为
1、轻量化,总共代码不超过400行
2、自动化,基于QF框架,自动注入值
缺点:
1、不能序列化Unity内部类型,如Vector
2、需要给能被序列化的类加上【Serialize】属性
依赖下载
在Unity的包管理器中,输入Newtonsoft.json,下载依赖
模块分析
Storage:工具模块
维护一个数据缓冲区,负责将数据从磁盘中读取到缓冲区中,并且从缓冲区获取数据写入磁盘
using QFramework;
namespace EggFramework.Storage
{
public interface IStorage : IUtility
{
void AssignSavePath(string path = "saveData");
void ReadFromDisk();
void WriteToDisk();
T Load<T>(string key, T defaultValue,out bool newValue);
void Save<T>(string key, T saveData);
void Clear();
}
}
接口模块定义如上
FileSystem:系统模块
维护一个保存了存档名的列表,并且负责实际注册数据,加载数据,保存数据
using QFramework;
using System.Collections.Generic;
namespace EggFramework.FileSystem
{
public interface IFileSystem : ISystem
{
List<string> FileNames { get; }
void Register<T>(BsProperty<T> savable, string name);
void Unregister<T>(BsProperty<T> savable);
void CreateFile(string fileName);
//自动加载当前的存档
void LoadFile(string fileName = "File1");
void SaveFile();
void DeleteFile(string fileName);
void DeleteAll();
}
}
其中BSProperty定义如下
#region
//文件创建者:Egg
//创建时间:09-10 02:24
#endregion
using System;
using QFramework;
namespace EggFramework.FileSystem
{
public sealed class BsProperty<T> : BindableProperty<T>, ISavable
{
Action ISavable.OnLoad { get; set; } = () => { };
Action ISavable.OnSave { get; set; } = () => { };
}
}
#region
//文件创建者:Egg
//创建时间:09-10 02:23
#endregion
using System;
namespace EggFramework.FileSystem
{
public interface ISavable
{
Action OnLoad { get; set; }
Action OnSave { get; set; }
}
}
通过ISavable限制BSProperty,并且通过在框架初始化的时候,将数据注册到FileSystem,加载时,统一将数据注入BSProperty。
Storage实现
using System.Collections.Generic;
using System.IO;
using Newtonsoft.Json;
using UnityEngine;
namespace EggFramework.Storage
{
public sealed class JsonStorage : IStorage
{
private string _savePath = "saveData.json";
private Dictionary<string, object> _saveData = new();
public void ReadFromDisk()
{
var path = Path.Combine(Application.persistentDataPath, _savePath);
Debug.Log(path);
if (File.Exists(path))
{
var json = File.ReadAllText(Path.Combine(Application.persistentDataPath, _savePath));
_saveData = JsonConvert.DeserializeObject<Dictionary<string, object>>(json);
Debug.Log("读取成功");
}
else
{
_saveData.Clear();
}
}
public void WriteToDisk()
{
var json = JsonConvert.SerializeObject(_saveData);
File.WriteAllText(Path.Combine(Application.persistentDataPath, _savePath), json);
}
public void Save<T>(string key, T saveData)
{
if (saveData == null)
{
Debug.LogWarning("Saving null data");
}
if (_saveData.TryAdd(key, saveData))
{
_saveData[key] = saveData;
}
}
public T Load<T>(string key, T defaultValue, out bool newValue)
{
newValue = false;
if (!_saveData.TryGetValue(key, out var saveValue))
{
newValue = true;
return defaultValue;
}
//5月15日补丁,对直接使用的布尔类型区别处理
if (typeof(T) == typeof(bool))
{
return (T)saveValue;
}
return JsonConvert.DeserializeObject<T>(saveValue.ToString());
}
public void AssignSavePath(string path = "saveData")
{
_savePath = path + ".json";
}
public void Clear()
{
_saveData.Clear();
}
}
}
FileSystem实现
using QFramework;
using System.Collections.Generic;
using EggFramework.Storage;
using UnityEngine;
namespace EggFramework.FileSystem
{
public sealed class FileSystem : AbstractModule, IFileSystem
{
public List<string> FileNames { get; private set; } = new();
private bool _inited;
private bool _loaded;
private IStorage _storage;
private readonly List<ISavable> _savableProperties = new();
private readonly Dictionary<ISavable, object> _defaultValues = new();
private string _currentFile = string.Empty;
public void DeleteFile(string fileName)
{
MakeSureInited();
if (FileNames.Contains(fileName))
{
_storage.AssignSavePath(fileName);
_storage.Clear();
_storage.WriteToDisk();
FileNames.Remove(fileName);
Debug.Log("删除成功");
}
else
{
Debug.Log("要删除的存档不存在");
}
}
public void DeleteAll()
{
MakeSureInited();
foreach (var fileName in FileNames)
{
DeleteFile(fileName);
}
}
private void MakeSureInited()
{
if (string.IsNullOrEmpty(_currentFile))
{
if (FileNames == null)
{
OnInit();
}
if (FileNames!.Count == 0)
{
FileNames.Add("File1");
}
_currentFile = FileNames[0];
}
}
private T Load<T>(string key, T defaultValue, out bool isNew)
{
MakeSureInited();
return _storage.Load(key, defaultValue, out isNew);
}
private void LoadFileInner()
{
MakeSureInited();
SaveFileName();
//清除缓存区
_storage.Clear();
//指定路径
_storage.AssignSavePath(_currentFile);
//加载数据
_storage.ReadFromDisk();
//为每个已注册值赋值
foreach (var savable in _savableProperties)
{
savable.OnLoad?.Invoke();
}
_loaded = true;
}
//必須在所有值都註冊完畢後,调用LoadFile,否则无法填充对应的值
public void LoadFile(string fileName = "File1")
{
_currentFile = fileName;
LoadFileInner();
}
/// <summary>
///用于将存档名写入文件
///
/// </summary>
private void SaveFileName()
{
MakeSureInited();
//指定路径
_storage.AssignSavePath();
//读取磁盘
_storage.ReadFromDisk();
//写入缓存区
_storage.Save(nameof(FileNames), FileNames);
//写入磁盘
_storage.WriteToDisk();
}
private void Save<T>(string key, T value)
{
MakeSureInited();
_storage.Save(key, value);
}
public void SaveFile()
{
if (!_loaded)
{
Debug.LogWarning("需要先加载一个存档才行");
return;
}
_storage.AssignSavePath(_currentFile);
_storage.Clear();
//依次调用onSave方法,把注册数据写入缓存区
foreach (var savable in _savableProperties)
{
savable.OnSave?.Invoke();
}
//将缓存区中的数据写入文件
_storage.WriteToDisk();
//将存档名写入文件
SaveFileName();
}
protected override void OnInit()
{
if (!_inited)
{
_storage = this.GetUtility<IStorage>();
_storage.AssignSavePath();
FileNames = _storage.Load(nameof(FileNames), new List<string>(), out _);
_inited = true;
}
}
//CreateFile有两个重载
//可以自定义存档名或者默认存档名
public void CreateFile(string fileName)
{
MakeSureInited();
if (!FileNames.Contains(fileName))
{
FileNames.Add("File" + fileName);
SaveFileName();
}
}
public void Register<T>(BsProperty<T> savable, string name)
{
if (!_savableProperties.Contains(savable))
{
_savableProperties.Add(savable);
_defaultValues.Add(savable, savable.Value);
var saveItem = (ISavable)savable;
saveItem.OnLoad += () =>
{
var value = Load(name, (T)_defaultValues[savable], out var isNew);
if (!isNew)
savable.Value = value;
else
savable.Value = (T)_defaultValues[savable];
};
saveItem.OnSave += () => Save(name, savable.Value);
}
else Debug.Log("重复注册值");
}
public void Unregister<T>(BsProperty<T> savable)
{
if (_savableProperties.Contains(savable))
{
_savableProperties.Remove(savable);
var saveItem = (ISavable)savable;
saveItem.OnLoad = null;
saveItem.OnSave = null;
}
else Debug.Log("重复注销值");
}
}
}
#region
//文件创建者:Egg
//创建时间:09-11 07:30
#endregion
using QFramework;
namespace EggFramework
{
public abstract class AbstractModule : AbstractSystem
{
}
}