欧美bbbwbbbw肥妇,免费乱码人妻系列日韩,一级黄片

Unity實現(xiàn)游戲存檔框架

 更新時間:2020年01月19日 10:14:42   作者:hackerzhuli  
這篇文章主要為大家詳細介紹了Unity實現(xiàn)游戲存檔框架,文中示例代碼介紹的非常詳細,具有一定的參考價值,感興趣的小伙伴們可以參考一下

最近重構(gòu)了一下我的存檔框架。我在這里對實現(xiàn)方法進行簡單的解析。注意這里主要演示算法,所以,效率上并不是最佳。一個游戲中,可能有成百上千個物體需要存儲,而且有幾十種類型,接下來就用一個簡單的例子來解釋。一個很簡單的例子,有一個Unit(單位)類型,有一個Inventory(背包)類型,有一個Item(道具)類型。

接下來先介紹框架中最重要的接口,ISavable,表示這個類型可以存檔

public interface ISavable{
 uint Id {get; set;}
 Type DataType {get;} // 存檔數(shù)據(jù)類型
 Type DataContainerType {get;} // 存檔數(shù)據(jù)容器類型
 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上就可以實現(xiàn)該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{
  // 包含實際數(shù)據(jù)的數(shù)據(jù)類
  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(){
    // 實現(xiàn)略過
  }
  
  // 將場景物體中的數(shù)據(jù)存入到_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中的數(shù)據(jù)載入到場景物體中
  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);
  }
}

SaveDataContainer:

// 這個類型存儲了實際的數(shù)據(jù),相當于是一個數(shù)據(jù)庫
[Serializable]
public class SaveDataContainer{
  // 這個中存儲這實際物體的數(shù)據(jù),需要將這個字典轉(zhuǎn)換成數(shù)組并序列化
  private Dictionary<uint, IId> _data;
 
  public Dictionary<unit, IId> Data{get{return _data}}
 
  public IId GetData(uint id){
    return _data[id];
  }
 
  public void SetData(IId data){
    _data[data.Id] = data;
  }
}

好了,框架就講到這里,接下來實現(xiàn)示例代碼:

Unit:

[Serializable]
public class UnitSave:IId{
  [SerializeField]
  private uint _id;
  public uint PrefabId;
  public uint InventoryId;
  public int Hp;
  public int Level;
  public uint Id {get{return _id;}set{_id = value;}}
}
 
public class Unit:MonoBehaviour, ISavable{
  public int Hp;
  public int Level;
  public int PrefabId;
  public Inventory Inventory;
  
  public uint Id{get;set;}
  ISavable.DataType{get{return typeof(UnitSave);}}
  ISavable.DataContainerType{get{return typeof(ExampleSaveDataContainer);}}
  ISavable.Read(object data){
    UnitSave save = data as UnitSave;
    Hp = save.Hp;
    Level = save.Level;
  }
 
  ISavable.Write(object data){
    UnitSave save = data as UnitSave;
    save.Hp = Hp;
    save.Level = Level;
    save.InventoryId = Inventory.Id;
  }
}

Inventory: 

[Serializable]
public class InventorySave:IId{
  [SerializeField]
  private uint _id;
  public uint UnitId;
  public uint[] Items;
  public uint Id{get{return _id;}set{_id = value;}}
}
 
public class Inventory:MonoBehaviour, ISavable, ISavableContainer{
  public Unit Unit;
  public List<Item> Items;
 
  public uint Id{get;set;}
  ISavable.DataType{get{return typeof(InventorySave);}}
  ISavable.DataContainerType{get{return typeof(ExampleSaveDataContainer));}}
  ISavable.Read(object data){
    // 空
  }
  ISavable.Write(object data){
    InventorySave save = data as InventorySave;
    save.UnitId = Unit.Id;
    save.Items = Items.Select(item => item.Id).ToArray();
  }
 
  ISavableContainer.Savables{
    return Items;
  }
}

Item:

[Serializable]
public ItemSave: IId{
  [SerializeField]
  private uint _id;
  public uint PrefabId;
  public int Count;
  public uint Id{get{return _id;}set{_id = value;}}
}
 
