跳到主要内容

技能系统数据关联全解(新手策划入门)

面向新策划的一站式指南:从客户端配表 → 服务端 SkillBase → 代码执行, 以及 NpcBase 中技能字段如何与 SkillBase 挂钩,一篇文章全看懂。

适用对象:技能策划、数值策划、新手策划 关联文件:tbl/SkillBase.xmltbl/SkillBase.tbl2tbl/NpcBase.xmltbl/NpcBase.tbl2tbl_xml/ExcelMakePack.xmlbase/zDatabase.hScenesServer/Skill.cppScenesServer/SkillStatusManager.cpptools/gen_server_skillbase.py


一、数据表全景图

┌─────────────────────────────────────────────────────────────┐
│ 客户端(策划设计器导出) │
│ │
│ skillbase.xml — 技能基础表(每技能一行) │
│ skilllevelbase.xml — 技能等级表(每技能ID+等级一行) │
│ │ │
│ │ tools/gen_server_skillbase.py 自动映射 │
│ ▼ │
│ SkillBase.xml / SkillBase.tbl2(服务端) │
│ ┌──────────────────────────────────────────────────┐ │
│ │ 24 列,基础字段自动填,仅「效果」需人工配置 │ │
│ │ 技能ID + 技能等级 联合唯一 │ │
│ └──────────┬───────────────────────────────────────┘ │
│ │ 按 (技能ID-1)*10+等级 哈希索引 │
│ ▼ │
│ skillbm(服务端技能数据管理器) │
│ ┌──────────────────────────────────────────────────┐ │
│ │ zSkillB 结构,解析后包含: │ │
│ │ - 技能基础属性(等级/消耗/间隔/前提等) │ │
│ │ - skillStatus 向量(「效果」字段解析结果) │ │
│ │ - weaponlist(需要武器列表) │ │
│ └──────────────────────────────────────────────────┘ │
│ │
│ NpcBase.xml / NpcBase.tbl2(服务端NPC表) │
│ ┌──────────────────────────────────────────────────┐ │
│ │ 「技能」列 → NPC基础攻击技能ID │ │
│ │ 「使用技能」列 → 结构化技能配置串 │ │
│ └──────────────────────┬───────────────────────────┘ │
│ │ 技能ID引用 │
│ ▼ │
│ skillbm 中的 zSkillB(按技能ID+等级查找) │
└─────────────────────────────────────────────────────────────┘

二、客户端表与服务端 SkillBase 的关系

2.1 客户端的两张表

客户端策划设计器导出两份技能表:

文件名粒度作用
技能基础表skillbase.xml每个技能一行(不含等级)技能的基本设定:名称、图标、特效
技能等级表skilllevelbase.xml每个「技能ID+技能等级」一行每个等级的具体数值:消耗、间隔、伤害等

关键点skilllevelbase.xml 已经按「技能ID + 技能等级」逐行展开, 这与服务端 SkillBase 的存储方式完全一致。

2.2 服务端 SkillBase 的 24 列结构

服务端 tbl/SkillBase.xml / tbl/SkillBase.tbl2按「技能ID + 技能等级」逐行展开的 24 列配置表。

字段顺序(来自 tbl_xml/ExcelMakePack.xml 服务端配置):

序号字段名类型说明
0技能IDDWORD技能唯一ID,和技能等级联合确定一行
1技能名称string(32)技能显示名称
2技能等级DWORD技能等级 1~10
3技能系别DWORD技能所属职业/系统
4技能树别DWORD技能树分支
5需要本线技能点数DWORD学习该等级需要投入的技能点数
6前提技能一DWORD前置技能1的ID
7前提技能一等级DWORD前置技能1所需等级
8前提技能二DWORD前置技能2的ID
9前提技能二等级DWORD前置技能2所需等级
10前提技能三DWORD前置技能3的ID(或角色等级要求)
11前提技能三等级DWORD前置技能3所需等级
12间隔时间DWORD技能冷却时间(毫秒)
13攻击方式DWORD技能类型(1=主动/2=被动触发/3=被动常驻)
14能否骑马使用DWORD0=不可骑马使用 1=可以
15需要物品DWORD学习所需物品ID
16需要武器string(128)使用该技能所需武器类型,冒号分隔
17消耗体力值DWORD使用消耗的体力
18消耗法术值DWORD使用消耗的法力
19消耗生命值DWORD使用消耗的生命
20伤害加成DWORD额外伤害加成
21效果string(1024)技能逻辑核心,详见下文
22消耗物品类型DWORD使用消耗的物品ID
23物品消耗数量DWORD使用消耗的物品数量

2.3 客户端 → 服务端的字段映射

通过 tools/gen_server_skillbase.py 将客户端的 skilllevelbase.xml 一键映射为服务端 SkillBase:

服务端字段客户端字段备注
技能ID / 技能名称 / 技能等级同名硬性一致,不可改名/错号
需要本线技能点数需要本树技能点数字段名归一化
前提技能一/二/三前提技能1/2/3字段名归一化
前提技能一/二/三等级前提技能1/2/3级别字段名归一化
能否骑马使用允许骑马字段名归一化
需要武器适用武器类型字段名归一化
消耗物品类型消耗物品字段名归一化
物品消耗数量消耗数量字段名归一化
效果(客户端无此字段)需策划人工配置
需要物品(客户端无此字段)需策划按需填写

注意:客户端表中还有「目标」「距离范围」「攻击方式」「施放方式」等字段, 它们不直接进入服务端 SkillBase 的独立列,而是作为配置「效果」字段时的参考信息 出现在 SkillBase_效果待填清单.csv 中,辅助策划判断该技能的施法方式和目标类型。


三、服务端 SkillBase 如何被加载和索引

3.1 哈希索引规则

服务端将所有技能按 skill_hash(技能ID, 技能等级) 索引存储:

#define skill_hash(type, level) ((type - 1) * 10 + level)

即:技能ID=1, 等级=1 → hash = (1-1)*10+1 = 1 技能ID=1, 等级=10 → hash = (1-1)*10+10 = 10 技能ID=2, 等级=1 → hash = (2-1)*10+1 = 11

3.2 加载到内存

服务端启动时,将 tbl/SkillBase.tbl2 的每一行读入 skillbm(一个全局技能数据管理器)。

每一行被解析为 zSkillB 结构,包含:

  • 所有基础字段(技能ID、等级、消耗、间隔等)
  • skillStatus 向量(「效果」字段的解析结果)
  • weaponlist(需要武器列表)

3.3 运行时使用

当角色/NPC使用技能时,通过 skill_hashskillbm 中查找到对应的 zSkillB, 创建 zSkill 运行时对象,然后通过 zSkill::action() 执行技能逻辑。


四、NpcBase 中的技能字段

4.1 两个关键字段

tbl/NpcBase.xml / tbl/NpcBase.tbl2 中有两个与技能相关的字段:

字段类型用途
技能DWORDNPC 的基础攻击技能ID,单个技能
使用技能string(1024)NPC 的主动技能配置,结构化字符串,可配多个

4.2 「技能」字段(基础攻击)

  • 这是 NPC 的普攻技能
  • 当 NPC 进行近战/远程普通攻击时,代码将该技能ID作为 wdMagicType 发送。
  • 若此字段为 0,近战攻击使用 SKILLNORMAL(技能ID=377),远程攻击使用 NORMALBOW(技能ID=378)。
  • 该技能ID引用 SkillBase.tbl2 中对应 技能ID 的技能,使用 zSkill::create(this, skill_id, npc->level) 加载。

实际数据示例:

编号=277 名称=王城守卫 技能=8001
编号=12106 名称=虚空凶灵 技能=304

即「王城守卫」的基础攻击使用技能8001,「虚空凶灵」使用技能304(神罚)。

4.3 「使用技能」字段(主动技能)

这个字段是一个结构化字符串,格式为:

技能类型:技能ID-需求等级-触发概率-系数,技能ID-需求等级-触发概率-系数;技能类型:...;

各字段含义:

位置含义说明
技能类型NPC AI 分类1=伤害 2=恢复 3=增益Buff 4=减益Debuff 5=召唤 6=复活
技能ID技能表ID引用 SkillBase.tbl2 中的 技能ID
需求等级使用门槛NPC等级需≥此值才可用(当前AI代码中未强制检查,预留)
触发概率施放概率0~100 的百分比,AI判定是否施放
系数伤害/数值系数预留字段,当前AI代码未使用

多个技能以 , 分隔,多个类型以 ; 分隔。

实际数据示例:

编号=401 名称=禁卫军 使用技能=1:77-0-100-1;
编号=277 名称=王城守卫 使用技能=1:993-0-100-1;
编号=30093 名称=南方增长天王 使用技能=1:979-0-100-1;5316:10000:1-1;5315:10000:1-1;...

解析逻辑(zNpcB::parseSkills):

  1. ; 分割 → 得到每个技能类型组
  2. : 分割 → 第一段是技能类型,第二段是该类型的技能列表
  3. , 分割 → 得到每个独立技能
  4. - 分割 → 得到技能ID、需求等级、概率、系数

4.4 NPC 技能如何被 AI 使用

NpcBase.tbl2 → parseSkills() → skillMap{类型: [npcSkill数组]}

NPC初始化时加载所有技能到 usm

AI 按类型随机选一个技能

检查触发概率 (selectByPercent)

useSkill(target, skill.id)

zSkill::action() → 执行技能效果

NPC AI 中按类型调用:

场景调用类型
普通攻击getRandomSkillByType(SKILL_TYPE_DAMAGE, skill)
治疗自己getRandomSkillByType(SKILL_TYPE_RECOVER, skill)
给自己上BuffgetRandomSkillByType(SKILL_TYPE_BUFF, skill)
给敌人上DebuffgetRandomSkillByType(SKILL_TYPE_DEBUFF, skill)

