unity插件Excel转换Proto插件-ExcelToProtobufferTool

news2025/1/21 9:20:59

unity插件Excel转换Proto插件-ExcelToProtobufferTool

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

ExcelToProtobufTool 插件文档

1. 插件概述

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

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

在这里插入图片描述


2. 默认配置类:DefaultIProtoPathConfig

属性说明
属性名称类型说明
PackagesRootPathNamestring包根目录名称,默认值为 "Packages"
PackagesFullNamestring完整的包路径,组合了根目录名称和包名。
PackagesPathstring包路径的完整路径,通过 DirectoryInfo 获取。
IsDebugbool是否启用调试模式,默认值为 true
IsUsedDLLbool是否使用生成的 DLL 文件。true 为打包成 DLL,false 为生成 C# 脚本。
ExcelPathstringExcel 文件路径,默认指向 Config/Excel/Game
GenerateProtoPathstringProtobuf 文件路径,默认指向 Config/ProtoFiles
GenerateProtoDataPathstringProtobuf 生成的 Data 文件路径,默认指向 Res/ProtoData
GenerateProtoCsRootPathstring生成的 Protobuf C# 脚本路径,默认指向 Assets/Scripts/ProtoCSharp
ProtoDllNamestringProtobuf 脚本生成的 DLL 文件名,默认值为 CompanyName.ProtoBuffData
GenerateCsCachePathstring脚本或 DLL 的缓存路径,默认指向 Library/ProtoCache
GenerateProtoDllPathstringProtobuf 生成的 DLL 路径,默认指向 Assets/Plugins/ProtoBuffData
ProtocPathstringProtobuf 文件解析工具(protoc)的路径,根据平台动态调整。
GoogleProtobufPathstringGoogle 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# 脚本路径。
示例代码
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 文件。

    • 示例数据:

      ,
      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 开始递增。
示例
ItemType,Consumable:0,Equipment:1,QuestItem:2,Currency:3
EffectType,Heal:0,Buff:1,Poison:2
转换为 Excel 表格
ItemTypeConsumable:0Equipment:1QuestItem:2Currency:3
EffectTypeHeal:0Buff:1Poison:2
生成 Proto 文件
enum ItemType {
  Consumable = 0;
  Equipment = 1;
  QuestItem = 2;
  Currency = 3;
}

enum EffectType {
  Heal = 0;
  Buff = 1;
  Poison = 2;
}
生成 C# 脚本
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文件字段编码
    • 支持基础类型、枚举类型、自定义类型、列表类型和字典类型。
