ORK FrameWork - 自定义存储数据ISaveData接口

最近遇到了想通过scrip利用ORK FrameWork中的ISaveData存储自定义数据这方面的需求,网上的资料特别少,自己绕了两天才从这个泥潭里绕出来,所以本着自己年初给自己立下了规矩的原则,所以现在记下来,防止之后重蹈覆辙。

本以为这个接口像Unity自带的PlayerPrefs一样,Set/Get, 直观的存进去,直观的拿出来,结果证明自己还是too young too simple了。。。

虽然ISave的接口定义是这样的

namespace ORKFramework
{
    public interface ISaveData
    {
        void LoadGame(DataObject data);
        DataObject SaveGame();
    }
}

其中DataObject的定义是这样的:

namespace ORKFramework
{
    public sealed class DataObject
    {
        public DataObject();

        public bool Contains(string key);
        public bool ContainsArray(string key);
        public void Get(string key, ref T value);
        public bool Get(string key, out T[] value);
        public Dictionary<string, T[]> GetArrayData(Type type);
        public Dictionary<string, T> GetData(Type type);
        public ORKDataFile GetDataFile(string name, bool encrypt);
        public DataObject GetFile(string key);
        public DataObject[] GetFileArray(string key);
        public void Set(string key, T value);
        public void Set(string key, T[] value);
        public void SetArrayData(Dictionary<string, T[]> data, Type type);
        public void SetData(Dictionary<string, T> data, Type type);
    }
}

官网上说了,如果想实现存储自己自定义的数据就要实现ISaveData中的SaveGame和LoadGame这两个方法。官网给的例子是这样的:
Custom Component Save Data

using UnityEngine;
using ORKFramework;

public class ComponentSaveTest : MonoBehaviour, IComponentSaveData
{
    // save key used to store different components of this class
    public string saveKey = "";


    // save values
    public bool toggle = false;

    public float number = 0;

    public string text = "";


    public string GetSaveKey()
    {
        return saveKey;
    }

    // called when the component data is saved/stored
    public DataObject SaveGame()
    {
        DataObject data = new DataObject();

        data.Set("toggle", this.toggle);
        data.Set("number", this.number);
        data.Set("text", this.text);

        return data;
    }

    // called when the component data is loaded
    public void LoadGame(DataObject data)
    {
        if(data != null)
        {
            data.Get("toggle", ref this.toggle);
            data.Get("number", ref this.number);
            data.Get("text", ref this.text);
        }
    }
}

But,But,But,这是个坑啊!!!
当我们想要存储自定义数据的时候,实际上除了例子中的SaveGame和LoadGame,如果想将有某些特性的东西作为一个整体存进这里边的话,就需要在这个对象类中再实现一组SaveGame和LoadGame方法,相当于绕了一道。因为当这个对象作为一个整体的时候它只能是一个DataObject对象。(其实只要当时调试的时候我好好观察一下这个DataObject包含的属性就能明白了( ╯□╰ ))因为DataObject的私有成员只有:
audioData, eventData, fontData, gameObjectData, generalAssetData, guiSkinData, materialData, textureData, intData, intArrayData, floatData, floatArrayData, boolData, boolArrayData, stringData, stringArrayData, subData, subArrayData,其中subData就是DataObject。
所以我说我想用这个接口存一个GameObjectList,
ORK Framework说:你想的美!
つ﹏⊂

比如:
一所学校School有若干班级Class,一个Class包含若干学生Student,每个Student包含属性 ID(int), Name(string), Score(float), isLocal(bool)。
所以按照需求我需要存储School的属性,包括School Name(string),Location(string),一个包含若干Class的Data的subArrayData,而一个Class要有属性Class Name以及包含若干StudentData的subArrayData。

所以在StudentData类里实现的接口ISaveData为:

//StudentData类

public DataObject SaveGame()
{
    DataObject data = new DataObject();
    data.Set("ID", this.id);
    data.Set("Name", this.name);
    data.Set("Score", this.score);
    data.Set("isLocal", this.isLocal);

    return data;
}

public void LoadGame(DataObject dataObj)
{
    if(dataObj != null)
    {
        dataObj.Get("ID", ref this.id);
        dataObj.Get("Name", ref this.name);
        dataObj.Get("Score", ref this.score);
        dataObj.Get("isLocal", ref this.isLocal);
    }
}

然后在Student类中再实现一遍这个接口

// Student类

StudentData sData = new StudentData();
DataObjet data = new DataObje();
public DataObject SaveGame()
{
    data.Set("StudentData", sData.SaveGame());
    return data;
}

public void LoadGame(DataObje dataObj)
{
    if(dataObj != null)
    {
        dataObj.Get("StudentData", ref data);
    }
}

public void Func()
{
    this.sData.LoadGame(data);
    //Then we can get the sData.id, sData.Name, sData.Score, sData.isLocal.
    //...
}

然后对于Class的Data也有一个ClassData类,在这里的SaveGame接口中调用Student类中的SaveGame,达到存储这个Class中Student信息的目的

//ClassData类

List dObjList = new List();
DataObject[] dObjArray = new DataObject[100];      //假设有100人
DataObject data = new DataObje();
public DataObject SaveGame()
{
    for(int i = 0; i < student.Count; i++)
    {
        if(!dObjList.Contains(student.SaveGame())
        {
            dObjList.Add(student.SaveGame());
        }
    }
    data.Set("StudentList", dObjList.ToArray());
    return data;
}

public void LoadGame(DataObje dataObj)
{
    if(dataObj != null)
    {
        dataObj.Get("StudentList", out dObjArray);  
    }
}

同样在Class类中还要再实现一遍属于Class类的SaveGame和LoadGame接口,和Student类中一样,就不敲了。

值得一提的还有当我LoadGame后取到之前存的数据后,想要实例化所有之前存的student,想要实例化就要知道prefab的名字,假设虽然student有100个,但是实际上工程内用的prefab只有4种,记录的数据是每种prefab实例化了25个放在地图的不同位置上,而DataObject的个数据的存储方式用的是Dictionary,又恰巧我存进去的key和prefab的名字完全不一样,但是其中student的一个属性type(string)是prefab的名字,那么问题就变成了在得到数据后怎么在Class类里取得student的type这个属性。
这就是DataObject中的另一个函数GetData了,它是根据类型返回一个DataObject中存储了所有该类型的Dictionary, 比如student中的name,type属性都是string,那么运用

Dictionary<string, string> sDataDic = sDataObjList[i].GetData<string>("abcd".GetType());
string stype = sDataDic["Type"];

估计还有更好的方法处理这个ORK Framework中这个存储数据的方式,但是我的脑子现在也就想到这些了,以后如果有新发现再做进一步优化。

你可能感兴趣的:(ORK,Framework)