docs(ai): BrainGraph 标签方案改为 C#9 兼容(显式可选字符串)
Unity 2022.3=C#9,不用 C#10 的 CallerArgumentExpression;When(cond,label)显式可选, 默认回退"cond";spec 示例与 Phase1 计划(Task4/5 测试)同步更新. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
This commit is contained in:
@@ -122,7 +122,7 @@ Behavior Designer 的行为树是**手绘的二进制图资产**:
|
||||
|
||||
### 3.8 多实例复用与对象池(同一场景多个相同敌人)
|
||||
|
||||
- **共享 `AiGraph`(每敌人类型 1 份,flyweight)**:状态图结构、转换、能力编排、条件 lambda、`[CallerArgumentExpression]` 抓到的条件文本标签,全部只构建一次、由该类型所有实例共享。场景放 N 个相同杂兵 → 结构零 per-instance 分配、无重复 GC、缓存友好。
|
||||
- **共享 `AiGraph`(每敌人类型 1 份,flyweight)**:状态图结构、转换、能力编排、条件 lambda、显式条件标签字符串,全部只构建一次、由该类型所有实例共享。场景放 N 个相同杂兵 → 结构零 per-instance 分配、无重复 GC、缓存友好。
|
||||
- **每实例 `AiRuntime`(N 份)**:仅当前状态指针、`Blackboard`、计时器、`AbilitySequence` 游标随实例独立演化。
|
||||
- **无实例捕获约束**:声明里的条件/动作 lambda 只能通过参数 `c`(上下文)访问实例,**不得闭包捕获具体敌人实例**——这是 lambda 能挂在共享图上的前提。由 `BrainBuilder` 约定保证(后续可加 Roslyn 分析器强校验)。
|
||||
- **对象池重置**:`EnemyBase : IPoolable`(池复用走 `ForceStateRespawn`)。敌人回池再生时,`EnemyAiBrain` 必须重置 `AiRuntime`:回到 Entry 状态、清空黑板/计时器/游标、清空 trace。**共享 `AiGraph` 不重置**(无状态)。挂进 `IPoolable` 的回收/取出回调。
|
||||
@@ -168,33 +168,33 @@ public sealed class BasicMeleeGruntAi : EnemyAiScript
|
||||
// 注意:普通受击/硬直/击飞的"让位"由 IsControllable 门自动处理,无需在此声明。
|
||||
// 父层事件转换只用于决策层需要的、超出物理动画的额外行为响应。
|
||||
b.Global()
|
||||
.To("Dead").OnEvent(AiSignal.Died) // 终止决策
|
||||
.To("Enrage").When(c => c.Vitals.HpBelow(c.P.EnrageHpRatio)); // 低血狂暴(示例)
|
||||
.To("Dead").OnEvent(AiSignal.Died) // 终止决策
|
||||
.To("Enrage").When(c => c.Vitals.HpBelow(c.P.EnrageHpRatio), "HpBelow(enrage)"); // 低血狂暴(示例)
|
||||
|
||||
b.State("Patrol")
|
||||
.Tick(c => c.Mover.WalkRandom())
|
||||
.To("Chase").When(c => c.Sensor.SeesPlayer());
|
||||
.To("Chase").When(c => c.Sensor.SeesPlayer(), "SeesPlayer");
|
||||
|
||||
b.State("Chase")
|
||||
.Tick(c => c.Mover.MoveTo(c.Blackboard.LastKnown))
|
||||
.To("Combat").When(c => c.Sensor.InRange(c.P.MeleeRange))
|
||||
.To("Search").When(c => c.Sensor.LostFor(c.P.LoseLinkTimeout));
|
||||
.To("Combat").When(c => c.Sensor.InRange(c.P.MeleeRange), "InRange(melee)")
|
||||
.To("Search").When(c => c.Sensor.LostFor(c.P.LoseLinkTimeout), "LostFor(timeout)");
|
||||
|
||||
b.State("Combat")
|
||||
.OnEnter(c => c.Mover.FacePlayer())
|
||||
.Do(Seq.Select( // 按 SO 的 preferredRange/priority/exclusion 选招并等待其结束
|
||||
Ability("slash").When(c => c.Sensor.InRange(c.P.MeleeRange))))
|
||||
.To("Chase").When(c => !c.Sensor.InRange(c.P.MeleeRange) && c.Combat.NoAbilityRunning);
|
||||
Ability("slash").When(c => c.Sensor.InRange(c.P.MeleeRange), "InRange(melee)")))
|
||||
.To("Chase").When(c => !c.Sensor.InRange(c.P.MeleeRange) && c.Combat.NoAbilityRunning, "outOfMelee");
|
||||
|
||||
b.State("Search")
|
||||
.OnEnter(c => c.Mover.LookAround())
|
||||
.To("Chase").When(c => c.Sensor.SeesPlayer())
|
||||
.To("Chase").When(c => c.Sensor.SeesPlayer(), "SeesPlayer")
|
||||
.To("Patrol").After(c => c.P.InvestigateDuration);
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
- 转换条件的可读标签由 `[CallerArgumentExpression]` 自动从 lambda 源码文本抓取(可选覆写)。
|
||||
- 转换条件的可读标签走**显式可选字符串**(`When(cond, "SeesPlayer")`):本项目 Unity 2022.3 = C# 9,不用 C# 10 的 `[CallerArgumentExpression]`;不传标签则回退 `"cond"`。将来升级语言级别可无痛切换为自动抓取。
|
||||
- `c.P.*` = 参数视图,解析自 `EnemyStatsSO` / 能力 SO。
|
||||
|
||||
---
|
||||
@@ -203,7 +203,7 @@ public sealed class BasicMeleeGruntAi : EnemyAiScript
|
||||
|
||||
### 6.1 Mermaid/dot 导出器
|
||||
|
||||
- 纯函数 `AiGraph → stateDiagram-v2` 文本;层级子机 → composite state;边标签取自 `[CallerArgumentExpression]` 抓到的条件文本 / 事件枚举名。
|
||||
- 纯函数 `AiGraph → stateDiagram-v2` 文本;层级子机 → composite state;边标签取自转换的显式标签字符串 / 事件枚举名。
|
||||
- 入口:菜单 `BaseGames/AI/Export Graph` + 静态方法(打印 Console,供 MCP `unity_execute_code` 抓);文件写 `Docs/AI/<enemyId>.md`。
|
||||
|
||||
### 6.2 运行时实时调试器(Editor 窗口,仅编辑器)
|
||||
|
||||
Reference in New Issue
Block a user