docs(enemy): 修正作者指南与两处注释残留的死亡层表述
This commit is contained in:
@@ -130,7 +130,7 @@ namespace BaseGames.Tests.EditMode.AI
|
|||||||
[Test]
|
[Test]
|
||||||
public void Fragments_Throw_WhenDifferentFragmentKindsCollide()
|
public void Fragments_Throw_WhenDifferentFragmentKindsCollide()
|
||||||
{
|
{
|
||||||
// 跨层撞名同样要挡:未发现层的 Locomotion 态与死亡层的 Ability 态重名。
|
// 跨层撞名同样要挡:未发现层的 Locomotion 态与交战层的 Ability 态重名。
|
||||||
var b = new BrainBuilder();
|
var b = new BrainBuilder();
|
||||||
AiStateFragments.Locomotion(b, "Shared", LocomotionMode.Patrol);
|
AiStateFragments.Locomotion(b, "Shared", LocomotionMode.Patrol);
|
||||||
Assert.Throws<System.InvalidOperationException>(
|
Assert.Throws<System.InvalidOperationException>(
|
||||||
|
|||||||
@@ -18,7 +18,7 @@ namespace BaseGames.Enemies
|
|||||||
/// <summary>挂内部转换(如站立 ⇄ 巡逻的计时边)。没有内部边的模块留空实现。</summary>
|
/// <summary>挂内部转换(如站立 ⇄ 巡逻的计时边)。没有内部边的模块留空实现。</summary>
|
||||||
void Link(BrainBuilder b);
|
void Link(BrainBuilder b);
|
||||||
|
|
||||||
/// <summary>图入口(出生态)。注意与交战/死亡层的 EntryState 不同:
|
/// <summary>图入口(出生态)。注意与交战层的 EntryState 不同:
|
||||||
/// 本属性会成为整张图唯一的 BrainBuilder.Entry,而 EntryState 只是别处 To() 的目标。</summary>
|
/// 本属性会成为整张图唯一的 BrainBuilder.Entry,而 EntryState 只是别处 To() 的目标。</summary>
|
||||||
string Entry { get; }
|
string Entry { get; }
|
||||||
|
|
||||||
|
|||||||
@@ -16,7 +16,7 @@ namespace BaseGames.Enemies
|
|||||||
[Tooltip("交战层:发现玩家之后怎么打")]
|
[Tooltip("交战层:发现玩家之后怎么打")]
|
||||||
[SerializeReference, SubclassSelector] IEngagementModule _engagement = new RushEngagement();
|
[SerializeReference, SubclassSelector] IEngagementModule _engagement = new RushEngagement();
|
||||||
|
|
||||||
// 无「死亡层」字段:死亡归物理层,骨架自己声明 Death 终态。
|
// 死亡不做成第三层可插拔构件:死亡归物理层,骨架自己声明 Death 终态。
|
||||||
// PerformDeath 先 ForceState(Dead) 再发 Died 信号,此后 IsControllable 永久为假,
|
// PerformDeath 先 ForceState(Dead) 再发 Died 信号,此后 IsControllable 永久为假,
|
||||||
// 死亡链里的条件边永不被求值——所以死亡不能做成可插拔层。
|
// 死亡链里的条件边永不被求值——所以死亡不能做成可插拔层。
|
||||||
|
|
||||||
|
|||||||
@@ -168,8 +168,9 @@ Build(b):
|
|||||||
1. **未发现层内部边必须放 `Link()`,不能放 `Declare()`。**
|
1. **未发现层内部边必须放 `Link()`,不能放 `Declare()`。**
|
||||||
`AiRuntime.TryTransition` 按**声明顺序**遍历当前态的转换列表,条件都满足时先声明的先命中。`PerceptionSkeleton.Add` 的调用顺序固定为 `Declare → 挂升级边 → Link`:升级边(进警觉/进交战)必须先于未发现层的内部边(如巡逻计时)声明。如果把内部边挂进 `Declare()`,它会排在升级边前面——玩家进入追逐区那一帧,若恰好计时器也到点,敌人会切去另一个待机态而不是扑上来。
|
`AiRuntime.TryTransition` 按**声明顺序**遍历当前态的转换列表,条件都满足时先声明的先命中。`PerceptionSkeleton.Add` 的调用顺序固定为 `Declare → 挂升级边 → Link`:升级边(进警觉/进交战)必须先于未发现层的内部边(如巡逻计时)声明。如果把内部边挂进 `Declare()`,它会排在升级边前面——玩家进入追逐区那一帧,若恰好计时器也到点,敌人会切去另一个待机态而不是扑上来。
|
||||||
|
|
||||||
2. **死亡态用 `AiStateFragments.AbilityOnce`,不用 `Ability`。**
|
2. **一次性演出用 `AiStateFragments.AbilityOnce`,不用 `Ability`。**
|
||||||
`Ability()` 建的态每帧 `Tick` 都会 `EnsureAbility`(若能力已结束则重新触发),用于"只要还在这个态就该一直在做"的追击/冲锋类持续态。死亡演出只该播一次——用 `Ability()` 会在演出播完后被下一帧 `Tick` 重新触发,死亡动画无限重播。`AbilityOnce()` 只在 `OnEnter` 触发一次,没有 `Tick`。
|
`Ability()` 建的态每帧 `Tick` 都会 `EnsureAbility`(若能力已结束则重新触发),这是为"只要还在这个态就该一直在做"的持续态设计的——比如追击/冲锋类态,中途被受击/硬直打断后,下一帧 `Tick` 会自动把能力重新触发回来,符合"停了就该接着做"的语义。但如果态本身是一次性演出,`Ability()` 就用错了:演出播完后下一帧 `Tick` 仍会把它重新触发一遍,动画无限重播、态永远退不出去。真实的一次性态例子(均来自设计文档,尚未实现)——E004 蛭母出场时的 `Appear`(吼叫示威,播完衔接 `Idle`,见 `Docs/Game/敌人/小怪/E004_蛭母.md`)、E002 簧蛭钻出啃咬的 `Skill_Start`(钻出动作打完转 `Skill_Loop` 悬挂,见 `Docs/Game/敌人/小怪/E002_簧蛭.md`)——都该用 `AbilityOnce()`:只在 `OnEnter` 触发一次,没有 `Tick`,态自己的转换边(如 `.To(...).When(!IsAbilityRunning())`)负责在演出播完后离开。
|
||||||
|
注意:死亡态不适用本条——`Ability`/`AbilityOnce` 都不该用在死亡态上,死亡态的坑是转换边完全不会被求值(见下一条),不是"演出重播"的问题;`Death` 直接用 `Terminal` 声明为无行为终态。
|
||||||
|
|
||||||
3. **死亡链里不能放条件边,死亡完全归物理层。**
|
3. **死亡链里不能放条件边,死亡完全归物理层。**
|
||||||
`EnemyBase.PerformDeath` 先 `ForceState(EnemyStateType.Dead)`,再 `_brain?.Send(AiSignal.Died)`。`ForceState` 之后 `EnemyBrainContext.IsControllable`(`_enemy.CurrentState == EnemyStateType.Controlled`)永久为假;`AiRuntime.Tick` 里事件转换(`Died` 信号)先于 `IsControllable` 让位门处理,能正常切到 `Death` 终态,但**此后**任何条件边都不会再被求值(`Tick` 在让位门处直接 `return`)。所以骨架把 `Death` 声明成 `AiStateFragments.Terminal`(无行为终态),不做成可插拔层——死亡演出/清理/对象池归还全部交给 `EnemyBase.PerformDeath`、`EnemyDeathSequence`(前摇演出)、`EnemySpawnerOnEvent`(死亡生成小怪)等物理层组件。
|
`EnemyBase.PerformDeath` 先 `ForceState(EnemyStateType.Dead)`,再 `_brain?.Send(AiSignal.Died)`。`ForceState` 之后 `EnemyBrainContext.IsControllable`(`_enemy.CurrentState == EnemyStateType.Controlled`)永久为假;`AiRuntime.Tick` 里事件转换(`Died` 信号)先于 `IsControllable` 让位门处理,能正常切到 `Death` 终态,但**此后**任何条件边都不会再被求值(`Tick` 在让位门处直接 `return`)。所以骨架把 `Death` 声明成 `AiStateFragments.Terminal`(无行为终态),不做成可插拔层——死亡演出/清理/对象池归还全部交给 `EnemyBase.PerformDeath`、`EnemyDeathSequence`(前摇演出)、`EnemySpawnerOnEvent`(死亡生成小怪)等物理层组件。
|
||||||
|
|||||||
Reference in New Issue
Block a user