最佳实践
在传统配置系统中,为了实现模块化概念,通常需要将相关概念放在单独的配置表中,其他表通过ID进行引用。这种方式在配置一个功能时需要涉及多个表格文件,增加了配置复杂度。
通过定义结构体(struct)的方式,可以在功能表中直接配置相关概念,将紧密相关的配置内容放在一起,从而减少表拆分,提高配置效率。
表行数过多处理方案
Section titled “表行数过多处理方案”当配置表行数过多时,可以采用文件拆分策略来管理大量数据:
- 文件拆分:将大型表格拆分为多个文件,如
item.csv、item_1.csv、item_2.csv等 - Excel工作表拆分:在Excel文件中使用多个工作表,如
item、item_1、item_2等 - 逻辑统一:系统在逻辑层面仍将这些拆分后的文件视为同一个配置表
策划在规划拆表规则时,可以采用以下策略:
- 按类型拆分:根据物品类型进行拆分,如所有装备放入
item_1,宝石放入item_2 - 按ID分段拆分:根据ID范围进行分段拆分,便于管理和维护
表列数过多处理方案
Section titled “表列数过多处理方案”简单结构压缩配置
Section titled “简单结构压缩配置”对于简单的数据结构,可以通过配置 pack 或 sep 参数将其压缩到单个单元格中:
pack:使用逗号分隔结构体字段sep:使用自定义分隔符分隔结构体字段
示例:Position结构体默认占用3列,配置 sep 后仅占用1列
// 位置结构体// 使用分号分隔x、y、z坐标,在Excel中仅占用1列struct Position (sep=';') { x:int; y:int; z:int;}简单列表压缩配置
Section titled “简单列表压缩配置”对于简单列表类型(如 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);}结构体列表压缩配置
Section titled “结构体列表压缩配置”结构体列表是导致列数失控的主要原因。例如,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 | 测试掉落 | 100 | 1001 | 10 | 20 |
| 1002 | |||||
| 1003 | |||||
| 10 | 2001 | 10 | 10 | ||
| 10 | 2002 | 0 | 1 | ||
| 50 | 3001 | 1 | 1 | ||
| 2 | 剧情任务测试2 | 100 | 10001 | 1 | 1 |
| 3 | 通告栏掉落染色模板1 | 80 | 20001 | 10 | 20 |
此示例也展示了块状布局支持嵌套使用。
方案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记录共享使用
行列转换模式
Section titled “行列转换模式”对于行数少但列数多的表格,可以使用列模式(columnMode)进行行列转换。具体示例参考模块参数表一节。
单元格配置最佳实践
Section titled “单元格配置最佳实践”可空单元格处理
Section titled “可空单元格处理”对于可选的引用字段,应当正确使用空单元格来表示未配置状态:
示例场景:对话表配置NPC转向
- 如果配置了
npcid,则角色转向该NPC - 如果未配置
npcid,则不进行转向
正确做法:将 npcid 对应的单元格留空
错误做法:填写 0、-1 等特殊值
重要提示:
- Excel或CSV中空单元格默认值为:
false、0、""- 不要使用
0作为记录ID- 对于
nullableRef类型,请使用留空而非填0,否则程序会检测报错
复杂结构单元格配置
Section titled “复杂结构单元格配置”系统支持在单个单元格中配置复杂数据结构:
示例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结构中的逗号可以使用分号替代,也支持逗号和分号混合使用。
实际应用案例
Section titled “实际应用案例”物品表通常包含装备、宝石、货币等多种分类,每个分类具有独特的属性。以下是两种设计方案:
方案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_set | 2 |
| 保底策略名称 | draw_protect_name | 测试 |
| 公告Id | broadcastid | 9500 |
| 公告的最低品质 | broadcast_least_quality | 1003 |
| 抽卡周奖励的邮件id | week_reward_mailid | 100 |
掉落表的设计涉及复杂的结构体列表配置。详细的设计方案和优化策略请参考结构体列表压缩配置一节,其中详细讨论了块状布局、双表关联等多种解决方案。