feat(enemy): 嘲风 AI 图(BrainGraph 定制路径)
Wait →(Engaged) Intro → Ground⇄GroundAttack →(hp<50%) PhaseTx → Air⇄AirAttack。 Boss 不脱战(rest 指回自身)、阶段单向推进、起手就打完。 死亡不在图上:全局 Died 边进 Death 终态,演出归物理层。 8 条 EditMode 断言覆盖状态序列、过渡只发起一次、过渡结束必须转出、 脱离感知区不脱战、血量回升不回退阶段。
This commit is contained in:
@@ -0,0 +1,8 @@
|
||||
fileFormatVersion: 2
|
||||
guid: d0934d78d9f1fe34aa6c0429a0822df8
|
||||
folderAsset: yes
|
||||
DefaultImporter:
|
||||
externalObjects: {}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
@@ -0,0 +1,80 @@
|
||||
using BaseGames.AI;
|
||||
|
||||
namespace BaseGames.Enemies
|
||||
{
|
||||
/// <summary>
|
||||
/// 嘲风的 AI 图(定制路径:Boss 数量少、彼此差异大,手写图而非配方)。
|
||||
///
|
||||
/// 阶段 0(地面):逼近 ↔ 选招。地面招由 BossPhaseAbilityGate 在阶段 0 放行。
|
||||
/// 阶段 1(空中):悬停 ↔ 选招。空中招由阶段门在阶段 1 放行,地面招被禁用。
|
||||
///
|
||||
/// 招式的射程 / 冷却 / 权重全归各能力自己的 EnemyAbilitySO;本图只决定
|
||||
/// 「什么时候该逼近、什么时候该出手、什么时候该换阶段」。
|
||||
/// 死亡不在本图:EnemyBase 的死亡流程发 Died 事件,全局边把决策停在 Death 终态。
|
||||
///
|
||||
/// 三条刻意的形状,改图时别顺手抹掉:
|
||||
/// 1. Boss 不脱战——逼近态的 rest 指回自身,脱离感知区也不回静置态。
|
||||
/// 2. 阶段单向推进——空中阶段没有回地面阶段的边,血量回升也不退阶段(招池已换)。
|
||||
/// 3. 起手就打完——攻击态不挂脱战边,前摇中玩家跑开仍完整打完。
|
||||
/// </summary>
|
||||
[AiDefinition("ChaoFeng")]
|
||||
public sealed class ChaoFengAi : AiScript
|
||||
{
|
||||
public const string Wait = "Wait";
|
||||
public const string Intro = "Intro";
|
||||
public const string Ground = "Ground";
|
||||
public const string GroundAttack = "GroundAttack";
|
||||
public const string PhaseTx = "PhaseTransition";
|
||||
public const string Air = "Air";
|
||||
public const string AirAttack = "AirAttack";
|
||||
public const string Death = "Death";
|
||||
|
||||
/// <summary>入场演出能力 id(对应入场能力资产的 abilityId)。</summary>
|
||||
public const string IntroAbility = "chaofeng_intro";
|
||||
|
||||
/// <summary>空中阶段的血量阈值。</summary>
|
||||
private const float AirPhaseHpRatio = 0.5f;
|
||||
|
||||
/// <summary>阶段过渡的目标阶段索引。</summary>
|
||||
private const int AirPhaseIndex = 1;
|
||||
|
||||
/// <summary>阶段过渡无敌时长,须 ≥ 浮空上升时长 + 缓冲。</summary>
|
||||
private const float PhaseTxInvincible = 2f;
|
||||
|
||||
protected override void Build(BrainBuilder b)
|
||||
{
|
||||
b.Entry(Wait);
|
||||
|
||||
// 竞技场触发前静置:不巡逻、不警觉(Boss 不搜敌,等玩家进场)
|
||||
AiStateFragments.Locomotion(b, Wait, LocomotionMode.Idle)
|
||||
.To(Intro).OnEvent(AiSignal.Engaged);
|
||||
|
||||
// 入场演出:一次性能力,播完进战
|
||||
AiStateFragments.AbilityOnce(b, Intro, IntroAbility)
|
||||
.To(Ground).When(x => !x.Combat.IsAbilityRunning(IntroAbility), "introDone");
|
||||
|
||||
// 阶段 0(地面):逼近 ↔ 选招。rest 指回自身——Boss 不脱战。
|
||||
BossFragments.ApproachAttack(b, Ground, GroundAttack, rest: Ground)
|
||||
.To(PhaseTx).When(x => x.Vitals.HpBelow(AirPhaseHpRatio), "hp<50%");
|
||||
|
||||
// 阶段过渡:无敌 + 浮空演出由 Boss 侧的阶段过渡回调承担。
|
||||
// PhaseTransition 片段刻意不带出边——这条 txDone 边就是它的出口,删了会把 Boss 永久锁死。
|
||||
BossFragments.PhaseTransition(b, PhaseTx, AirPhaseIndex, PhaseTxInvincible)
|
||||
.To(Air).When(x => !x.Boss.IsPhaseTransitioning, "txDone");
|
||||
|
||||
// 阶段 1(空中):悬停朝向玩家 ↔ 选招。刻意不挂回地面阶段的边(阶段单向推进)。
|
||||
AiStateFragments.Locomotion(b, Air, LocomotionMode.Face)
|
||||
.To(AirAttack).When(x => x.Combat.HasEligibleAttack(), "attackInRange");
|
||||
|
||||
// 与地面攻击态同语义:起手就打完,不挂脱战边
|
||||
b.DeclareState(AirAttack)
|
||||
.OnEnter(x => { x.Locomotion.Stop(); x.Combat.UseBestAttack(); })
|
||||
.OnExit (x => x.Combat.InterruptAbilities())
|
||||
.To(Air).When(x => !x.Combat.IsAbilityRunning(), "attackDone");
|
||||
|
||||
// 死亡终态:演出走物理层的击败序列,图上只需停止决策
|
||||
AiStateFragments.Terminal(b, Death);
|
||||
b.Global().To(Death).OnEvent(AiSignal.Died);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,11 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 9df917389cefced4692c7e56ed9bd6e3
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
Reference in New Issue
Block a user