// 道具并不是繼承自MonoBehaviour的,是一個普通的類
public class Item:ISavable{
  // 道具源數(shù)據(jù)所在Prefab,用于重新創(chuàng)建道具
  public uint PrefabId;
  public int Count;
  public uint Id {get;set;}
 
  public uint Id{get;set;}
  ISavable.DataType{get{return typeof(ItemSave);}}
  ISavable.DataContainerType{get{return typeof(ExampleSaveDataContainer));}}
  ISavable.Read(object data){
    ItemSave save = data as ItemSave;
    Count = save.Count;
  }
  ISavable.Write(object data){
    ItemSave save = data as ItemSave;
    save.PrefabId = PrefabId;
    save.Count = Count;
  }
}

ExampleSaveDataContainer:

[Serializable]
public class ExampleSaveDataContainer: SaveDataContainer, ISerializationCallbackReceiver {
  public UnitSave[] Units;
  public ItemSave[] Items;
  public InventorySave[] Inventories;
 
  public void OnBeforeSerialize(){
    // 將Data字典中的數(shù)據(jù)復(fù)制到數(shù)組中,實現(xiàn)略過
  }
 
  public void OnAfterDeserialize(){
    // 將數(shù)組中的數(shù)據(jù)賦值到Data字典中,實現(xiàn)略過
  }
}

ExampleGame:

public class ExampleGame:MonoBehaviour{
 
  public void LoadGame(SaveFile file){
    // 從文件中讀入數(shù)據(jù)到SaveDataContainer
    file.LoadFromFile<ExampleSaveDataContainer>();
    SaveDataContainer dataContainer = file.SaveDataContainer;
 
    // 創(chuàng)建所有物體并賦值相應(yīng)Id
    Unit[] units = dataContainer.Units.Select(u=>CreateUnit(u));
    Item[] items = dataContainer.Items.Select(item=>CreateItem(item));
 
    // 將道具放入相應(yīng)的道具欄中
    foreach(Unit unit in units){
      uint inventoryId = unit.Inventory.Id;
      InventorySave inventorySave = dataContainer.GetData(inventoryId);
      foreach(Item item in items.Where(i=>inventorySave.Items.Contains(i.Id))){
        unit.Inventory.Put(item);
      }
    }
 
    // 調(diào)用Load進行實際的數(shù)據(jù)載入
    file.Load();
  }
 
  public void SaveGame(SaveFile file){
    // 相對來說,存檔的實現(xiàn)比載入簡單了許多
    file.Save<ExampleSaveDataContainer>();
    file.SaveToFile();
  }
 
  public Unit CreateUnit(UnitSave save){
    Unit unit = Instantiate(GetPrefab(save.PrefabId)).GetComponent<Unit>();
    unit.Id = save.Id;
    unit.Inventory.Id = save.InventoryId;
    return unit;
  }
 
  public Item CreateItem(ItemSave save){
    Item item = GetPrefab(save.PrefabId).GetComponent<ItemPrefab>().CreateItem();
    item.Id = save.Id;
    return item;
  }
}

使用方法:

給單位Prefab中的Unit組件和Inventory組件所在的GameObject上放SaveEntity組件即可。

思考問題:

1.擴展功能,讓SaveFile包含一個SaveDataContainer數(shù)組,這樣子可以實現(xiàn)包含多個數(shù)據(jù)容器(數(shù)據(jù)庫)的情況
2.對SaveFile存儲內(nèi)容進行壓縮,減少存儲體積
3.SaveFile存儲到文件時進行加密,避免玩家修改存檔
4.如何避免存儲時候卡頓 

存儲過程:

1.從場景中搜集數(shù)據(jù)到SaveFile中(SaveFile.Save),得到一個SaveFileData的數(shù)據(jù)
2.將SaveFileData序列化成一個json字符串
3.對字符串進行壓縮
4.對壓縮后的數(shù)據(jù)進行加密
5.將加密后的數(shù)據(jù)存儲于文件 

可以發(fā)現(xiàn),只要完成第1步,得到一個SaveFileData,實際上就已經(jīng)完成了存檔了,接下來實際上就是一個數(shù)據(jù)轉(zhuǎn)換的過程。所以,這也給出了避免游戲卡頓的一種方法:

