当前位置 主页 > 网站技术 > 代码类 >

    Unity实现游戏存档框架

    栏目:代码类 时间:2020-01-19 12:07

    最近重构了一下我的存档框架。我在这里对实现方法进行简单的解析。注意这里主要演示算法,所以,效率上并不是最佳。一个游戏中,可能有成百上千个物体需要存储,而且有几十种类型,接下来就用一个简单的例子来解释。一个很简单的例子,有一个Unit(单位)类型,有一个Inventory(背包)类型,有一个Item(道具)类型。

    接下来先介绍框架中最重要的接口,ISavable,表示这个类型可以存档

    public interface ISavable{
     uint Id {get; set;}
     Type DataType {get;} // 存档数据类型
     Type DataContainerType {get;} // 存档数据容器类型
     void Read(object data);
     void Write(object data);
    }

    ISavableContainer,用来返回一组ISavable的容器:

    public interface ISavableContainer{
      IEnumerable<ISavable> Savables;
    }

    IId, 具有Id的接口:

    public interface IId
    {
      uint Id {get; set;}
    }

    SaveEntity, 这是一个MonoBehaviour,将这个组件放到需要存档的GameObject上就可以实现该GameObject的存档了,这是最核心的类之一:

    public class SaveEntity : MonoBehaviour{
      public void Save(SaveDataContainer container){
        foreach(ISavable savable in GetSavables()){
          if(savable.DataContainerType = container.GetType()){
            IId newData = Activator.CreateInstance(savable.DataType) as IId;
            newData.Id = savable.Id;
            savable.Write(newData);
            container.SetData(newData);
          }
        }
      }
     
      public void Load(SaveDataContainer container){
        foreach(ISavable savable in GetSavables()){
          if(savable.DataContainerType = container.GetType()){
            IId data = container.GetData(savable.Id);
            savable.Read(data);
          }
        }    
      }
     
      public IEnumerable<ISavable> GetSavables(){
        foreach(ISavable savable in GetComponents<ISavable>()){
          yield return savable;
        }
        foreach(ISavable savableContainer in GetComponents<ISavableContainer>()){
          foreach(ISavable savable in savableContainer.Savables){
            yield return savable;
          }
        }
      }
    }

    SaveFile代表一个文件

    [Serializable]
    public class SaveFileData{
      public uint CurId;
      public string DataContainer;
    }
     
    // 代表一个存档文件
    public class SaveFile: MonoBehaviour{
      // 包含实际数据的数据类
      private SaveDataContainer _saveDataContainer;
      private uint _curId;
     
      public string Path{get;set;}
      public SaveDataContainer SaveDataContainer{get{return _saveDataContainer;}}
     
      private uint NextId{get{return ++_curId;}}
     
      // 得到场景里所有的SaveEntity
      private IEnumerable<SaveEntity> GetEntities(){
        // 实现略过
      }
      
      // 将场景物体中的数据存入到_saveDataContainer中
      public void Save<T>() where T:SaveDataContainer, new()
      {
        // 一轮Id赋值,保证Id为0的所有ISavable都赋值一个新Id
        foreach(SaveEntity entity in Entities){
          foreach (Savable savable in entity.GetSavables()){
            if(savable.DataContainerType == typeof(T)){
              if(savable.Id == 0){
                savable.Id = NextId;
              }
            }
          }
        }
     
        T dataContainer = new T();
     
        foreach(SaveEntity entity in Entities){
          entity.Save(this, dataContainer);
        }
     
        _saveDataContainer = dataContainer;
      }
     
      // 将_saveDataContainer中的数据载入到场景物体中
      public void Load(){
        foreach(SaveEntity entity in Entities){
          entity.Load(this, _saveDataContainer);
        }
      }
     
      public void LoadFromFile<T>() where T:SaveDataContainer
      {
        string json = File.ReadAllText(Path);
        SaveFileData data = JsonUtility.FromJson<SaveFileData>(json);
        _saveDataContainer = JsonUtility.FromJson<T>(data.DataContainer);
        _curId = data.CurId;
      }
     
      public void SaveToFile(){
        SaveFileData data = new SaveFileData();
        data.CurId = _curId;
        data.DataContainer = JsonUtility.ToJson(_saveDataContainer);
        string json = JsonUtility.ToJson(data);
        File.WriteAllText(Path, json);
      }
    }