unity插件Excel转换Proto插件-ExcelToProtobufferTool

unity插件Excel转换Proto插件-ExcelToProtobufferTool

    • [**ExcelToProtobufTool 插件文档**](#ExcelToProtobufTool 插件文档)
      • [**1. 插件概述**](#1. 插件概述)
      • [**2. 默认配置类:`DefaultIProtoPathConfig`**](#2. 默认配置类:DefaultIProtoPathConfig)
      • [**3. 自定义配置类**](#3. 自定义配置类)
      • [**4. 使用方式**](#4. 使用方式)
        • [**4.1 默认路径**](#4.1 默认路径)
        • [**4.2 自定义路径**](#4.2 自定义路径)
        • [**4.3 Excel 配置规则**](#4.3 Excel 配置规则)
      • [**5. 注意事项**](#5. 注意事项)
      • [**6. 总结**](#6. 总结)
  • Excel数值配置填充规则
      • [**Excel 文档配置规则说明(更新版)**](#Excel 文档配置规则说明(更新版))
        • [**1. 概述**](#1. 概述)
        • [**2. 工作表页签命名规则**](#2. 工作表页签命名规则)
        • [**3. 枚举表(_Enum 后缀)**](#3. 枚举表(_Enum 后缀))
          • **文件结构**
          • **示例**
          • [**转换为 Excel 表格**](#转换为 Excel 表格)
          • [**生成 Proto 文件**](#生成 Proto 文件)
          • [**生成 C# 脚本**](# 脚本**)
        • [**4. 类定义表(_Class 后缀)**](#4. 类定义表(_Class 后缀))
          • **文件结构**
          • **示例**
          • [**转换为 Excel 表格**](#转换为 Excel 表格)
          • [**生成 Proto 文件**](#生成 Proto 文件)
          • [**生成 C# 脚本**](# 脚本**)
        • [**5. 列表类表**](#5. 列表类表)
          • **文件结构**
          • **示例**
          • [**转换为 Excel 表格**](#转换为 Excel 表格)
          • [**生成 Proto 文件**](#生成 Proto 文件)
          • [**生成 C# 脚本**](# 脚本**)
        • [**6. 第一行词典 Key 的规则补充说明**](#6. 第一行词典 Key 的规则补充说明)
        • [**1. 规则说明**](#1. 规则说明)
        • [**2. 示例:地图 Map 配置**](#2. 示例:地图 Map 配置)
        • [**3. 定义嵌套字典的 Key 和 Value**](#3. 定义嵌套字典的 Key 和 Value)
        • [**4. 生成的数据结构**](#4. 生成的数据结构)
        • [**5. 生成逻辑**](#5. 生成逻辑)
        • [**6. 总结**](#6. 总结)
        • [**7. 生成的解析脚本**](#7. 生成的解析脚本)
        • [**8. 注意事项**](#8. 注意事项)
        • [**7. 字典类型的规则补充说明**](#7. 字典类型的规则补充说明)
          • [**1. Key 的类型限制**](#1. Key 的类型限制)
      • **字典字段的定义规则**
        • [**情况 1:`:` 前面是非 `:` 的字符**](#情况 1:: 前面是非 : 的字符)
        • [**情况 2:`:` 前面是 `::`**](#情况 2:: 前面是 ::)
      • **通用规则**
      • **示例详解**
        • [**示例 1:Value 为基础类型**](#示例 1:Value 为基础类型)
        • [**示例 2:Value 为枚举类型**](#示例 2:Value 为枚举类型)
        • [**示例 3:Value 为自定义类型**](#示例 3:Value 为自定义类型)
        • [**示例 4:自定义类型中嵌套列表类型**](#示例 4:自定义类型中嵌套列表类型)
        • [**示例 5:自定义类型中嵌套字典类型**](#示例 5:自定义类型中嵌套字典类型)
      • **定义**
        • [1. **`EffectItem` 类型的定义**](#1. EffectItem 类型的定义)
        • [2. **`ItemPrices` 类型的定义**](#2. ItemPrices 类型的定义)
      • **数据**
      • **最终生成的数据结构**
      • **示例代码(C#)**
      • **输出结果**
      • **说明**
      • **总结**
      • **自定义类数据填充注意事项**
        • [**1. 数据填充必须与自定义类的成员个数对应且成员总数相同**](#1. 数据填充必须与自定义类的成员个数对应且成员总数相同)
        • [**2. 数据填充顺序与 Proto 编码顺序一致**](#2. 数据填充顺序与 Proto 编码顺序一致)
        • [**3. 数据填充需要转义字符**](#3. 数据填充需要转义字符)
      • **总结**

ExcelToProtobufTool 插件文档

1. 插件概述

ExcelToProtobufTool 是一个 Unity 插件,用于将 Excel 配置文件转换为 Protobuf 数据格式,并生成对应的 C# 脚本或 DLL 文件。通过配置类 DefaultIProtoPathConfig,开发者可以自定义 Excel 文件路径、Protobuf 数据生成路径、DLL 生成路径等。

不支持.Net Standard,需要将功成切换到.Net Framework.


2. 默认配置类:DefaultIProtoPathConfig

属性说明
属性名称 类型 说明
PackagesRootPathName string 包根目录名称,默认值为 "Packages"
PackagesFullName string 完整的包路径,组合了根目录名称和包名。
PackagesPath string 包路径的完整路径,通过 DirectoryInfo 获取。
IsDebug bool 是否启用调试模式,默认值为 true
IsUsedDLL bool 是否使用生成的 DLL 文件。true 为打包成 DLL,false 为生成 C# 脚本。
ExcelPath string Excel 文件路径,默认指向 Config/Excel/Game
GenerateProtoPath string Protobuf 文件路径,默认指向 Config/ProtoFiles
GenerateProtoDataPath string Protobuf 生成的 Data 文件路径,默认指向 Res/ProtoData
GenerateProtoCsRootPath string 生成的 Protobuf C# 脚本路径,默认指向 Assets/Scripts/ProtoCSharp
ProtoDllName string Protobuf 脚本生成的 DLL 文件名,默认值为 CompanyName.ProtoBuffData
GenerateCsCachePath string 脚本或 DLL 的缓存路径,默认指向 Library/ProtoCache
GenerateProtoDllPath string Protobuf 生成的 DLL 路径,默认指向 Assets/Plugins/ProtoBuffData
ProtocPath string Protobuf 文件解析工具(protoc)的路径,根据平台动态调整。
GoogleProtobufPath string Google Protobuf 库的 DLL 路径,默认指向插件内置的 Google.Protobuf.dll

3. 自定义配置类

定义规则
  1. 继承 DefaultIProtoPathConfig

    • 自定义配置类必须继承自 DefaultIProtoPathConfig
  2. 添加 [ExecuteInEditMode] 特性

    • 确保配置在 Unity 编辑模式下生效。
  3. 实现静态构造函数

    • 在静态构造函数中注册自定义配置到 ProtoPathConfig.CurProtoPathConfig
  4. 重写需要自定义的属性

    • 根据项目需求,重写以下常用属性:
      • ExcelPath:Excel 文件路径。
      • GenerateProtoDataPath:Protobuf 数据比特流文件路径。
      • IsUsedDLL:是否使用生成的 DLL 文件。
      • GenerateProtoDllPath:Protobuf 生成的 DLL 路径。
      • GenerateProtoCsRootPath:Protobuf 生成的 C# 脚本路径。
示例代码
csharp 复制代码
using UnityEngine;
using HuaXianQu.ProtoBuffEx.Runtime;

// 添加 [ExecuteInEditMode] 特性,使脚本在编辑模式下运行
[ExecuteInEditMode]
public class CustomProtoPathConfig : DefaultIProtoPathConfig
{
    // 静态构造函数,用于注册自定义配置
    static CustomProtoPathConfig()
    {
        // 自动注册自定义配置
        ProtoPathConfig.CurProtoPathConfig = new CustomProtoPathConfig();
    }

    // 自定义 Excel 文件路径
    public override string ExcelPath => $"{Application.dataPath}/../../Config/Excel/CustomGame";

    // 自定义 Protobuf 数据比特流文件路径
    public override string GenerateProtoDataPath => $"{Application.dataPath}/Res/CustomProtoData";

    // 启用 DLL 模式
    public override bool IsUsedDLL => true;

    // 自定义 Protobuf 生成的 DLL 路径
    public override string GenerateProtoDllPath => "Assets/Plugins/CustomProtoBuffData";

    // 自定义 Protobuf 生成的 C# 脚本路径
    public override string GenerateProtoCsRootPath => "Assets/Scripts/CustomProtoCSharp";
}

4. 使用方式

4.1 默认路径

如果不自定义配置类,插件将使用默认路径:

  • Excel 文件路径$"{Application.dataPath}/../../Config/Excel/Game"
  • Protobuf 数据路径$"{Application.dataPath}/Res/ProtoData"
  • C# 脚本路径"Assets/Scripts/ProtoCSharp"
4.2 自定义路径

如果需要自定义路径,请按照以下步骤操作:

  1. 创建自定义配置类

    • 按照上述规则创建自定义配置类,并重写需要自定义的属性。
  2. 将自定义配置类放置在项目中

    • CustomProtoPathConfig 类放置在项目的任意脚本文件夹中(如 Assets/Scripts)。
  3. 插件自动使用配置

    • 插件会自动调用静态构造函数,注册并使用自定义配置。
4.3 Excel 配置规则

具体请查看 Excel数值配置填充规则章节

  1. Excel 文件格式

    • 使用 CSV 文件格式存储数据,方便生成 Excel 文件。

    • 示例数据:

      csv 复制代码
      ,
      1,2,3,4,5,6,7,8,9
      地图ID,地图名称,区域类型,怪物分布,背景音乐,地图等级,位置,大小,描述
      int,string,string,string,string,int,string,string,string
      MapID,Name,RegionType,MonsterDistribution,BackgroundMusic,MapLevel,Location,Size,Description
      1,新手村,安全区,"恶魔,魔王,沙虫,林,狼",轻松的背景音乐,1,"(0,0)",100x100,新手玩家的起点
      2,黑暗森林,危险区,"哥布林、狼,沙虫",紧张的音乐,5,"(100,50)",200x150,充满危险的森林
      3,地下城,副本,"骷髅战士,沙虫",神秘的背景音乐,10,"(-300,200)",150x100,隐藏着宝藏的地下城
      4,主城,安全区,"恶魔,魔王,沙虫,林,狼",欢快的背景音乐,1,"(-500,500)",300x300,玩家聚集的主城
      5,雪山,危险区,"雪狼,雪人,沙虫",寒冷的背景音乐,15,"(800,1000)",250x200,寒冷的雪山区域
      6,沙漠,危险区,"沙虫,蝎子,毒蛇",炎热的背景音乐,20,"(1200,1500)",300x250,炎热的沙漠区域
      7,沼泽,危险区,"沼泽怪,毒蛇",阴森的背景音乐,25,"(200,800)",180x150,危险的沼泽区域
      8,火山,危险区,"火焰巨人,熔岩兽",炽热的背景音乐,30,"(1500,2000)",350x300,炽热的火山区域
      9,精灵森林,安全区,"恶魔,魔王,沙虫,蝎子",自然的背景音乐,1,"(700,700)",200x200,精灵族的栖息地
      10,地狱,副本,"恶魔,魔王,沙虫",恐怖的背景音乐,35,"(2500,3000)",400x350,魔王统治的地狱区域
  2. 创建 Excel 文件

    • ExcelPath 指定的路径下创建 CSV 文件(如 Map.csv)。

    • 将上述数据复制到 CSV 文件中。

    • 注意 :保存格式必须为 UTF-8 with BOM,步骤如下:

      1. 打开 CSV 文件。

      2. 选择 文件 -> 另存为

      3. 在保存对话框中,选择编码格式为 UTF-8 with BOM

        选择编码格式为 UTF-8 with BOM

      4. 保存文件。

  3. 转换为 Excel 文件

    • 使用 Excel 打开 CSV 文件,保存为 .xlsx 格式。
  4. 生成 Protobuf 文件

    • 在 Unity 中,导航到菜单栏 Tools -> ExcelToCsharp
    • 等待进度完成,插件将在配置目录中生成对应的 Protobuf 文件、C# 脚本或 DLL 文件。
    • 成功后会弹出提示窗口:
  5. 查看生成的数据

    • 生成的 Protobuf 数据将以 JSON 形式显示在 Unity 的属性面板中。

5. 注意事项

  1. 路径配置

    • 确保自定义路径(如 ExcelPathGenerateProtoDataPath 等)在项目中存在且有效。
  2. 避免重复注册

    • 如果项目中存在多个自定义配置类,确保只有一个配置类被注册到 ProtoPathConfig.CurProtoPathConfig,避免冲突。
  3. 编辑模式测试

    • 由于 [ExecuteInEditMode] 特性的存在,可以在 Unity 编辑器中直接测试配置是否生效,无需进入运行模式。

测试实例

打开PackageManager导入实例

如下图

打开ProtoBuffDataTest.unity场景

主要测试代码ExcelWin.cs

运行结果

值得注意说明的是ExcelWin使用的Map.cs和MapItem使用的是案例里面的脚本。不是生成的脚本。原因是为了使测试用例运行正常。如果使用新编译的脚本或者dll请删除测试用例的脚本并修改HuaXianQu.ProtoBuffEx.Tests.Sample.asmdef

6. 总结

通过创建自定义配置类并重写相关属性,可以轻松配置 ExcelToProtobufTool 插件的行为。只需将自定义配置类放置在项目中,插件会自动使用自定义配置。结合 Excel 配置规则,开发者可以快速将 Excel 数据转换为 Protobuf 格式,并生成对应的 C# 脚本或 DLL 文件。



Excel数值配置填充规则

Excel 文档配置规则说明(更新版)


1. 概述

本文档详细说明了如何定义 枚举列表类 的字段和数据,并生成对应的 Proto 文件C# 脚本。通过遵循这些规则,您可以快速定义配置文件并生成代码。


2. 工作表页签命名规则
  • 枚举表 :以 _Enum 为后缀。
    • 示例:ItemType_EnumEffectType_Enum
  • 类定义表 :以 _Class 为后缀。
    • 示例:EffectItem_ClassBackpackItem_Class
  • 列表类表 :直接使用大驼峰命名法,无需后缀。
    • 示例:BackpackCharacter

3. 枚举表(_Enum 后缀)
文件结构
  1. 第一列:枚举名称,使用大驼峰命名法(PascalCase)。
  2. 其他列 :枚举值定义,格式为 枚举值名称:枚举值
    • 第一个枚举值必须为 0
    • 枚举值名称使用大驼峰命名法(PascalCase)。
    • 枚举值为整数,从 0 开始递增。
示例
csv 复制代码
ItemType,Consumable:0,Equipment:1,QuestItem:2,Currency:3
EffectType,Heal:0,Buff:1,Poison:2
转换为 Excel 表格
ItemType Consumable:0 Equipment:1 QuestItem:2 Currency:3
EffectType Heal:0 Buff:1 Poison:2
生成 Proto 文件
proto 复制代码
enum ItemType {
  Consumable = 0;
  Equipment = 1;
  QuestItem = 2;
  Currency = 3;
}

enum EffectType {
  Heal = 0;
  Buff = 1;
  Poison = 2;
}
生成 C# 脚本
csharp 复制代码
public enum ItemType
{
    Consumable = 0,  // 消耗品
    Equipment = 1,   // 装备
    QuestItem = 2,   // 任务物品
    Currency = 3     // 货币
}

public enum EffectType
{
    Heal = 0,  // 治疗
    Buff = 1,  // 增益
    Poison = 2 // 中毒
}

4. 类定义表(_Class 后缀)
文件结构
  1. 第一列:类名,使用大驼峰命名法(PascalCase)。
  2. 其他列 :成员定义,格式为 成员字段类型:成员字段名称:Proto文件字段编码
    • 支持基础类型、枚举类型、自定义类型、列表类型和字典类型。
示例
csv 复制代码
EffectItem,string:Name:1,int:Level:2,int:ID:3
BackpackItem,int:ItemID:1,string:ItemName:2,ItemType:ItemType:3,SellInfo:Sellable:4
SellInfo,bool:IsSellable:1,string:CurrencyType:2,PriceRange:Range:3
PriceRange,double:MinPrice:1,double:MaxPrice:2
ItemPrices,int:ItemID:1,"Dictionary<string,EffectItem>:Prices:2"
转换为 Excel 表格
EffectItem string:Name:1 int:Level:2 int:ID:3
BackpackItem int:ItemID:1 string:ItemName:2 ItemType:ItemType:3
SellInfo bool:IsSellable:1 string:CurrencyType:2 PriceRange:Range:3
PriceRange double:MinPrice:1 double:MaxPrice:2
ItemPrices int:ItemID:1 Dictionary<string,EffectItem>:Prices:2
生成 Proto 文件
proto 复制代码
message EffectItem {
  string Name = 1;
  int32 Level = 2;
  int32 ID = 3;
}
message BackpackItem {
  int32 ItemID = 1;
  string ItemName = 2;
  ItemType ItemType = 3;
  SellInfo Sellable = 4;
}
message SellInfo {
  bool IsSellable = 1;
  string CurrencyType = 2;
  PriceRange Range = 3;
}

message PriceRange {
  double MinPrice = 1;
  double MaxPrice = 2;
}
message ItemPrices {
  int32 ItemID = 1;
  map<string,EffectItem> Prices = 2;
}
生成 C# 脚本
csharp 复制代码
public class EffectItem
{
    public string Name { get; set; }  // Proto 编码: 1
    public int Level { get; set; }    // Proto 编码: 2
    public int ID { get; set; }       // Proto 编码: 3
}
public class BackpackItem
{
    public int ItemID { get; set; }           // Proto 编码: 1
    public string ItemName { get; set; }      // Proto 编码: 2
    public ItemType ItemType { get; set; }    // Proto 编码: 3
    public SellInfo Sellable { get; set; }    // Proto 编码: 4
}
public class SellInfo
{
    public bool IsSellable { get; set; }  // Proto 编码: 1
    public string CurrencyType { get; set; }  // Proto 编码: 2
    public PriceRange Range { get; set; }  // Proto 编码: 3
}

public class PriceRange
{
    public double MinPrice { get; set; }  // Proto 编码: 1
    public double MaxPrice { get; set; }  // Proto 编码: 2
}

public class ItemPrices
{
    public int ItemID { get; set; }           // Proto 编码: 1
    public MapField<string,EffectItem> Prices { get; set; }      // Proto 编码: 2
}

5. 列表类表
文件结构
  1. 第一行 :字段的附加属性(类似 C# 的特性),用 {} 括起来,包含多个键值对。
    • 示例:{key;EffectName:1,lan:Public,DefaultValue:0}
  2. 第二行 :字段的编码值,从 1 开始递增,表示生成 Proto 文件时变量对应的编码值。
    • 示例:1,2,3,4,5,6,7,8,9,10,11,12,13
  3. 第三行 :字段的功能描述,用中文简要说明每一列的作用。
    • 示例:唯一ID,物品名称,物品类型,最大堆叠数,是否可交易,是否可销毁,出售信息,出售价格列表,图标资源路径,描述,关联道具列表,效果列表,属性加成
  4. 第四行 :字段类型,用于转换成 C# 类型。
    • 支持类型:
      • 基础类型intuintlongulongdoublefloatboolstring
      • 枚举类型 :如 ItemType
      • 自定义类型 :如 EffectItem
      • 列表类型 :如 List<EffectItem>
      • 字典类型 :如 Dictionary<string, EffectItem>
  5. 第五行 :字段属性名称(大驼峰命名),方便代码调用。
    • 示例:ItemID,ItemName,ItemType,MaxStack,Tradable,Destructible,Sellable,SellPrices,IconPath,Description,LinkedItemIDs,Effects,AttributeBonus
  6. 第六行及以后:具体数据,按照字段类型和属性名称逐行填写。
示例
csv 复制代码
"{key;EffectName:1,lan:Public,DefaultValue:0}",
1,2,3,4,5,6,7,8,9,10,11,12,13,14
Unique ID,Item Name,Item Type,Max Stack Size,Tradable,Destructible,Sell Info,Sell Price List,Icon Path,Description,Linked Item IDs,Effects,Attribute Bonuses,Package Info
int,string,ItemType,int,bool,bool,SellInfo,List<PriceInfo>,string,string,List<int>,"Dictionary<string, EffectItem>","Dictionary<string, double>","Dictionary<int, ItemPrices>"
ItemID,ItemName,ItemType,MaxStack,Tradable,Destructible,Sellable,SellPrices,IconPath,Description,LinkedItemIDs,Effects,AttributeBonus,Info
1,Small Healing Potion,Consumable,99,TRUE,TRUE,"{TRUE,Gold,{10.0,20.0}}","{{Gold,10.0},{Silver,20.0},{Copper,30.0}}",icons/potion_small.png,Restores a small amount of health,"{1,2,3}","Name:{{Heal,1,50},{Buff,2,10},{Poison,3,30}}","::{Consumable:1.5,Equipment:0.5,QuestItem:2.0}","::{1:{10,Name:{{Fireball,5,9},{Thunderstorm,5,9}}}, 2:{20,Name:{{Blink,5,9},{BlackHole,5,9}}}}"
2,Large Healing Potion,Consumable,99,TRUE,TRUE,"{TRUE,Diamond,{50.0,100.0}}","{{Diamond,50.0},{Gold,100.0},{Silver,150.0}}",icons/potion_large.png,Restores a large amount of health,"{4,5}","Name:{{Heal,1,100},{Buff,2,20},{Poison,3,60}}","::{Consumable:2.0,Equipment:1.0,QuestItem:3.0}","::{1:{10,Name:{{Fireball,5,9},{Thunderstorm,5,9}}}, 2:{20,Name:{{Blink,5,9},{BlackHole,5,9}}}}"
3,Iron Sword,Equipment,1,TRUE,TRUE,"{FALSE,Gold,{200.0,400.0}}","{{Gold,200.0},{Silver,400.0},{Copper,600.0}}",icons/sword_iron.png,A common iron sword,{},"Name:{{Buff,2,15},{Poison,3,45},{Heal,1,75}}","::{Equipment:3.0,Consumable:1.0,QuestItem:0.5}","::{1:{10,Name:{{Fireball,5,9},{Thunderstorm,5,9}}}, 2:{20,Name:{{Blink,5,9},{BlackHole,5,11}}}}"
转换为 Excel 表格
{key;EffectName:1,lan:Public,DefaultValue:0}
1 2 3 4 5 6 7 8 9 10 11 12 13
唯一ID 物品名称 物品类型 最大堆叠数 是否可交易 是否可销毁 出售信息 出售价格列表 图标路径 描述 关联道具列表 效果 属性加成 背包信息
int string ItemType int bool bool SellInfo List string string List Dictionary<string, EffectItem> Dictionary<ItemType, double> Dictionary<string,ItemPrices>
ItemID ItemName ItemType MaxStack Tradable Destructible Sellable SellPrices IconPath Description LinkedItemIDs Effects AttributeBonus Info
1 小型治疗药水 Consumable 99 TRUE TRUE {TRUE,Gold,{10.0,20.0}} {{Gold,10.0},{Silver,20.0},{Copper,30.0}} icons/potion_small.png 回复少量生命值 {1,2,3} Name:{{Heal,1,50},{Buff,2,10},{Poison,3,30}} ::{Consumable:1.5,Equipment:0.5,QuestItem:2.0} ::{1:{10,Name:{{Fireball,5,9},{Thunderstorm,5,9}}}, 2:{20,Name:{{Blink,5,9},{BlackHole,5,9}}}}
2 大型治疗药水 Consumable 99 TRUE TRUE {TRUE,Diamond,{50.0,100.0}} {{Diamond,50.0},{Gold,100.0},{Silver,150.0}} icons/potion_large.png 回复大量生命值 {4,5} Name:{{Heal,1,100},{Buff,2,20},{Poison,3,60}} ::{Consumable:2.0,Equipment:1.0,QuestItem:3.0} ::{1:{10,Name:{{Fireball,5,9},{Thunderstorm,5,9}}}, 2:{20,Name:{{Blink,5,9},{BlackHole,5,9}}}}
3 铁剑 Equipment 1 TRUE TRUE {FALSE,Gold,{200.0,400.0}} {{Gold,200.0},{Silver,400.0},{Copper,600.0}} icons/sword_iron.png 一把普通的铁剑 {} Name:{{Buff,2,15},{Poison,3,45},{Heal,1,75}} ::{Equipment:3.0,Consumable:1.0,QuestItem:0.5} ::{1:{10,Name:{{Fireball,5,9},{Thunderstorm,5,9}}}, 2:{20,Name:{{Blink,5,9},{BlackHole,5,9}}}}
生成 Proto 文件
proto 复制代码
message EffectItem {
  string Name = 1;
  int32 Level = 2;
  int32 ID = 3;
}
message BackpackItem {
  int32 ItemID = 1;
  string ItemName = 2;
  ItemType ItemType = 3;
  SellInfo Sellable = 4;
}
message SellInfo {
  bool IsSellable = 1;
  string CurrencyType = 2;
  PriceRange Range = 3;
}

message PriceRange {
  double MinPrice = 1;
  double MaxPrice = 2;
}
message ItemPrices {
  int32 ItemID = 1;
  map<string,EffectItem> Prices = 2;
}

message BackpackItem {
  int32 ItemID = 1;
  string ItemName = 2;
  ItemType ItemType = 3;
  int32 MaxStack = 4;
  bool Tradable = 5;
  bool Destructible = 6;
  SellInfo Sellable = 7;
  repeated PriceInfo SellPrices = 8;
  string IconPath = 9;
  string Description = 10;
  repeated int32 LinkedItemIDs = 11;
  map<string, EffectItem> Effects = 12;
  map<ItemType, double> AttributeBonus = 13;
  map<ItemType, ItemPrices> Info = 13;
}
生成 C# 脚本
csharp 复制代码
public class EffectItem
{
    public string Name { get; set; }  // Proto 编码: 1
    public int Level { get; set; }    // Proto 编码: 2
    public int ID { get; set; }       // Proto 编码: 3
}
public class BackpackItem
{
    public int ItemID { get; set; }           // Proto 编码: 1
    public string ItemName { get; set; }      // Proto 编码: 2
    public ItemType ItemType { get; set; }    // Proto 编码: 3
    public SellInfo Sellable { get; set; }    // Proto 编码: 4
}
public class SellInfo
{
    public bool IsSellable { get; set; }  // Proto 编码: 1
    public string CurrencyType { get; set; }  // Proto 编码: 2
    public PriceRange Range { get; set; }  // Proto 编码: 3
}

public class PriceRange
{
    public double MinPrice { get; set; }  // Proto 编码: 1
    public double MaxPrice { get; set; }  // Proto 编码: 2
}

public class ItemPrices
{
    public int ItemID { get; set; }           // Proto 编码: 1
    public MapField<string,EffectItem> Prices { get; set; }      // Proto 编码: 2
}

public class BackpackItem
{
    public int ItemID { get; set; }
    public string ItemName { get; set; }
    public ItemType ItemType { get; set; }
    public int MaxStack { get; set; }
    public bool Tradable { get; set; }
    public bool Destructible { get; set; }
    public SellInfo Sellable { get; set; }  // 嵌套类
    public List<PriceInfo> SellPrices { get; set; }  // 列表类(嵌套类)
    public string IconPath { get; set; }
    public string Description { get; set; }
    public List<int> LinkedItemIDs { get; set; }
    public MapField<string, EffectItem> Effects { get; set; }
    public MapField<ItemType, double> AttributeBonus { get; set; }
}

6. 第一行词典 Key 的规则补充说明

1. 规则说明
  • 数据填充格式
    • 第一行使用 {key;EffectDict:编码} 定义嵌套字典的 Key。
    • 编码:决定 Key 的排序优先级(从 1 开始递增,数字越小优先级越高)。
    • 示例:{key;EffectDict:1} 表示该字段作为第一层 Key,{key;EffectDict:2} 表示该字段作为第二层 Key,依此类推。

2. 示例:地图 Map 配置

以下是一个地图配置的示例,定义了嵌套字典的 Key 和 Value。

csv 复制代码
{key;EffectDict:1},,"{key;EffectDict:2,key;EffectInfoDict:1}",{key;EffectDict:3},,{key;EffectInfoDict:2},,,
1,2,3,4,5,6,7,8,9
MapID,MapName,RegionType,MonsterDistribution,BackgroundMusic,MapLevel,Location,Size,Description
int,string,string,string,string,int,string,string,string
MapID,Name,RegionType,MonsterDistribution,BackgroundMusic,MapLevel,Location,Size,Description
1,Starter Village,Safe Zone,"Demon, Devil King, Sandworm, Forest, Wolf",Relaxing BGM,1,"(0,0)",100x100,Starting point for new players
2,Dark Forest,Danger Zone,"Goblin, Wolf, Sandworm",Intense BGM,5,"(100,50)",200x150,A dangerous forest
3,Dungeon,Instance,"Skeleton Warrior, Sandworm",Mysterious BGM,10,"(-300,200)",150x100,A dungeon filled with treasures
4,Main City,Safe Zone,"Demon, Devil King, Sandworm, Forest, Wolf",Cheerful BGM,1,"(-500,500)",300x300,The main city where players gather
5,Snowy Mountain,Danger Zone,"Snow Wolf, Yeti, Sandworm",Chilly BGM,15,"(-800,1000)",250x200,A cold snowy mountain area
6,Desert,Danger Zone,"Sandworm, Scorpion, Poisonous Snake",Hot BGM,20,"(-1200,1500)",300x250,A hot desert area
7,Swamp,Danger Zone,"Swamp Monster, Poisonous Snake",Eerie BGM,25,"(-200,800)",180x150,A dangerous swamp area
8,Volcano,Danger Zone,"Fire Giant, Lava Beast",Scorching BGM,30,"(-1500,2000)",350x300,A scorching volcano area
9,Elven Forest,Safe Zone,"Demon, Devil King, Sandworm, Scorpion",Natural BGM,1,"(-700,700)",200x200,The habitat of the elves
10,Hell,Instance,"Demon, Devil King, Sandworm",Terrifying BGM,35,"(-2500,3000)",400x350,The realm ruled by the Devil King

对应对的excel


3. 定义嵌套字典的 Key 和 Value
  • Key
    • {key;词典名称:编码} 定义的字段组成。
    • 示例中:
      • MapID 作为第一层 Key({key;EffectDict:1})。
      • RegionType 作为第二层 Key({key;EffectDict:2})。
      • MonsterDistribution 作为第三层 Key({key;EffectDict:3})。
  • Value
    • 最后一个字段是嵌套字典的 Value,即 MapItem 类型的数据。

4. 生成的数据结构

上面共定义两个词典EffectDict和EffectInfoDict

最终生成的数据结构为:

csharp 复制代码
Dictionary<int, Dictionary<string, Dictionary<string, MapItem>>>EffectDict;
Dictionary<string, Dictionary<int, MapItem>>EffectInfoDict;
  • EffectDict的Key
    • 第一层:MapIDint 类型)。
    • 第二层:RegionTypestring 类型)。
    • 第三层:MonsterDistributionstring 类型)。
  • EffectDict的Value
    • MapItem 类型的数据。
  • EffectInfoDict的Key
    • 第一层:RegionTypestring 类型)。
    • 第二层:MapLevelstring 类型)。
  • EffectInfoDict的Value
    • MapItem 类型的数据。

这两个词典有一个相同的key是RegionType。就是说一个字段可以作为多个词典的key


5. 生成逻辑

解析脚本的核心逻辑如下:

  1. 读取 CSV 数据
    • 跳过表头,从数据行开始解析。
  2. 解析每一行数据
    • 根据字段类型和属性名称,提取嵌套字典的 Key 和 Value。
  3. 构建嵌套字典
    • 使用 MapID 作为第一层 Key。
    • 使用 RegionType 作为第二层 Key。
    • 使用 MonsterDistribution 作为第三层 Key。
    • MapItem 数据作为 Value。

6. 总结
  • {key;词典名称:编码}

    • 用于定义嵌套字典的每一层 Key。
    • 编码决定 Key 的排序优先级(从 1 开始递增,数字越小优先级越高)。
  • 嵌套字典结构

    • 多个字段共同组成嵌套字典的 Key。
    • 最后一个字段是嵌套字典的 Value。
    • 示例中生成的结构为 Dictionary<int, Dictionary<string, Dictionary<string, MapItem>>>
  • 解析脚本

    • 通过逐行解析 CSV 数据,构建嵌套字典。
    • 支持异步编程,使用 ConcurrentDictionary

    在使用词典时先调用实现IProtoInit的Init方法,目的用于性能优化,预加载,如下示例代码:

csharp 复制代码
 if (messageData is IProtoInit protoInit)
 {
     protoInit.Init();
 }

在插件演示示例的ProtoDataCenter脚本完整的示例代码

csharp 复制代码
 public T _Get<T>(Action<T> callFun) where T : class, IMessage, new()
  {
      Type type = typeof(T);
      T messageData = (T)protoDataDict.GetOrAdd(type, _ =>
      {
          T messageData = Activator.CreateInstance(typeof(T)) as T;
          if (GetBytes(type.Name, out byte[] protoData))
          {
              messageData.MergeFrom(protoData);
              if (messageData is IProtoInit protoInit)
              {
                  protoInit.Init();
              }
          }

          return messageData;
      });

      callFun?.Invoke(messageData);
      return messageData;
  }

如果不调用接口也能正确使用词典。因为在词典属性里将会自定初始化并调用这个方法,如:

csharp 复制代码
    private ConcurrentDictionary<int, ConcurrentDictionary<string, ConcurrentDictionary<string, MapItem>>>
        _EffectDictMap = null;

    ConcurrentDictionary<int, ConcurrentDictionary<string, ConcurrentDictionary<string, MapItem>>> EffectDictMap
    {
        get
        {
            if (_EffectDictMap == null)
            {
                _EffectDictMap =
                    new ConcurrentDictionary<int,
                        ConcurrentDictionary<string, ConcurrentDictionary<string, MapItem>>>();
                Init();
            }

            return _EffectDictMap;
        }
        set => _EffectDictMap = value;
    }

7. 生成的解析脚本

以下是根据上述配置生成的解析脚本:

csharp 复制代码
using System.Collections;
using System.Collections.Concurrent;
using Google.Protobuf;
using HuaXianQu.ProtoBuffEx.Runtime.ProtoInterface;

public partial class Map : IProtoInit
{
    private ConcurrentDictionary<int, ConcurrentDictionary<string, ConcurrentDictionary<string, MapItem>>>
        _EffectDictMap = null;

    ConcurrentDictionary<int, ConcurrentDictionary<string, ConcurrentDictionary<string, MapItem>>> EffectDictMap
    {
        get
        {
            if (_EffectDictMap == null)
            {
                _EffectDictMap =
                    new ConcurrentDictionary<int,
                        ConcurrentDictionary<string, ConcurrentDictionary<string, MapItem>>>();
                Init();
            }

            return _EffectDictMap;
        }
        set => _EffectDictMap = value;
    }

    private ConcurrentDictionary<string, ConcurrentDictionary<int, MapItem>> _EffectInfoDictMap = null;

    ConcurrentDictionary<string, ConcurrentDictionary<int, MapItem>> EffectInfoDictMap
    {
        get
        {
            if (_EffectInfoDictMap == null)
            {
                _EffectInfoDictMap = new ConcurrentDictionary<string, ConcurrentDictionary<int, MapItem>>();
                Init();
            }

            return _EffectInfoDictMap;
        }
        set => _EffectInfoDictMap = value;
    }

    public void Init()
    {
        if (_EffectDictMap == null)
        {
            _EffectDictMap =
                new ConcurrentDictionary<int, ConcurrentDictionary<string, ConcurrentDictionary<string, MapItem>>>();
        }

        if (_EffectInfoDictMap == null)
        {
            _EffectInfoDictMap = new ConcurrentDictionary<string, ConcurrentDictionary<int, MapItem>>();
        }

        for (int i = 0; i < DataList.Count; i++)
        {
            var item = DataList[i];
            InitEffectDict(item);
            InitEffectInfoDict(item);
        }
    }

    private void InitEffectDict(MapItem item)
    {
        var MapIDMap = EffectDictMap.GetOrAdd(item.MapID,
            key => new ConcurrentDictionary<string, ConcurrentDictionary<string, MapItem>>());
        var RegionTypeMap = MapIDMap.GetOrAdd(item.RegionType, key => new ConcurrentDictionary<string, MapItem>());
        RegionTypeMap.TryAdd(item.MonsterDistribution, item);
    }

    private void InitEffectInfoDict(MapItem item)
    {
        var RegionTypeMap =
            EffectInfoDictMap.GetOrAdd(item.RegionType, key => new ConcurrentDictionary<int, MapItem>());
        RegionTypeMap.TryAdd(item.MapLevel, item);
    }

    public bool GetEffectDictMap<T>(int MapID, out T value) where T : IDictionary
    {
        value = default(T);
        if (EffectDictMap.TryGetValue(MapID, out var MapIDMap))
        {
            value = (T)(IDictionary)MapIDMap;
            return true;
        }

        return false;
    }

    public bool GetEffectDictMap<T>(int MapID, string RegionType, out T value) where T : IDictionary
    {
        value = default(T);
        if (EffectDictMap.TryGetValue(MapID, out var MapIDMap))
        {
            if (MapIDMap.TryGetValue(RegionType, out var RegionTypeMap))
            {
                value = (T)(IDictionary)RegionTypeMap;
                return true;
            }
        }

        return false;
    }

    public bool GetEffectDictMap<T>(int MapID, string RegionType, string MonsterDistribution, out T value)
        where T : IMessage
    {
        value = default(T);
        if (EffectDictMap.TryGetValue(MapID, out var MapIDMap))
        {
            if (MapIDMap.TryGetValue(RegionType, out var RegionTypeMap))
            {
                if (RegionTypeMap.TryGetValue(MonsterDistribution, out var MonsterDistributionMap))
                {
                    value = (T)(IMessage)MonsterDistributionMap;
                    return true;
                }
            }
        }

        return false;
    }

    public bool GetEffectInfoDictMap<T>(string RegionType, out T value) where T : IDictionary
    {
        value = default(T);
        if (EffectInfoDictMap.TryGetValue(RegionType, out var RegionTypeMap))
        {
            value = (T)(IDictionary)RegionTypeMap;
            return true;
        }

        return false;
    }

    public bool GetEffectInfoDictMap<T>(string RegionType, int MapLevel, out T value) where T : IMessage
    {
        value = default(T);
        if (EffectInfoDictMap.TryGetValue(RegionType, out var RegionTypeMap))
        {
            if (RegionTypeMap.TryGetValue(MapLevel, out var MapLevelMap))
            {
                value = (T)(IMessage)MapLevelMap;
                return true;
            }
        }

        return false;
    }
}

8. 注意事项
  • key;EffectDict:编码
    • EffectDict 是标记相同词典的名称。
    • 一个字段可以有多个 key;词典名称:编码,但词典名称不能相同。
  • 初始化
    • 在初始化 Map 数据时,需要调用 Init 方法实现 IProtoInit 接口。
  • 异步支持
    • 使用 ConcurrentDictionary 支持异步编程。

通过以上规则和示例,您可以灵活定义复杂的嵌套字典结构,并生成对应的代码和配置文件。


7. 字典类型的规则补充说明

在原有的字典类型规则基础上,进一步明确 Key 的类型限制,并补充相关示例。


1. Key 的类型限制
  • Key 只能为基础类型
    • 支持的基础类型包括:intuintlongulongdoublefloatboolstring
    • 不支持枚举类型、自定义类型、列表类型或字典类型作为 Key。
  • Value 可以是任意类型
    • 支持基础类型、枚举类型、自定义类型、列表类型或字典类型。

字典字段的定义规则

字典字段的定义规则分为两种情况,具体取决于 : 前面的字符:


情况 1:: 前面是非 : 的字符
  • 含义: 前面的字符表示 类成员名称,用于定义 Key。
  • 规则
    1. Key:由类成员名称决定,通常是类中的某个字段。
    2. Value:是类本身的数据。
    3. 格式类成员名称:{类数据}
    4. 示例
      • 定义类:

        csv 复制代码
        Effects,string:EffectName:1,EffectItem:EffectData:2
      • 数据:

        csv 复制代码
        EffectName:{{Heal,1,50},{Buff,2,10},{Poison,3,30}}
      • 解释:

        • EffectNameEffects 类的一个成员字段,作为 Key。
        • {Heal,1,50}{Buff,2,10}{Poison,3,30}Effects 类的数据,作为 Value。
        • 最终生成 Dictionary<string, Effects> 类型。

情况 2:: 前面是 ::
  • 含义:: 表示 Key 是 基础类型,直接使用值作为 Key。
  • 规则
    1. Key :基础类型的值(如 intstring 等)。
    2. Value :可以是 基础类型枚举类型自定义类型
      • 不支持直接使用列表类型或字典类型作为 Value。
      • 如果需要在 Value 中使用列表或字典类型,可以通过 自定义类型 嵌套实现。
    3. 格式
      • 如果 Value 是 基础类型,直接填写值。
      • 如果 Value 是 枚举类型 ,直接填写 枚举值枚举值名称
      • 如果 Value 是 自定义类型 ,使用 {类数据} 格式。
    4. 示例
      • 定义类:

        csv 复制代码
        SellInfo,bool:IsSellable:1,string:CurrencyType:2,PriceRange:Range:3
      • 数据:

        csv 复制代码
        ::{1:{TRUE,Gold,{10.0,20.0}},2:{FALSE,Silver,{15.0,25.0}}}
      • 解释:

        • 12 是 Key 值(int 类型)。
        • {TRUE,Gold,{10.0,20.0}}{FALSE,Silver,{15.0,25.0}}SellInfo 类的数据,作为 Value。
        • 最终生成 Dictionary<int, SellInfo> 类型。

通用规则

情况 Key 定义 Value 定义 格式 示例
: 前面是非 : 的字符 类成员名称(如 EffectName 类数据 类成员名称:{类数据} EffectName:{``{Heal,1,50},{Buff,2,10},{Poison,3,30}}
: 前面是 :: 基础类型的值(如 1"key1" 基础类型、枚举类型、自定义类型 ::{Key值:Value数据} ::{1:10, 2:20}::{1:Heal, 2:Buff}::{1:{TRUE,Gold,{10.0,20.0}}}

示例详解

示例 1:Value 为基础类型
  • 定义

    csv 复制代码
    ItemPrices,int:ItemID:1,double:Price:2
  • 数据

    csv 复制代码
    ::{1:10.5, 2:20.0, 3:30.75}
  • 解释

    • 123 是 Key 值(int 类型)。
    • 10.520.030.75 是 Value 值(double 类型)。
    • 最终生成 Dictionary<int, double> 类型。

示例 2:Value 为枚举类型
  • 定义

    csv 复制代码
    ItemEffects,int:ItemID:1,EffectType:Effect:2
  • 数据

    csv 复制代码
    ::{1:0, 2:1, 3:2}

    csv 复制代码
    ::{1:Heal, 2:Buff, 3:Poison}
  • 解释

    • 123 是 Key 值(int 类型)。
    • 0(或 Heal)、1(或 Buff)、2(或 Poison)是 Value 值(EffectType 枚举类型)。
    • 最终生成 Dictionary<int, EffectType> 类型。

示例 3:Value 为自定义类型
  • 定义

    csv 复制代码
    SellInfo,bool:IsSellable:1,string:CurrencyType:2,PriceRange:Range:3
  • 数据

    csv 复制代码
    ::{1:{TRUE,Gold,{10.0,20.0}}, 2:{FALSE,Silver,{15.0,25.0}}}
  • 解释

    • 12 是 Key 值(int 类型)。
    • {TRUE,Gold,{10.0,20.0}}{FALSE,Silver,{15.0,25.0}}SellInfo 类的数据,作为 Value。
    • 最终生成 Dictionary<int, SellInfo> 类型。

示例 4:自定义类型中嵌套列表类型
  • 定义

    csv 复制代码
    ItemAttributes,int:ItemID:1,List<string>:Attributes:2
  • 数据

    csv 复制代码
    ::{1:{300,{Attack,Defense}}, 2:{400,{Speed,Agility}}}
  • 解释

    • 12 是 Key 值(int 类型)。
    • {300,{Attack,Defense}}{400,{Speed,Agility}}ItemAttributes 类的数据,作为 Value。
      • 12ItemID 字段。
      • {Attack,Defense}{Speed,Agility}Attributes 字段(List<string> 类型)。
    • 最终生成 Dictionary<int, ItemAttributes> 类型。

示例 5:自定义类型中嵌套字典类型
  • 定义

    csv 复制代码
    ItemPrices,int:ItemID:1,Dictionary<string,double>:Prices:2
  • 数据

    csv 复制代码
    ::{1:{10,::{Gold:10.0,Silver:20.0}}, 2:{20,::{Gold:15.0,Silver:25.0}}}
  • 解释

    • 12 是 Key 值(int 类型)。
    • {10,::{Gold:10.0,Silver:20.0}}{20,::{Gold:15.0,Silver:25.0}}ItemPrices 类的数据,作为 Value。
      • 1020ItemID 字段。
      • {Gold:10.0,Silver:20.0}{Gold:15.0,Silver:25.0}Prices 字段(Dictionary<string, double> 类型)。
    • 最终生成 Dictionary<int, ItemPrices> 类型。

这个示例展示了如何在自定义类型中嵌套字典类型,并通过 CSV 格式定义和存储数据。以下是详细的解释和结构化说明:


定义

1. EffectItem 类型的定义
csv 复制代码
EffectItem,string:Name:1,int:Level:2,int:ID:3
  • EffectItem:自定义类型。
  • 字段
    • Namestring 类型,表示特效名称。
    • Levelint 类型,表示特效等级。
    • IDint 类型,表示特效的唯一标识符。
2. ItemPrices 类型的定义
csv 复制代码
ItemPrices,int:ItemID:1,Dictionary<string,EffectItem>:Prices:2
  • ItemPrices:自定义类型。
  • 字段
    • ItemIDint 类型,表示物品的唯一标识符。
    • PricesDictionary<string, EffectItem> 类型,表示以 EffectItemName 为键的字典。

数据

csv 复制代码
::{
  1:{10,Name:{{Fireball,5,9},{Thunderstorm,5,9}}}, 
  2:{20,Name:{{Blink,5,9},{BlackHole,5,9}}}
}
  • 外层字典

    • 键值对为 12int 类型)。
    • 对应的值分别为 {10,Name:{``{Fireball,5,9},{Thunderstorm,5,9}}}{20,Name:{``{Blink,5,9},{BlackHole,5,9}}}ItemPrices 类型)。
  • ItemPrices 结构

    • ItemID1020int 类型)。
    • PricesDictionary<string, EffectItem> 类型。
      • 键为 Namestring 类型)。
      • 值为 EffectItem 类型的数据,例如 {Fireball,5,9}{Thunderstorm,5,9}
  • EffectItem 结构

    • Fireball,5,9 表示 Name="Fireball", Level=5, ID=9
    • Thunderstorm,5,9 表示 Name="Thunderstorm", Level=5, ID=9

最终生成的数据结构

csharp 复制代码
Dictionary<int, ItemPrices>
  • Key12int 类型)。
  • ValueItemPrices 类型,包含:
    • ItemID1020int 类型)。
    • PricesDictionary<string, EffectItem> 类型,包含:
      • Key:Namestring 类型)。
      • Value:EffectItem 类型,例如 {Fireball,5,9}{Thunderstorm,5,9}

示例代码(C#)

以下是用 C# 表示的等效数据结构:

csharp 复制代码
using System;
using System.Collections.Generic;
using UnityEngine;

public class EffectItem
{
    public string Name { get; set; }
    public int Level { get; set; }
    public int ID { get; set; }
}

public class ItemPrices
{
    public int ItemID { get; set; }
    public Dictionary<string, EffectItem> Prices { get; set; }
}

public class ProtoTest:MonoBehaviour
{
    void Start()
    {
        // 创建 EffectItem 实例
        var fireball = new EffectItem { Name = "Fireball", Level = 5, ID = 9 };
        var thunderstorm = new EffectItem { Name = "Thunderstorm", Level = 5, ID = 9 };
        var blink = new EffectItem { Name = "Blink", Level = 5, ID = 9 };
        var blackHole = new EffectItem { Name = "BlackHole", Level = 5, ID = 9 };

        // 创建 ItemPrices 实例
        var item1 = new ItemPrices
        {
            ItemID = 10,
            Prices = new Dictionary<string, EffectItem>
            {
                { fireball.Name, fireball },
                { thunderstorm.Name, thunderstorm }
            }
        };

        var item2 = new ItemPrices
        {
            ItemID = 20,
            Prices = new Dictionary<string, EffectItem>
            {
                { blink.Name, blink },
                { blackHole.Name, blackHole }
            }
        };

        // 创建外层字典
        var itemPricesDict = new Dictionary<int, ItemPrices>
        {
            { 1, item1 },
            { 2, item2 }
        };

        // 输出结果
        foreach (var kvp in itemPricesDict)
        {
            Debug.Log(($"Key: {kvp.Key}");
            Debug.Log(($"ItemID: {kvp.Value.ItemID}");
            foreach (var price in kvp.Value.Prices)
            {
                Debug.Log(($"  Price Key: {price.Key}");
                Debug.Log(($"  EffectItem: {price.Value.Name}, Level={price.Value.Level}, ID={price.Value.ID}");
            }
        }
    }
}

输出结果

Key: 1
ItemID: 10
  Price Key: Fireball
  EffectItem: Fireball, Level=5, ID=9
  Price Key: Thunderstorm
  EffectItem: Thunderstorm, Level=5, ID=9
Key: 2
ItemID: 20
  Price Key: Blink
  EffectItem: Blink, Level=5, ID=9
  Price Key: BlackHole
  EffectItem: BlackHole, Level=5, ID=9

说明

  • 该数据结构是一个嵌套字典,外层字典的键为 int,值为 ItemPrices 类型。
  • ItemPrices 包含一个 int 类型的 ItemID 和一个 Dictionary<string, EffectItem> 类型的 Prices
  • EffectItem 是一个自定义类型,包含 NameLevelID 字段。
  • 这种结构适合用于存储复杂的游戏数据,例如物品价格及其关联的特效信息。

总结

  • 字典字段的格式
    • {Key1:Value1,Key2:Value2,...}
    • Key 和 Value 之间用 : 分隔,多个键值对之间用 , 分隔。
  • Key 的类型
    • Key 只能是基础类型(如 intstring 等)。
  • Value 的类型
    • Value 可以是基础类型、枚举类型或自定义类型。
    • 如果 Value 是自定义类型,使用 {类数据} 格式。

自定义类数据填充注意事项


1. 数据填充必须与自定义类的成员个数对应且成员总数相同
  • 数据填充时,必须确保每个字段都有对应的值,即使为空也需要用空字符占位。

  • 示例:

    csharp 复制代码
    public class EffectItem
    {
        public string Name { get; set; }
        public string Prices { get; set; }
        public int Level { get; set; }
        public int ID { get; set; }
    }
    • 数据填充为:{Fireball,,1,2}
      • NameFireballPrices 为空,Level1ID2

2. 数据填充顺序与 Proto 编码顺序一致
  • Proto 文件中的字段编码决定了数据填充的顺序

  • 示例:

    • 定义类:

      csv 复制代码
      EffectItem,string:Name:1,int:Level:2,int:ID:3
    • 生成的 Proto 文件:

      proto 复制代码
      message EffectItem
      {
          string Name = 1;
          int32 Level = 2;
          int32 ID = 3;
      }
    • 数据填充为:{Fireball,56,100}

      • NameFireballLevel56ID100
  • 如果修改字段编码顺序

    • 定义类:

      csv 复制代码
      EffectItem,string:Name:2,int:Level:1,int:ID:3
    • 生成的 Proto 文件:

      proto 复制代码
      message EffectItem
      {
          int32 Level = 1;
          string Name = 2;
          int32 ID = 3;
      }
    • 数据填充为:{56,Fireball,100}

      • Level56NameFireballID100
  • 注意事项

    • 数据填充必须按照字段编码顺序进行,否则会导致数据错乱。
    • 建议字段编码从小到大使用,以避免混淆。

3. 数据填充需要转义字符
  • 当数据中包含特殊字符(如英文逗号 ,、大括号 {} )时,需要在前面添加 \ 进行转义。
  • 示例:
    • 数据中包含逗号:

      csv 复制代码
      {Fireball\, the Great,56,100}
      • NameFireball, the GreatLevel56ID100
    • 数据中包含大括号:

      csv 复制代码
      {Fireball\{Special\},56,100}
      • NameFireball{Special}Level56ID100

总结

  1. 数据填充必须与类成员个数一致,空值用空字符占位。
  2. 数据填充顺序必须与 Proto 编码顺序一致,否则会导致数据错乱。
  3. 特殊字符需要转义 ,使用 \ 进行标记。

通过遵循以上规则,可以确保数据填充的准确性和一致性,避免因数据错乱导致的转换错误。

相关推荐
qq_12039813372 小时前
EXCEL的一些用法记录
excel
来恩100315 小时前
Unity 学习之旅:从新手到高手的进阶之路
学习·unity·游戏引擎
hakesashou16 小时前
python如何导出数据到excel文件
开发语言·python·excel
东京老树根19 小时前
Excel 技巧12 - 如何在Excel中输入对号叉号(★),字体Wingdings2
笔记·学习·excel
Excel_easy21 小时前
WPS生成文件清单,超链接到工作簿文件-Excel易用宝
excel·wps
东京老树根1 天前
Excel 技巧11 - 如何使用Excel作成简单的排班表(★★),weekday 函数,TEXT函数
笔记·学习·excel
向宇it1 天前
【从零开始入门unity游戏开发之——C#篇46】C#补充知识点——命名参数和可选参数
开发语言·unity·c#·编辑器·游戏引擎
快乐觉主吖1 天前
使用Newtonsoft.Json插件,打包至Windows平台显示不支持
unity·json