docs(enemy): 修正 AI 配方解析方式——直接引用替代注册表双源
实施计划阶段发现两处硬阻塞: 1. BaseGames.AI 只引用 BaseGames.Core,无法收集依赖 Enemies 的 AiRecipeSO; 2. 项目 database SO 走 [SerializeField] 直接引用而非 Addressables,静态注册表取不到。 改为 EnemyAiBrain 上 _recipe 资产引用 / _definitionId 脚本 id 二选一。 净减 AiRecipeDatabaseSO、Addressables 加载、AddressKeys 改动、asmdef 改动; flyweight 与配方路径零字符串 id 均保持。 Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
This commit is contained in:
@@ -311,14 +311,50 @@ public sealed class PerceptionRecipeSO : AiRecipeSO
|
||||
| ~15% | 同上 + 在配方里追加 1–2 个自定义前置态(如掉落链、出场链) | 少量代码 |
|
||||
| ~5% | 写 `[AiDefinition] AiScript` 子类,完全自由 | 与现状相同 |
|
||||
|
||||
### 4.3 注册表双源
|
||||
### 4.3 解析方式:配方走直接引用,脚本走注册表
|
||||
|
||||
`AiDefinitionRegistry` 改为:
|
||||
> 修订(2026-07-29,实施计划阶段):初稿设计为「注册表双源 + `AiRecipeDatabaseSO` 经
|
||||
> Addressables 加载」。该方案有两处硬阻塞,已废弃:
|
||||
> 1. **程序集方向不允许** —— `AiDefinitionRegistry` 在 `BaseGames.AI`,该程序集只引用
|
||||
> `BaseGames.Core`,无法引用 `BaseGames.Enemies`(模块依赖 `IEnemyActor` / `EnemyAbilitySO`),
|
||||
> 也未引用 `Unity.Addressables`。
|
||||
> 2. **与项目既有模式不符** —— `FormSkillDatabaseSO` 等库资产走的是 `[SerializeField]`
|
||||
> 直接引用,不是 Addressables。静态注册表拿不到序列化引用;改用 Addressables 异步加载
|
||||
> 则与 `EnemyAiBrain.Start()` 的同步取图产生时序冲突。
|
||||
|
||||
1. 反射收集 `[AiDefinition]` 标注的 `AiScript` 子类(现状逻辑不变)。
|
||||
2. 从 `AiRecipeDatabaseSO`(Addressables 单一权威源,沿用 `FormSkillDatabaseSO` /
|
||||
`BestiaryDatabaseSO` 的既有模式)收集配方资产。
|
||||
3. **id 冲突显式抛错**(现状对重复 id 已抛,扩展到跨源)。
|
||||
改为两条互斥的解析路径,各自最短:
|
||||
|
||||
```csharp
|
||||
// BaseGames.AI —— 只依赖 BrainBuilder / AiGraph,无 Enemies 依赖
|
||||
public interface IAiDefinition { AiGraph GetOrBuildGraph(); }
|
||||
public abstract class AiScript : IAiDefinition { ... }
|
||||
public abstract class AiRecipeSO : ScriptableObject, IAiDefinition { ... }
|
||||
|
||||
// BaseGames.Enemies
|
||||
public sealed class PerceptionRecipeSO : AiRecipeSO { ... }
|
||||
```
|
||||
|
||||
`EnemyAiBrain` 上二选一,两者互斥:
|
||||
|
||||
```csharp
|
||||
[Tooltip("配方路径(约 95% 敌人):直接引用 AI 配方资产")]
|
||||
[SerializeField] AiRecipeSO _recipe;
|
||||
|
||||
[Tooltip("定制路径(约 5% 敌人):[AiDefinition(id)] 的 AiScript 子类 id")]
|
||||
[SerializeField] string _definitionId;
|
||||
```
|
||||
|
||||
`Awake` 校验「恰好设置其一」,否则 `Debug.LogError` 并禁用组件(沿用现状对漏配 id 的处理)。
|
||||
|
||||
这条修订带来三点净收益:
|
||||
|
||||
- **无需 `AiRecipeDatabaseSO`、无需 Addressables、无需改 `AddressKeys`、无需改任何 asmdef。**
|
||||
预制体到配方是硬引用,Unity 保证随预制体一起加载,不存在"库没加载到"的失败模式。
|
||||
- **flyweight 不变** —— 所有实例引用同一个 SO 资产,`AiRecipeSO` 内缓存唯一一份 `AiGraph`。
|
||||
- **配方路径彻底没有字符串 id**,痛点「硬编码字符串」在 95% 的敌人上完全消失。
|
||||
|
||||
`AiDefinitionRegistry` 保持单源(反射收集 `AiScript`),逻辑不变,仅让 `AiScript` 实现
|
||||
`IAiDefinition`。校验器侧枚举配方资产用 `AssetDatabase.FindAssets("t:AiRecipeSO")`(编辑器专用)。
|
||||
|
||||
### 4.4 关闭 Domain Reload 的约束
|
||||
|
||||
@@ -338,7 +374,7 @@ public struct AbilityRef
|
||||
[SerializeField] EnemyAbilitySO _asset;
|
||||
string _literal; // 定制脚本路径使用
|
||||
|
||||
public string Id => _asset != null ? _asset.Id : _literal;
|
||||
public string Id => _asset != null ? _asset.abilityId : _literal;
|
||||
public static implicit operator AbilityRef(string id) => new AbilityRef(id);
|
||||
}
|
||||
```
|
||||
@@ -379,9 +415,9 @@ E003(战斗区激活才掉落)、E004(战斗触发才出场)将来需要
|
||||
|
||||
| 工具 | 作用 | 优先级 |
|
||||
|---|---|---|
|
||||
| `AiDefinitionValidator` | 接入 `SOValidationRunner`。校验:配方引用的能力是否存在于该敌人的 `EnemyAbilityRegistry`、`ApproachAttackEngagement` 是否配了 `EnemyAttackSelector`、id 是否跨源重复、模块字段是否留空。**唯一能防住 450+ 引用出错的东西** | 紧接本次 |
|
||||
| `EnemyAiRecipeWizard` | 建配方资产的脚手架(CLAUDE.md 第 2 条:不裸建),顺带完成 Addressables 注册与 `AiRecipeDatabaseSO` 登记。**本次必须先有它**,E001 的配方资产才能合规产出 | 本次 |
|
||||
| `_definitionId` 下拉 Drawer | 从 `AiDefinitionRegistry.Ids` 取值,预制体上不再手打字符串 | 紧接本次 |
|
||||
| `AiDefinitionValidator` | 接入 `SOValidationRunner`。以 `AssetDatabase.FindAssets("t:AiRecipeSO")` 枚举全部配方,校验:模块字段是否留空、`AbilityRef` 是否两侧同时非空、`ApproachAttackEngagement` 是否配了 `EnemyAttackSelector`。**唯一能防住 450+ 引用出错的东西** | 紧接本次 |
|
||||
| `EnemyAiRecipeWizard` | 建配方资产的脚手架(CLAUDE.md 第 2 条:不裸建),按 `AssetFolderSpec` 定名定路径。**本次必须先有它**,E001 的配方资产才能合规产出 | 本次 |
|
||||
| `_definitionId` 下拉 Drawer | 定制路径(约 5%)从 `AiDefinitionRegistry.Ids` 取值,预制体上不再手打字符串 | 紧接本次 |
|
||||
| `AiGraphExporter` | 把 `AiGraph` 导出为 Mermaid(`Transition.Label` 已为此准备)。150 张图若不能一眼看懂就没人敢改 | 路线图 |
|
||||
|
||||
---
|
||||
@@ -401,7 +437,7 @@ E003(战斗区激活才掉落)、E004(战斗触发才出场)将来需要
|
||||
|
||||
| 阶段 | 内容 |
|
||||
|---|---|
|
||||
| **本次** | 四层架构 + 8 个模块 + `AiStateFragments` + `BrainBuilder.RequireState` + `AiSignal` 清理 + `AbilityRef` + `AiRecipeSO` / `PerceptionRecipeSO` / `AiRecipeDatabaseSO` + 双源注册表 + `PlayModeResetHook` + `SubclassSelector` Drawer + **`EnemyAiRecipeWizard` 脚手架** + E001 迁移为配方资产 + 测试重写 |
|
||||
| **本次** | 四层架构 + 8 个模块 + `AiStateFragments` + `BrainBuilder.RequireState` + `AiSignal` 清理 + `AbilityRef` + `IAiDefinition` / `AiRecipeSO` / `PerceptionRecipeSO` + `EnemyAiBrain` 双路径解析 + `PlayModeResetHook` + `SubclassSelector` Drawer + **`EnemyAiRecipeWizard` 脚手架** + E001 迁移为配方资产 + `SceneObjectPlacerTool` 提示更新 + 测试重写 |
|
||||
| **紧接** | `AiDefinitionValidator` 校验器 + `_definitionId` 下拉 Drawer |
|
||||
| **随敌人落地** | 新模块按需增加;`AiSignal.Engaged` 在 E003 / E004 需要时才加 |
|
||||
| **单独立项** | Boss 双轨统一(`BossSkillExecutor` + `BossSkillSO` vs `EnemyAttackSelector` + `EnemyAbilitySO`)。E004「小 BOSS」归属未定前不动。本次 `ApproachAttackEngagement` 按精英怪(E005)尺度做,不为 E004 加码 |
|
||||
@@ -417,8 +453,8 @@ E003(战斗区激活才掉落)、E004(战斗触发才出场)将来需要
|
||||
E001 迁移为 `PerceptionRecipeSO` 配方资产,依 `AssetFolderSpec.md` 命名与放置:
|
||||
|
||||
- 配方资产:`Assets/_Game/Data/Enemies/E001/ENM_E001_Ai.asset`(与既有 `ENM_E001_Stats.asset` / `ENM_E001_AnimConfig.asset` 同目录同前缀)
|
||||
- 权威源库:`Assets/_Game/Data/Enemies/ENM_AiRecipeDatabase.asset`
|
||||
- 两者均由 `EnemyAiRecipeWizard` 创建并自动完成 Addressables 注册与登记(CLAUDE.md 第 2 条)
|
||||
- 由 `EnemyAiRecipeWizard` 创建(CLAUDE.md 第 2 条:不裸建)
|
||||
- 预制体 `EnemyAiBrain._recipe` 直接引用该资产,`_definitionId` 留空
|
||||
|
||||
字段取值:
|
||||
|
||||
@@ -460,7 +496,8 @@ public sealed class E001CaoZhiAi : AiScript
|
||||
| `RushEngagementTests` | 两种 `RushExit` 的进入条件与脱战边;committed 不被感知丢失打断;CD 期不抖动 |
|
||||
| `ApproachAttackEngagementTests` | 逼近 ↔ 攻击往返;脱战;**新增:攻击起手后玩家离开感知区仍完整打完**(§3.6 行为变更) |
|
||||
|
||||
补充:`AiDefinitionRegistryTests` 增加双源收集与跨源 id 冲突抛错用例。
|
||||
补充:`PerceptionRecipeSoTests` —— 配方产出的图与等价手写组装的图行为一致;同一配方多次
|
||||
`GetOrBuildGraph()` 返回同一实例(flyweight)。`AiDefinitionRegistry` 逻辑未变,现有测试保持不动。
|
||||
|
||||
---
|
||||
|
||||
@@ -475,19 +512,20 @@ public sealed class E001CaoZhiAi : AiScript
|
||||
新增 Assets/_Game/Scripts/Enemies/AIBrain/Modules/Unaware/{SinglePost, DisguiseThenPatrol, AlternatingIdlePatrol}.cs
|
||||
新增 Assets/_Game/Scripts/Enemies/AIBrain/Modules/Engagement/{RushEngagement, ApproachAttackEngagement}.cs
|
||||
新增 Assets/_Game/Scripts/Enemies/AIBrain/Modules/Death/{TerminalDeath, AbilityDeath, TwoStageDeath}.cs
|
||||
新增 Assets/_Game/Scripts/Enemies/AIBrain/Recipes/{AiRecipeSO, PerceptionRecipeSO, AiRecipeDatabaseSO}.cs
|
||||
新增 Assets/_Game/Scripts/AI/IAiDefinition.cs
|
||||
新增 Assets/_Game/Scripts/AI/AiRecipeSO.cs (BaseGames.AI:无 Enemies 依赖)
|
||||
新增 Assets/_Game/Scripts/Enemies/AIBrain/Recipes/PerceptionRecipeSO.cs
|
||||
新增 Assets/_Game/Scripts/Enemies/Abilities/AbilityRef.cs
|
||||
新增 Assets/_Game/Scripts/Editor/AI/SubclassSelectorDrawer.cs
|
||||
新增 Assets/_Game/Scripts/Editor/AI/EnemyAiRecipeWizard.cs
|
||||
新增 Assets/_Game/Data/Enemies/E001/ENM_E001_Ai.asset(经向导产出)
|
||||
新增 Assets/_Game/Data/Enemies/ENM_AiRecipeDatabase.asset(经向导产出)
|
||||
删除 Assets/_Game/Scripts/Enemies/AIBrain/PerceptionStateMachine.cs
|
||||
删除 Assets/_Game/Scripts/Enemies/AIBrain/Ai/E001CaoZhiAi.cs(迁移为配方资产)
|
||||
改 Assets/_Game/Scripts/AI/BrainBuilder.cs + RequireState(name)
|
||||
改 Assets/_Game/Scripts/AI/AiSignal.cs 8 → 1
|
||||
改 Assets/_Game/Scripts/AI/AiScript.cs 实现 IAiDefinition
|
||||
改 Assets/_Game/Scripts/AI/AiDefinitionRegistry.cs 双源收集
|
||||
改 AddressKeys.cs 新增配方库地址常量(AddressablesLabelSpec:禁止硬编码字符串)
|
||||
改 Assets/_Game/Scripts/Enemies/AIBrain/EnemyAiBrain.cs 配方引用 / 脚本 id 二选一解析
|
||||
改 Assets/_Game/Scripts/Editor/Scene/SceneObjectPlacerTool.cs 7 处 AI 挂载提示改为配方路径
|
||||
测试 Assets/Tests/EditMode/AI/ 拆 PerceptionStateMachineTests → 5 份,扩 AiDefinitionRegistryTests
|
||||
文档 Docs/Guides/ 新增一节「新敌人 AI 怎么写」(四层组装规则 + 三条作者路径 + 六个敌人范例)
|
||||
```
|
||||
|
||||
Reference in New Issue
Block a user