完成第一步之后,將后面的步驟全部都放到另一個線程里面處理。實際上,第一步的速度是相當快的。往往不會超過50ms,可以說,卡頓并不會很明顯。

以上就是本文的全部內(nèi)容,希望對大家的學(xué)習(xí)有所幫助,也希望大家多多支持腳本之家。

相關(guān)文章

  • C#/VB.NET實現(xiàn)在Word文檔中添加頁眉和頁腳

    C#/VB.NET實現(xiàn)在Word文檔中添加頁眉和頁腳

    頁眉位于文檔中每個頁面的頂部區(qū)域,常用于顯示文檔的附加信息;頁腳位于文檔中每個頁面的底部的區(qū)域,常用于顯示文檔的附加信息。今天這篇文章就將為大家展示如何以編程的方式在在?Word?文檔中添加頁眉和頁腳
    2023-03-03
  • C#中使用Override和New關(guān)鍵字進行版本控制

    C#中使用Override和New關(guān)鍵字進行版本控制

    在?C#?中,override?和?new?關(guān)鍵字用于控制類之間的成員方法的隱藏和重寫,理解它們之間的差異和使用場景對于設(shè)計靈活且易于維護的代碼至關(guān)重要,在這篇博客中,我們將詳細探討這兩個關(guān)鍵字的用法,并通過示例來說明它們的實際應(yīng)用,需要的朋友可以參考下
    2024-10-10
  • C#異步編程詳解

    C#異步編程詳解

    本文主要介紹異步編程中Task、Async和Await的基礎(chǔ)知識。具有很好的參考價值,下面跟著小編一起來看下吧
    2017-02-02
  • C#?TreeView控件使用技巧匯總

    C#?TreeView控件使用技巧匯總

    這篇文章主要介紹了C#?TreeView控件使用技巧匯總,TreeView控件在窗體應(yīng)用里面使用也是頻率比較高的,我們在使用TreeView一般是對資源的分層展示,類似數(shù)據(jù)結(jié)構(gòu)里面樹的凹入表示法
    2022-08-08
  • C# 使用Log4net添加日志記錄的方法

    C# 使用Log4net添加日志記錄的方法

    本文主要介紹了C# 使用Log4net添加日志記錄的方法,文中通過示例代碼介紹的非常詳細,對大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價值,需要的朋友們下面隨著小編來一起學(xué)習(xí)學(xué)習(xí)吧
    2022-04-04
  • C#批量插入數(shù)據(jù)到Sqlserver中的三種方式

    C#批量插入數(shù)據(jù)到Sqlserver中的三種方式

    這篇文章主要為大家詳細介紹了C#批量插入數(shù)據(jù)到Sqlserver中的三種方式,具有一定的參考價值,感興趣的小伙伴們可以參考一下
    2016-12-12
  • c# 配置文件App.config操作類庫的方法

    c# 配置文件App.config操作類庫的方法

    下面小編就為大家?guī)硪黄猚# 配置文件App.config操作類庫的方法。小編覺的挺不錯的,現(xiàn)在就分享給大家,也給大家做個參考。一起跟隨小編過來看看吧
    2016-12-12
  • c# 引用Nlog插件的步驟

    c# 引用Nlog插件的步驟

    這篇文章主要介紹了c# 引用Nlog插件的步驟,幫助大家更好的理解和學(xué)習(xí)使用c#,感興趣的朋友可以了解下
    2021-04-04
  • C#常用日期時間方法匯總

    C#常用日期時間方法匯總

    這篇文章介紹了C#常用的日期時間方法,文中通過示例代碼介紹的非常詳細。對大家的學(xué)習(xí)或工作具有一定的參考借鑒價值,需要的朋友可以參考下
    2022-04-04
  • C#刪除UL LI中指定標簽里文字的方法

    C#刪除UL LI中指定標簽里文字的方法

    這篇文章主要介紹了C#刪除UL LI中指定標簽里文字的方法,涉及C#針對頁面HTML元素進行正則匹配與替換的相關(guān)操作技巧,需要的朋友可以參考下
    2017-05-05

最新評論