示例
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 表格
EffectItemstring:Name:1int:Level:2int:ID:3
BackpackItemint:ItemID:1string:ItemName:2ItemType:ItemType:3
SellInfobool:IsSellable:1string:CurrencyType:2PriceRange:Range:3
PriceRangedouble:MinPrice:1double:MaxPrice:2
ItemPricesint:ItemID:1Dictionary<string,EffectItem>:Prices:2
生成 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# 脚本
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. 第六行及以后:具体数据,按照字段类型和属性名称逐行填写。
示例
"{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}
12345678910111213
唯一ID物品名称物品类型最大堆叠数是否可交易是否可销毁出售信息出售价格列表图标路径描述关联道具列表效果属性加成背包信息
intstringItemTypeintboolboolSellInfoListstringstringListDictionary<string, EffectItem>Dictionary<ItemType, double>Dictionary<string,ItemPrices>
ItemIDItemNameItemTypeMaxStackTradableDestructibleSellableSellPricesIconPathDescriptionLinkedItemIDsEffectsAttributeBonusInfo
1小型治疗药水Consumable99TRUETRUE{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大型治疗药水Consumable99TRUETRUE{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铁剑Equipment1TRUETRUE{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 文件
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# 脚本
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。

{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
最终生成的数据结构为:

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方法,目的用于性能优化,预加载,如下示例代码:

 if (messageData is IProtoInit protoInit)
 {
     protoInit.Init();
 }

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

 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;
  }

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

    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. 生成的解析脚本

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

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. 示例
      • 定义类:
        Effects,string:EffectName:1,EffectItem:EffectData:2
        
      • 数据:
        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. 示例
      • 定义类:
        SellInfo,bool:IsSellable:1,string:CurrencyType:2,PriceRange:Range:3
        
      • 数据:
        ::{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 为基础类型
  • 定义
    ItemPrices,int:ItemID:1,double:Price:2
    
  • 数据
    ::{1:10.5, 2:20.0, 3:30.75}
    
  • 解释
    • 123 是 Key 值(int 类型)。
    • 10.520.030.75 是 Value 值(double 类型)。
    • 最终生成 Dictionary<int, double> 类型。

示例 2:Value 为枚举类型
  • 定义
    ItemEffects,int:ItemID:1,EffectType:Effect:2
    
  • 数据
    ::{1:0, 2:1, 3:2}
    
    ::{1:Heal, 2:Buff, 3:Poison}
    
  • 解释
    • 123 是 Key 值(int 类型)。
    • 0(或 Heal)、1(或 Buff)、2(或 Poison)是 Value 值(EffectType 枚举类型)。
    • 最终生成 Dictionary<int, EffectType> 类型。

示例 3:Value 为自定义类型
  • 定义
    SellInfo,bool:IsSellable:1,string:CurrencyType:2,PriceRange:Range:3
    
  • 数据
    ::{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:自定义类型中嵌套列表类型
  • 定义
    ItemAttributes,int:ItemID:1,List<string>:Attributes:2
    
  • 数据
    ::{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:自定义类型中嵌套字典类型
  • 定义
    ItemPrices,int:ItemID:1,Dictionary<string,double>:Prices:2
    
  • 数据
    ::{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 类型的定义
EffectItem,string:Name:1,int:Level:2,int:ID:3
  • EffectItem:自定义类型。
  • 字段
    • Namestring 类型,表示特效名称。
    • Levelint 类型,表示特效等级。
    • IDint 类型,表示特效的唯一标识符。
2. ItemPrices 类型的定义
ItemPrices,int:ItemID:1,Dictionary<string,EffectItem>:Prices:2
  • ItemPrices:自定义类型。
  • 字段
    • ItemIDint 类型,表示物品的唯一标识符。
    • PricesDictionary<string, EffectItem> 类型,表示以 EffectItemName 为键的字典。

数据

::{
  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

最终生成的数据结构

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

示例代码(C#)

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

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. 数据填充必须与自定义类的成员个数对应且成员总数相同
  • 数据填充时,必须确保每个字段都有对应的值,即使为空也需要用空字符占位。
  • 示例:
    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 文件中的字段编码决定了数据填充的顺序

  • 示例:

    • 定义类:
      EffectItem,string:Name:1,int:Level:2,int:ID:3
      
    • 生成的 Proto 文件:
      message EffectItem
      {
          string Name = 1;
          int32 Level = 2;
          int32 ID = 3;
      }
      
    • 数据填充为:{Fireball,56,100}
      • NameFireballLevel56ID100
  • 如果修改字段编码顺序

    • 定义类:
      EffectItem,string:Name:2,int:Level:1,int:ID:3
      
    • 生成的 Proto 文件:
      message EffectItem
      {
          int32 Level = 1;
          string Name = 2;
          int32 ID = 3;
      }
      
    • 数据填充为:{56,Fireball,100}
      • Level56NameFireballID100
  • 注意事项

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

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

总结

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

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

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/2279753.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

【数据结构篇】顺序表 超详细!

目录 一.顺序表的定义 1.顺序表的概念及结构 1.1线性表 2.顺序表的分类 2.1静态顺序表 2.2动态顺序表 二.动态顺序表的实现 1.准备工作和注意事项 2.顺序表的基本接口&#xff1a; 2.0 创建一个顺序表 2.1 顺序表的初始化 2.2 顺序表的销毁 2.3 顺序表的打印 3.顺序…

vulnhub靶场【IA系列】之Tornado

前言 靶机&#xff1a;IA-Tornado&#xff0c;IP地址为192.168.10.11 攻击&#xff1a;kali&#xff0c;IP地址为192.168.10.2 都采用虚拟机&#xff0c;网卡为桥接模式 本文所用靶场、kali镜像以及相关工具&#xff0c;我放置在网盘中&#xff0c;可以复制后面链接查看 htt…

云上贵州多彩宝荣获仓颉社区先锋应用奖 | 助力数字政务新突破

在信息技术应用创新的浪潮中&#xff0c;仓颉社区吸引了众多企业和开发者的积极参与&#xff0c;已有多个应用成功落地&#xff0c;展现出蓬勃的创新活力。仓颉编程语言精心遴选了在社区建设、应用创新、开源共建、技术布道等方面做出突出贡献的优秀项目应用&#xff0c;并颁发…

第十二章:算法与程序设计

文章目录&#xff1a; 一&#xff1a;基本概念 1.算法与程序 1.1 算法 1.2 程序 2.编译预处理 3.面向对象技术 4.程序设计方法 5.SOP标志作业流程 6.工具 6.1 自然语言 6.2 流程图 6.3 N/S图 6.4 伪代码 6.5 计算机语言 二&#xff1a;程序设计 基础 1.常数 …

2025 最新flutter面试总结

目录 1.Dart是值传递还是引用传递&#xff1f; 2.Flutter 是单引擎还是双引擎 3. StatelessWidget 和 StatefulWidget 在 Flutter 中有什么区别&#xff1f; 4.简述Dart语音特性 5. Navigator 是什么&#xff1f;在 Flutter 中 Routes 是什么&#xff1f; 6、Dart 是不是…

BUUCTF_Web([GYCTF2020]Ezsqli)

1.输入1 &#xff0c;正常回显。 2.输入1 &#xff0c;报错false&#xff0c;为字符型注入&#xff0c;单引号闭合。 原因&#xff1a; https://mp.csdn.net/mp_blog/creation/editor/145170456 3.尝试查询字段&#xff0c;回显位置&#xff0c;数据库&#xff0c;都是这个。…

HTML学习笔记(4)

目录 一、背景相关样式 二、定位position 三、javascript 1、变量的定义 2、数据类型 3、绑定事件 一、背景相关样式 background-image: url(); // 背景图片 background-repeat: repeat; // 背景图片是否平铺 no-repeat background-size: 200px; // 背景图片尺寸 cover把…

亲测有效!如何快速实现 PostgreSQL 数据迁移到 时序数据库TDengine

小T导读&#xff1a;本篇文章是“2024&#xff0c;我想和 TDengine 谈谈”征文活动的优秀投稿之一&#xff0c;作者从数据库运维的角度出发&#xff0c;分享了利用 TDengine Cloud 提供的迁移工具&#xff0c;从 PostgreSQL 数据库到 TDengine 进行数据迁移的完整实践过程。文章…

Excel 技巧11 - 如何使用Excel作成简单的排班表(★★),weekday 函数,TEXT函数

本文讲了如何在Excel中制作简单的排班表。 1&#xff0c;排班表Layout效果 - B2 单元格找那个输入 日期 - C3 - AG3 输入日&#xff0c;就是该月份的几号&#xff0c;比如1月5号&#xff0c;就输入5 如果是周六周日&#xff0c;背景色显示为绿色 - B4 ~ 输入员工名称 当 B2…

mac m1下载maven安装并配置环境变量

下载地址&#xff1a;Download Apache Maven – Maven 解压到一个没有中文和空格的文件夹 输入pwd查看安装路径 输入cd返回根目录再输入 code .zshrc 若显示 command not found: code你可以通过以下步骤来安装和配置 code 命令&#xff1a; 1. 确保你已经安装了 Visual Studio…

w-form-select.vue(自定义下拉框组件)

文章目录 1、w-form-select.vue 组件中每个属性的含义2、实例3、源代码 1、w-form-select.vue 组件中每个属性的含义 好的&#xff0c;我们来详细解释 w-form-select.vue 组件中每个属性的含义&#xff0c;并用表格列出它们是否与后端字段直接相关&#xff1a; 属性解释表格&…

Flutter项目和鸿蒙平台的通信

Flutter项目和鸿蒙平台的通信 前言Flutter和Harmonyos通信MethodChannelBasicMessageChannelEventChannel 前言 大家在使用Flutter开发项目的时候&#xff0c; Flutter提供了Platfrom Channel API来和个个平台进行交互。 Flutter官方目前提供了一下三种方式来和个个平台交互&…

【KOA框架】koa框架基础入门

koa是express的一层封装&#xff0c;语法比express更加简洁。所以有必要了解下koa的相关开发方法。 代码实现 package.json {"name": "koapp","version": "1.0.0","main": "index.js","scripts": {&…

我的创作纪念日——我与CSDN一起走过的365天

目录 一、机缘&#xff1a;旅程的开始 二、收获&#xff1a;沿路的花朵 三、日常&#xff1a;不断前行中 四、成就&#xff1a;一点小确幸 五、憧憬&#xff1a;梦中的重点 一、机缘&#xff1a;旅程的开始 最开始开始写博客是在今年一二月份的时候&#xff0c;也就是上一…

DenseNet-密集连接卷积网络

DenseNet&#xff08;Densely Connected Convolutional Network&#xff09;是近年来图像识别领域中一种创新且高效的深度卷积神经网络架构。它通过引入密集连接的设计&#xff0c;极大地提高了特征传递效率&#xff0c;减缓了梯度消失问题&#xff0c;促进了特征重用&#xff…

人形机器人将制造iPhone!

前言 优必选机器人和富士康通过一项突破性的合作伙伴关系&#xff0c;正在将先进的人形机器人&#xff08;如Walker S1及其升级版Walker S2&#xff09;整合到制造流程中&#xff0c;以改变iPhone的生产方式。这一合作旨在通过提升机器人能力、优化工作流程以及实现更智能的自动…

数据结构(链表 哈希表)

在Python中&#xff0c;链表和哈希表都是常见的数据结构&#xff0c;可以用来存储和处理数据。 链表是一种线性数据结构&#xff0c;由一系列节点组成&#xff0c;每个节点包含一个数据元素和一个指向下一个节点的指针。链表可以用来实现栈、队列以及其他数据结构。Python中可…

[苍穹外卖] 1-项目介绍及环境搭建

项目介绍 定位&#xff1a;专门为餐饮企业&#xff08;餐厅、饭店&#xff09;定制的一款软件产品 功能架构&#xff1a; 管理端 - 外卖商家使用 用户端 - 点餐用户使用 技术栈&#xff1a; 开发环境的搭建 整体结构&#xff1a; 前端环境 前端工程基于 nginx 运行 - Ngi…

vmware虚拟机配置ubuntu 18.04(20.04)静态IP地址

VMware版本 &#xff1a;VMware Workstation 17 Pro ubuntu版本&#xff1a;ubuntu-18.04.4-desktop-amd64 主机环境 win11 1. 修改 VMware虚拟网络编辑器 打开vmware&#xff0c;点击顶部的“编辑"菜单&#xff0c;打开 ”虚拟化网络编辑器“ 。 选择更改设置&#…

AUTOSAR OS模块详解(三) Alarm

AUTOSAR OS模块详解(三) Alarm 本文主要介绍AUTOSAR OS的Alarm&#xff0c;并对基于英飞凌Aurix TC3XX系列芯片的Vector Microsar代码和配置进行部分讲解。 文章目录 AUTOSAR OS模块详解(三) Alarm1 简介2 功能介绍2.1 触发原理2.2 工作类型2.3 Alarm启动方式2.4 Alarm配置2.5…