五、「效果」字段详解

5.1 什么是「效果」字段

SkillBase 表的第 21 列「效果」,是一行技能全部逻辑效果的浓缩文本。 服务端在加载时解析为两层结构:

一个技能 = 若干「状态组」(SkillStatus)
一个状态组 = 若干「效果元素」(SkillElement)

5.2 四级分隔符

分隔符作用层级
.分隔多个状态组第1层
:分隔「状态定义」和「元素列表」第2层
;分隔多个效果元素第3层
-分隔字段内部数值第4层

5.3 状态组 SkillStatus(7个字段)

A-B-C-D-E-F-G
id target center range mode clear isInjure
位置字段含义取值
1id状态组标识通常为0
2target目标类型(位掩码)1=自身 2=友方 4=敌方 8=NPC 16=地表 32=宠物 64=召唤物,可叠加
3center施法中心1=鼠标点 2=自身
4range攻击范围1=单体,其他值=范围
5mode飞行模式0=近身 1=飞行
6clear能否被清除0/1
7isInjure是否触发伤害判定0=仅状态 1=伤害结算

5.4 效果元素 SkillElement(5个字段)

A-B-C-D-E
id percent value time state
位置字段含义说明
1id效果状态ID对应 SkillStatus_XX() 处理函数,决定效果类型
2percent触发百分比0~100,100=必定触发
3value效果数值具体加成/伤害值
4time持续时间
5state状态表现对应客户端 USTATE 状态ID

5.5 实际解析示例

以技能ID=993「固伤弓箭30%」的效果为例:

0-1-2-1-0-0-0:23-100-1-1-0;.0-12-1-1-1-0-1:73-100-30-1-0;133-0-0-0-0;.

状态组1 0-1-2-1-0-0-0:23-100-1-1-0;

  • 状态定义:target=1(自身) center=2(以自身为中心) range=1(单体) mode=0
  • 效果元素:SkillStatus_23 = 物理攻击力+1,100%触发,持续1秒

状态组2 0-12-1-1-1-0-1:73-100-30-1-0;133-0-0-0-0;

  • 状态定义:target=12(敌方+NPC) isInjure=1(触发伤害)
  • 效果元素1:SkillStatus_73 = 降低目标生命值30%,100%触发
  • 效果元素2:SkillStatus_133 = 标记本次攻击为物理伤害

整体含义:该技能对自身附加物攻+1的短暂增益,同时攻击目标并造成其生命值30%的伤害。


六、数据关联全景(最核心)

客户端 skilllevelbase.xml ──→ 服务端 SkillBase.tbl2 ──→ skillbm(内存)
(技能ID+等级展开) (gen脚本映射,效果人工填) (skill_hash索引)

├──→ 玩家使用技能
│ zSkill::action()
│ → 解析skillStatus
│ → SkillStatus_XX()执行

NpcBase.tbl2 ────────────────────────────────────────────→ │
「技能」字段(基础攻击)──────→ skillbm查找──────→ NPC普攻
「使用技能」字段(主动技能)──→ skillbm查找──────→ NPC AI施法

配技能需要关心的三个层次:

  1. 客户端:策划在设计器中配置技能的展示信息(名称、图标、特效)和基础参数
  2. 服务端 SkillBase:策划配置技能的所有数值和「效果」逻辑
  3. NpcBase:策划将技能挂载到 NPC 上,决定 NPC 何时用什么技能

七、新手策划配置流程速查

7.1 添加新技能

  1. 在客户端设计器配置技能的基础参数(名称、图标、冷却等)
  2. 导出 skilllevelbase.xml
  3. 运行 gen_server_skillbase.py 生成服务端 SkillBase 表
  4. 在「效果待填清单」中为每个技能编写「效果」字段
  5. 合并回填后,将最终 SkillBase.xml 替换到 tbl/ 目录
  6. 重新生成 SkillBase.tbl2(通过 ExcelMakePack 打包)

7.2 给NPC配置技能

  1. 确认技能已在 SkillBase.tbl2 中存在
  2. 打开 NpcBase
  3. 「技能」列填 NPC 基础攻击技能ID
  4. 「使用技能」列按格式填写:
    1:技能ID-0-100-1;2:技能ID-0-80-1;
    • 类型1=伤害 2=恢复 3=Buff 4=Debuff
    • 概率填触发百分比
  5. 重新打包 NpcBase.tbl2

7.3 常见问题排查

问题可能原因
NPC放不出技能技能ID不在SkillBase表中;NPC等级不满足;概率填0
技能没有效果「效果」字段未填或格式错误;效果ID超出 SKILLSTATENUMBER(400)
技能不生效但客户端显示正常服务端SkillBase「效果」列被截断(tbl2已修复此问题)
技能伤害不对检查效果元素的 value、percent 是否正确;检查状态组的 isInjure 是否为1
NPC不会治疗/加Buff「使用技能」中类型填错,AI按类型查找