主题
技能、AI 与表现:从配置到运行证据
技能不发动、怪物不接近或预警大小异常时,先确认节点是否接入,再确认最终值和执行时序。界面有配置、生成成功、编译成功和对局实际执行是不同的证据。本文说明各环节怎样核对,以及哪些现象不能直接归纳为框架限制。
1. 先对齐编辑器、SDK 与当前对局
记录编辑器版本、环境、项目使用的 SDK 路径、编译配置和实际依赖版本。文档可能对应较新的依赖;通过当前宿主发现工具能力,再使用可用的操作。API 编译报错时,分别检查客户端和服务端声明。例如 GameDataAINodeValidateScan.Range 仅在服务端存在,纯代码赋值需要 #if SERVER,详见公式字段的双端放置。
mermaid
flowchart TD
Source["JSON 源数据与触发器函数 / 纯代码定义"] --> Effective["继承合并后的有效配置"]
Effective --> Generate["生成代码:检查字段省略诊断"]
Generate --> Build["Client-Debug 与 Server-Debug 编译、部署"]
Build --> Session["新调试局:确认地图、玩家和 session"]
Session --> Server["服务端:目标、指令、效果、Buff"]
Session --> Client["客户端:动画、Actor、资源与画面"]build_start 会保存、生成并进行双端构建和部署,debug_start 还会请求新对局;跟踪操作直到完成。已经修改源数据时,不用 debug_start_no_compile 验证新改动。无需机械重复已有工具包含的保存和生成步骤。
JSON-backed 条目以源 JSON 为准,生成代码用于核对投影结果,运行快照用于核对实际执行。纯代码定义同样是合法路线。同一静态条目选择一种维护来源,避免 JSON 只保留空壳,再通过 .Data 静态补丁覆盖数编配置;不要修改 DataGenerated。
2. JSON 扩展字段与生成失败是两件事
用户和编辑器可以在 JSON 中保存自定义辅助字段,供其它读写流程使用。生成器只生成实际存在于类型上的字段;未知字段被静默跳过是预期行为。 保存了扩展字段,不等于生成的 C# 对象会出现同名属性,也不等于运行时自动读取该字段。需要运行时读取时,由对应业务流程负责。
例如下面的 AuthorNotes 可以保留在源 JSON 中;如果目标类型没有这个字段,生成器不会为它生成赋值,也不报告字段省略警告:
json
{
"AuthorNotes": { "role": "boss", "designRange": 400 }
}对于实际存在的字段,如果值类型、枚举、函数或 Link 无法解析,生成器可能保留一行注释,继续生成其它内容。这类省略通过 GDATA001 编译警告、编辑器日志和生成工具的结构化 diagnostics 报告,包含条目、字段路径、预期类型、所属端与原始值。生成返回成功仍可能有警告,应检查 generationWarningCount。增量生成只报告本次处理条目的诊断;需要完整审计时使用 generateAll: true,原有生成文件中的编译警告也应处理。
不要通过未知字段报错清单限制项目扩展,也不要把注释中的失败值当成已生效配置。
项目/条目校验中的 unknown_field 只是提示检查字段意图:扩展内容合法且会保留,不会阻止生成;它与已知字段生成失败的 GDATA001 含义不同。
3. 公式可以是常量,也可以引用图形化函数
| 需求 | 创作方式 | 核对内容 |
|---|---|---|
| 固定伤害、距离或持续时间 | 在支持的公式字段填写常量 | 生成常量公式及所属端 |
| 血线条件、选择器条件、动态数值 | 在函数字段选择已有函数,或点“新建函数...”进入触发编辑器 | 函数签名、参数、返回类型、保存和绑定的函数引用 |
| 项目选择纯代码维护该条目 | 在代码中定义 GameData 并设置公式 | 使用当前 API,按字段可用端编译 |
图形化函数在 JSON 中保存为 ${函数限定名} 引用,逻辑由触发器源数据保存和生成。因此搜索 JSON 中有没有 =>,无法判断项目有没有条件逻辑。引用名称由编辑器创建和选择,不凭显示名称猜写。MCP 自动化先发现当前函数创建、编辑与绑定能力;工具不足时报告具体缺口,不把它解释成框架只能使用 C#。
Amount 配成常量时,自然不会随攻击力变化。需要属性驱动的伤害,应让公式读取施法者的相应属性;是否扩展属性子类型取决于项目的数值公式,不是读取攻击力的必要前提。
4. 引用、模板与继承后的数组
新建引用对象和引用已有对象都合法;同表内完整模型定义也合法。空白模型、示例 Buff 或模板自带的效果需要按项目目的填写、替换或移除。独立蓝图按维护边界组织,不把“一张表只能有一个逻辑对象”当成类型限制。
修改集合时同时检查本地值、继承值和合并后的有效值。data_set_field / data_set_fields 的 fieldState / fieldStates 返回这些读回信息;旧工具可用本地数据、继承数据和字段状态查询分别读取。数组在继承层使用补丁语义,不能把某次合并现象归纳成所有写入都是 append。
data_reset_field 移除本地覆盖,恢复继承结果,并不清空继承数组。需要替换、删除或索引修改时,先按工具和数据补丁格式构造目标结果,再回读验证。LeveledData 超出索引取末项,不会使所有等级都使用末项。
同表 $this 与跨表 Link 都受支持。创建和设置条目时,文件内的 $this 引用会转换为所属表的内部引用,保存文件时可还原为相对引用。若旧编辑器出现创建后生成不了、重新加载却正常的现象,记录规范化后的引用和生成诊断,更新编辑器后复验。不要仅根据引用是同表还是跨表来判断模型或 Buff 是否完整。
5. 六种距离分别控制什么
| 值 | 作用与来源 |
|---|---|
Sight | 视野、战争迷雾等可见性;不是 AI 扫描半径的别名 |
AIThinkTree.ScanRange | 优先使用树的 ScanRange 公式;否则有攻击技能时 Clamp 攻击技能 Range,无攻击技能时取 MinimalScanRange |
扫描节点的 Range | 该节点实际搜敌范围;未设置时回退到树的 ScanRange |
DefaultApproachRange 与移动节点 Range | 有攻击技能时默认来自攻击技能 Range,否则取 MinimalApproachRange;移动节点还能覆盖,并可能应用 RangeRatio、体型和迟滞 |
技能 Range 与 ApproachTarget | 决定本次指令对接近目标的距离要求;不是只对单位目标技能生效 |
| 效果搜索的半径与角度 | 决定效果结算覆盖区域;可以与技能允许起手距离不同 |
WaveAI 默认扫描下限 500、上限 1000,均可配置,树和节点也可覆盖。不要为了扩大索敌添加一个假的长射程攻击技能,那还会影响默认接近距离。无攻击技能的默认接近距离是 200;有攻击技能时没有“最低 200,因此所有近战射程至少 140”的规则。
None / Vector 的 AlwaysAcquireTarget 会影响接近目标选择。点和向量技能可通过 Command.Target 传入 Entity 辅助瞄准;首次 Preswing 锁定位置,后续同一 Order 使用该快照。单位、单位或地面中的实体目标及交互宿主保留单位目标语义。详见完整生命周期。效果形状与技能目标类型是可组合的配置,圆形不必一律 None,扇形也不必一律 Vector。
6. 运行时直接读取“哪个值生效”
先通过 debug.list_tools 发现 gameplay.snapshot,在连服调试局中发起请求并轮询同一个 request_id:
json
{ "name": "gameplay.snapshot", "arguments": { "unit_id": 123, "limit": 1, "trace_seconds": 30 } }启用后复现操作,再发起一次新快照,读取 trace.events。对原 request_id 的轮询只读取原报告,不会重新采样。
scan_range_source/approach_range_source说明默认范围从哪里来;last_scan_range/last_scan_source是最近实际执行扫描时的值与来源。last_ai_node/last_ai_result/last_ai_time_ms是最近一次执行完成的验证节点结果,不代表整棵树每个节点的历史。command_target、approach_target、execution_target、aim_snapshot区分原始输入、接近、结算和首次快照;结合stage、order_identity、turn_order_matches判断。abilities是当前实例,missing_configured_abilities是配置中存在但没有实例的技能。AI 想使用但根本没加入单位配置的技能,还要从 AI 节点引用反查。buffs包含启用状态、层数、已应用层数、已采样的属性修改值和当前属性最终值。trace_seconds默认 0;开启后最多 60 秒、同时 32 个单位、每单位保留最近 32 条阶段或命令结果。它不追溯开启前的事件,也不会自动解析取消调用栈。需要调用来源时使用事件追踪。
报告按单位分页,技能、Buff 和每个 Buff 的修改最多展示 16 项,并提供总数;大量对象或追踪事件可能超过传输预算,先缩小到 limit: 1。快照需服务端和客户端使用配套依赖;纯客户端没有权威服务端报告。
7. Buff 与效果的完整链路
先核对 单位.Abilities → 技能.Effect → 搜索/集合 → 子效果。ActivatedEffect 有自己的激活语义,不能当作每次施法发动的 Effect。工具中的 workflow_attach_ability_to_unit、引用解析与项目校验可帮助检查接线。模板自带的示例伤害、减速 Buff 和 Actor 也要审核。空 Effect、重复 Actor 或孤立条目可能是有意配置,按用途判断,不一律拒绝。
新版条目/项目校验提供非阻断提醒:ai_ability_not_attached 表示单位的显式 TacticalAI 图引用了未挂载的技能;activated_effect_without_cast_effect 提醒核对激活与施法效果;repeated_actor_link 提醒核对 Actor 叠加。检查沿类型声明的 Link 进行,也适用于派生类型;AI 检查最多访问 128 个节点,未完成时返回 ai_attachment_check_truncated。动态添加技能、动态设置 AI 或自定义执行逻辑仍需结合运行快照判断,不能仅凭无提醒认定接线完整。
搜索子效果默认继承本次命中的目标。若显式改为 MainTarget,就改成技能级主目标;无目标技能可能因此指向施法者。不要根据自爆样例把所有效果都改成同一 TargetLocation。自爆通常先搜索结算再移除施法者,具体仍受各节点有效性检查影响。
Buff 的 Modifications 由框架自动应用,并随叠层、禁用和移除维护撤销。PropertySubType.Base 是固定增量,不是覆盖值。可先用 LifeMax + 250、MoveSpeed + 28 的最小例子验证:记录添加前最终值,添加后检查 Buff 是否激活及已应用层数,移除后确认恢复。最大生命增加不等于当前生命恢复;回血使用生命值修改效果。
图形化项目可按以下步骤完成一次属性增益验证:
- 从现有 Buff 模板创建独立 Buff,检查并调整模板自带的持续时间、修改和状态。
- 将有效
Modifications配为两项:Property分别选择已有的LifeMax、MoveSpeed属性,SubType选择Base,Value分别填 250、28。这里的数值会生成常量公式。 - 创建 BuffAdd 效果,将其
Buff引用改为该 Buff;自身增益明确选择施法者作为目标,并将效果接入可执行技能的Effect,再把技能挂到单位。 - 保存生成、检查诊断、完成双端构建并进入新局。记录增益前、应用一层、叠层、禁用/重新启用和移除后的属性及快照。以单层为例,最终值应分别增加 250 和 28,移除后恢复;当前生命另行观察。
纯代码项目可使用同样的数据结构:
csharp
var link = new GameData.GameLink<GameCore.BuffSystem.Data.GameDataBuff, GameCore.BuffSystem.Data.GameDataBuff>("MyGame.SpeedAndHealth");
_ = new GameCore.BuffSystem.Data.GameDataBuff(link)
{
Modifications =
[
new() { Property = GameCore.ScopeData.UnitProperty.LifeMax, Value = _ => 250 },
new() { Property = GameCore.ScopeData.UnitProperty.MoveSpeed, Value = _ => 28 },
],
};这段只定义 Buff 数据,实际应用仍由 BuffAdd 效果等入口完成。不要为同一个 JSON-backed 条目再注册同名纯代码定义。
不要因为血条看起来没变就改用永久 AddFixed。先查宿主、公式、生成诊断、最终属性和当前/最大生命。需要乘算时,再定义对应子类型与属性公式。
临时定身可使用 SuppressMovement:暂停移动,解除后允许恢复;DisableMovement 拒绝移动并清除持续摇杆输入。UnCommandable 还会影响其它命令,只有设计确实要求禁止操作时才添加,不作为所有定身的固定组成。
8. 表现:先资源完整,再比较时序与缩放
从当前 Actor 的引用反查实际使用的资源,先核对其是否真的挂载、是否重复、目标 scope 与生命周期是否匹配。阶段预警使用 StagedSpawns 的相应阶段;效果 ActorArray 跟随效果执行,替换前必须核对时机。
AutoScale 开启时,Actor 的自身 Scale/ScaleVector 还可能乘 scope Scale、Volume/AssetVolume(Z 有特殊处理)和 Radius/AssetRadius。AssetLayerScale 参与资产自身校准,不能单独解释最终画面大小。大模型不等于所有 Actor 都应关闭 AutoScale;要固定世界尺寸还是跟随技能半径,应逐项决定。
云模型查询骨骼失败时,先通过资源目录确认身份与 localExists,下载完整资源,再查询骨骼和 Socket;不要猜 Socket 名称。资源依赖按 prefab、effect、模型、材质和贴图实际引用闭包检查,目录文件数量不能证明完整。入口见云资源导出和资源清单排查。
动画和粒子问题同时查看 <EditorRoot>/logs/client;服务器日志正常只能说明服务器没有相应错误。资源预览、首次/再次实例、附着点和初始参数都应纳入证据。
首刀小圈应在冷启动进程、同进程第二次、重新进局、晚加入、资源未就绪/已就绪等条件下分别复现,保留资源标识、加载时间、预期与实际缩放、scope 和生成阶段。没有定位前,隐藏多次预热属于临时实验,不能作为每个预警粒子的强制流程;瞬态播放不会为晚加入者自动回放。
9. 怎样写一份可复用的排查记录
分别写出现象、版本与复现步骤、证据、已排除项、根因的确认程度和修复验证。将源码/契约确认、实机复现、待验证假设及临时绕法明确区分。一项调整让画面恢复,不足以证明它就是根因;不要将默认值、模板配方或单项目现象写成所有项目的硬限制。