跳转到内容

最佳实践

在传统配置系统中,为了实现模块化概念,通常需要将相关概念放在单独的配置表中,其他表通过ID进行引用。这种方式在配置一个功能时需要涉及多个表格文件,增加了配置复杂度。

通过定义结构体(struct)的方式,可以在功能表中直接配置相关概念,将紧密相关的配置内容放在一起,从而减少表拆分,提高配置效率。

当配置表行数过多时,可以采用文件拆分策略来管理大量数据:

  • 文件拆分:将大型表格拆分为多个文件,如 item.csvitem_1.csvitem_2.csv
  • Excel工作表拆分:在Excel文件中使用多个工作表,如 itemitem_1item_2
  • 逻辑统一:系统在逻辑层面仍将这些拆分后的文件视为同一个配置表

策划在规划拆表规则时,可以采用以下策略:

  • 按类型拆分:根据物品类型进行拆分,如所有装备放入 item_1,宝石放入 item_2
  • 按ID分段拆分:根据ID范围进行分段拆分,便于管理和维护

对于简单的数据结构,可以通过配置 packsep 参数将其压缩到单个单元格中:

  • pack:使用逗号分隔结构体字段
  • sep:使用自定义分隔符分隔结构体字段

示例:Position结构体默认占用3列,配置 sep 后仅占用1列

// 位置结构体
// 使用分号分隔x、y、z坐标,在Excel中仅占用1列
struct Position (sep=';') {
x:int;
y:int;
z:int;
}

对于简单列表类型(如 list<int>, list<float>, list<str>)以及映射类型(如 map<int, int>),可以使用以下配置选项:

  • sep:自定义分隔符
  • pack:逗号分隔
  • block:块状布局
  • fix:固定列数

示例:饰品套装部件列表

// 饰品套装配置表
// 演示fix属性的使用,将列表固定为4列
table jewelrysuit[SuitID] (entry='Ename') {
SuitID:int; // 饰品套装ID
Ename:str;
Name:text; // 策划用名字
SuitList:list<int> (fix=4); // 子部件列表
}

场景分析:如果部件数量大多数情况下小于4,但少数情况会达到12个

  • 方案1:固定列数 - 占用过多列数
table jewelrysuit[SuitID] (entry='Ename') {
...
SuitList:list<int> (fix=12);
}
  • 方案2:单列压缩 - 仅占用1列
table jewelrysuit[SuitID] (entry='Ename') {
...
SuitList:list<int> (pack);
}
table jewelrysuit[SuitID] (entry='Ename') {
...
SuitList:list<int> (sep=';');
}
  • 方案3:块状布局 - 占用4列,支持多行扩展
table jewelrysuit[SuitID] (entry='Ename') {
...
SuitList:list<int> (block=4);
}

结构体列表是导致列数失控的主要原因。例如,20个复合结构体,每个需要4列,总共需要80列。以下是三种解决方案:

方案1:块状布局(推荐)

使用 block=1 配置,将结构体列表压缩到多行中:

// 掉落物品结构体
// 定义单个掉落项的概率、物品列表和数量范围
struct DropItem {
chance:int; // 掉落概率
itemids:list<int> (block=1); // 掉落物品
countmin:int; // 数量下限
countmax:int; // 数量上限
}
// 掉落配置表
// 使用block=1将结构体列表压缩到多行中,避免列数爆炸
table drop[dropid] {
dropid:int; // 序号
name:text; // 名字
items:list<DropItem> (block=1); // 掉落概率
}

数据表示例

序号名字掉落概率掉落物品列表数量下限数量上限
1测试掉落10010011020
1002
1003
1020011010
10200201
50300111
2剧情任务测试21001000111
3通告栏掉落染色模板180200011020

此示例也展示了块状布局支持嵌套使用。

方案2:双表关联(=>操作符)

使用两个表并通过 => 操作符建立关联:

// 掉落物品明细表
// 存储每个掉落项的详细信息
table dropItem[dropItemId] {
dropItemId:int;
dropid:int ->drop;
chance:int; // 掉落概率
itemids:list<int> (block=1); // 掉落物品
countmin:int; // 数量下限
countmax:int; // 数量上限
}
// 掉落配置表
// 使用=>操作符反向引用dropItem表
table drop[dropid] {
dropid:int =>dropItem[dropid];
name:text;
}

说明

  • dropItem 表通过 dropid 字段引用 drop
  • drop 表通过 => 操作符索引到 dropItem 表的 dropid
  • 程序会自动生成 list<DropItem> 类型
  • 此方案会产生额外的 dropItemId,一般情况下不推荐使用

方案3:双表关联(列表引用)

// 掉落物品模板表
// 定义可复用的掉落物品模板
table dropItem[dropItemId] {
dropItemId:int;
chance:int; // 掉落概率
itemids:list<int> (block=1); // 掉落物品
countmin:int; // 数量下限
countmax:int; // 数量上限
}
// 掉落配置表
// 通过list<int>引用dropItem模板,支持模板复用
table drop[dropid] {
dropid:int;
name:text; // 名字
items:list<int> ->dropItem (pack); // 掉落概率
}

优点

  • 通过 list<int> 直观地引用 dropItem 列表
  • 支持 dropItem 记录被多个 drop 记录共享使用

对于行数少但列数多的表格,可以使用列模式(columnMode)进行行列转换。具体示例参考模块参数表一节。

对于可选的引用字段,应当正确使用空单元格来表示未配置状态:

示例场景:对话表配置NPC转向

  • 如果配置了 npcid,则角色转向该NPC
  • 如果未配置 npcid,则不进行转向

正确做法:将 npcid 对应的单元格留空 错误做法:填写 0-1 等特殊值

重要提示

  • Excel或CSV中空单元格默认值为:false0""
  • 不要使用 0 作为记录ID
  • 对于 nullableRef 类型,请使用留空而非填 0,否则程序会检测报错

系统支持在单个单元格中配置复杂数据结构:

示例1:简单结构体压缩

// 等级品质结构体
// 使用pack压缩到单个单元格,逗号分隔
struct LevelRank {
Level:int; // 等级
Rank:int ->equip.rank; // 品质
}
table xx ...{
...
reward:Reward (pack);
}
reward
1,100

示例2:结构体列表压缩

// 结构体列表使用pack压缩
// 多个结构体用括号包围,逗号分隔
table xx ...{
...
rewardList:list<Reward> (pack);
}
rewardList
(1,100),(2,50)

示例3:多态类型配置

// 任务完成条件接口
// 演示多态类型使用pack压缩后的配置格式
interface completecondition (enumRef='completeconditiontype') {
struct KillMonster {
monsterid:int ->other.monster;
count:int;
}
struct TalkNpc {
npcid:int;
}
struct CondAnd {
cond1:task.completecondition (pack);
cond2:task.completecondition (pack);
}
struct CollectItem {
itemid:int;
count:int;
}
}
table task...{
...
condition: completecondition(pack);
}
Condition
KillMonster(1001, 1)
CondAnd(TalkNpc(5),KillMonster(101, 3))

分隔符说明pack 结构中的逗号可以使用分号替代,也支持逗号和分号混合使用。

物品表通常包含装备、宝石、货币等多种分类,每个分类具有独特的属性。以下是两种设计方案:

方案1:多表关联设计

  • item 表:配置所有分类共有属性(名称、堆叠上限等)
  • itemtype 表:定义物品分类(装备、宝石、货币等)
  • itemequip 表:配置装备特有属性
  • itemgem 表:配置宝石特有属性

方案2:单表多态设计

  • 单个 item 表,包含:
    • 前面为共有字段
    • 后面为 extra 多态类型字段,包含各类物品特有属性
  • 利用文件拆分功能:装备放入 item_1,宝石放入 item_2

方案对比

方案优点缺点
多表关联类型安全,其他表可声明具体类型引用配置重复,一个装备ID需在item和itemequip两处配置
单表多态配置集中,无重复数据类型检查复杂,需要运行时判断item.extra类型

推荐:根据项目需求选择方案,对于类型安全要求高的项目推荐多表关联设计。

模块通常包含多个通用参数需要配置。当参数数量较多时,可以使用列模式(columnMode)来优化配置体验。

传统配置方式

  • 配置单行记录,使用 entry 引用整行
  • 缺点:列数过多,配置不直观

列模式配置

  • 配置 columnMode 参数,将表格旋转90度
  • 优点:多列变为多行,配置更加直观清晰

示例:装备配置表

// 装备系统参数配置表
// 使用columnMode将表格旋转90度,适用于参数多但记录少的场景
table equipconfig[entry] (entry='entry', columnMode) {
entry:str; // 入口,程序填
stone_count_for_set:int; // 形成套装的音石数量
draw_protect_name:str; // 保底策略名称
broadcastid:int; // 公告Id
broadcast_least_quality:int; // 公告的最低品质
week_reward_mailid:int; // 抽卡周奖励的邮件id
}

Excel表示例

配置项描述字段名
形成套装的音石数量stone_count_for_set2
保底策略名称draw_protect_name测试
公告Idbroadcastid9500
公告的最低品质broadcast_least_quality1003
抽卡周奖励的邮件idweek_reward_mailid100

掉落表的设计涉及复杂的结构体列表配置。详细的设计方案和优化策略请参考结构体列表压缩配置一节,其中详细讨论了块状布局、双表关联等多种解决方案。