refactor(enemy): E001 AI 迁移为配方资产,退役定制脚本

用 EnemyAiRecipeWizard 建 ENM_E001_Ai.asset,配置未发现层
DisguiseThenPatrol + 交战层 RushEngagement(e001_chase, Committed);
TestRoomA 场景 EnemyAiBrain 改绑 _recipe,_definitionId 清空;
删除 E001CaoZhiAi.cs;SceneObjectPlacerTool 的 E001 挂载分支与其余
AI(BrainGraph) 提示文案统一改为指向配方向导路径。
This commit is contained in:
2026-07-29 15:38:44 +08:00
parent ecdb0040cb
commit 44412adfc7
6 changed files with 75 additions and 53 deletions
@@ -0,0 +1,32 @@
%YAML 1.1
%TAG !u! tag:unity3d.com,2011:
--- !u!114 &11400000
MonoBehaviour:
m_ObjectHideFlags: 0
m_CorrespondingSourceObject: {fileID: 0}
m_PrefabInstance: {fileID: 0}
m_PrefabAsset: {fileID: 0}
m_GameObject: {fileID: 0}
m_Enabled: 1
m_EditorHideFlags: 0
m_Script: {fileID: 11500000, guid: 0b849b67f708c3b4f9af91a9e5f2e546, type: 3}
m_Name: ENM_E001_Ai
m_EditorClassIdentifier:
_unaware:
rid: 2385399935748276230
_engagement:
rid: 2385399935748276231
references:
version: 2
RefIds:
- rid: 2385399935748276230
type: {class: DisguiseThenPatrol, ns: BaseGames.Enemies, asm: BaseGames.Enemies}
data:
_disguiseMode: 0
_patrolMode: 1
- rid: 2385399935748276231
type: {class: RushEngagement, ns: BaseGames.Enemies, asm: BaseGames.Enemies}
data:
_ability:
_asset: {fileID: 11400000, guid: 0adeaa8a8508fbd40986dbb71cc85acd, type: 2}
_exit: 1
@@ -1,8 +1,8 @@
fileFormatVersion: 2 fileFormatVersion: 2
guid: d0934d78d9f1fe34aa6c0429a0822df8 guid: ec998c8e30b9c514d81b67da92c6d42a
folderAsset: yes NativeFormatImporter:
DefaultImporter:
externalObjects: {} externalObjects: {}
mainObjectFileID: 11400000
userData: userData:
assetBundleName: assetBundleName:
assetBundleVariant: assetBundleVariant:
+2 -1
View File
@@ -5403,7 +5403,8 @@ MonoBehaviour:
m_Script: {fileID: 11500000, guid: d92d657c6bbdfe246a7333e04f79f8f7, type: 3} m_Script: {fileID: 11500000, guid: d92d657c6bbdfe246a7333e04f79f8f7, type: 3}
m_Name: m_Name:
m_EditorClassIdentifier: m_EditorClassIdentifier:
_definitionId: E001 _recipe: {fileID: 11400000, guid: ec998c8e30b9c514d81b67da92c6d42a, type: 2}
_definitionId:
--- !u!114 &310446317 --- !u!114 &310446317
MonoBehaviour: MonoBehaviour:
m_ObjectHideFlags: 0 m_ObjectHideFlags: 0
@@ -338,8 +338,9 @@ namespace BaseGames.Editor
SetupPerceptionSystemSlots(sensorHub, new[] { "aggro", "los" }, report); SetupPerceptionSystemSlots(sensorHub, new[] { "aggro", "los" }, report);
report.Add("★ 指定 EnemyBase._statsSO、_animConfig 资产(按所创建的敌人类型命名)。"); report.Add("★ 指定 EnemyBase._statsSO、_animConfig 资产(按所创建的敌人类型命名)。");
report.Add("★ AI(BrainGraph)先写一个 [AiDefinition(\"<id>\")] 的 AiScript 子类" + report.Add("★ AI(BrainGraph)用菜单 BaseGames/AI/Enemy AI Recipe Wizard 建一个配方资产" +
"再挂 EnemyAiBrain 组件并把 _definitionId 设为该 id。"); "在 Inspector 选两层模块(未发现/交战),再挂 EnemyAiBrain 组件并把 _recipe 指向该资产。" +
"有独门机制的敌人才写 [AiDefinition] 的 AiScript 子类并改用 _definitionId。");
Undo.CollapseUndoOperations(undoGroup); Undo.CollapseUndoOperations(undoGroup);
Selection.activeGameObject = go; Selection.activeGameObject = go;
@@ -402,8 +403,9 @@ namespace BaseGames.Editor
SetupPerceptionSystemSlots(sensorHub, new[] { "aggro", "attack_melee", "attack_range", "los" }, report); SetupPerceptionSystemSlots(sensorHub, new[] { "aggro", "attack_melee", "attack_range", "los" }, report);
report.Add("填写 _bossId。"); report.Add("填写 _bossId。");
report.Add("★ 挂载 BossSkillExecutor 并指定 BossSkillSO 列表(技能执行层)。"); report.Add("★ 挂载 BossSkillExecutor 并指定 BossSkillSO 列表(技能执行层)。");
report.Add("★ AI(BrainGraph)先写一个 [AiDefinition(\"<id>\")] 的 AiScript 子类" + report.Add("★ AI(BrainGraph)用菜单 BaseGames/AI/Enemy AI Recipe Wizard 建一个配方资产" +
"再挂 EnemyAiBrain 组件并把 _definitionId 设为该 id。"); "在 Inspector 选两层模块(未发现/交战),再挂 EnemyAiBrain 组件并把 _recipe 指向该资产。" +
"有独门机制的敌人才写 [AiDefinition] 的 AiScript 子类并改用 _definitionId。");
report.Add("多阶段 Boss 可在此 GameObject 上继续 AddComponent 阶段切换控制器。"); report.Add("多阶段 Boss 可在此 GameObject 上继续 AddComponent 阶段切换控制器。");
Selection.activeGameObject = go; Selection.activeGameObject = go;
@@ -495,10 +497,21 @@ namespace BaseGames.Editor
GetOrAddComponent<EnemyLocomotion>(go); GetOrAddComponent<EnemyLocomotion>(go);
// BrainGraph AI:挂载决策组件并绑定定义 id(取代旧的 Opsive BehaviorTree // BrainGraph AI:挂载决策组件并绑定 AI 配方资产(取代旧的 Opsive BehaviorTree
var brain = GetOrAddComponent<EnemyAiBrain>(go); var brain = GetOrAddComponent<EnemyAiBrain>(go);
AssignString(brain, "_definitionId", "E001", report); var recipe = AssetDatabase.LoadAssetAtPath<PerceptionRecipeSO>(
report.Add("✔ 已挂载 EnemyAiBrain 并绑定 AI 定义 id=E001BrainGraph)。"); "Assets/_Game/Data/Enemies/E001/ENM_E001_Ai.asset");
if (recipe == null)
{
// 根因暴露:配方缺失就报出来,不退回旧的字符串 id 路径掩盖。
report.Add("✘ 未找到 AI 配方 ENM_E001_Ai —— 请先用菜单 BaseGames/AI/Enemy AI Recipe Wizard 创建。");
}
else
{
AssignReference(brain, "_recipe", recipe, report);
AssignString(brain, "_definitionId", "", report);
report.Add("✔ 已挂载 EnemyAiBrain 并绑定 AI 配方 ENM_E001_AiBrainGraph)。");
}
Undo.CollapseUndoOperations(undoGroup); Undo.CollapseUndoOperations(undoGroup);
Selection.activeGameObject = go; Selection.activeGameObject = go;
@@ -598,8 +611,9 @@ namespace BaseGames.Editor
report.Add("★ 将此对象放置于天花板,调整位置使 CapsuleCollider 正好贴合天花板底面。"); report.Add("★ 将此对象放置于天花板,调整位置使 CapsuleCollider 正好贴合天花板底面。");
report.Add("★ HurtBox.enabled 初始为 false;钻出后由 CeilingHangStrikeAbility 在悬挂窗口开启。"); report.Add("★ HurtBox.enabled 初始为 false;钻出后由 CeilingHangStrikeAbility 在悬挂窗口开启。");
report.Add("★ attack_range 槽位为正下方 BoxCast(玩家经过检测区);按需在 Inspector 调整 offset/size。"); report.Add("★ attack_range 槽位为正下方 BoxCast(玩家经过检测区);按需在 Inspector 调整 offset/size。");
report.Add("★ AI(BrainGraph)E002 尚无 AI 定义——先写 [AiDefinition(\"E002\")] 的 AiScript 子类" + report.Add("★ AI(BrainGraph)用菜单 BaseGames/AI/Enemy AI Recipe Wizard 建一个配方资产" +
"再挂 EnemyAiBrain 并设 _definitionId=E002。"); "在 Inspector 选两层模块(未发现/交战),再挂 EnemyAiBrain 组件并把 _recipe 指向该资产。" +
"有独门机制的敌人才写 [AiDefinition] 的 AiScript 子类并改用 _definitionId。");
report.Add("★ BD 树逻辑建议:Idle(悬挂)→ IsSensorDetecting(aggro) → UseAbility(CeilingDrop) → IsGrounded → Patrol(Pace)。"); report.Add("★ BD 树逻辑建议:Idle(悬挂)→ IsSensorDetecting(aggro) → UseAbility(CeilingDrop) → IsGrounded → Patrol(Pace)。");
Undo.CollapseUndoOperations(undoGroup); Undo.CollapseUndoOperations(undoGroup);
@@ -685,8 +699,9 @@ namespace BaseGames.Editor
SetupPerceptionSystemSlots(sensorHub, new[] { "aggro", "los" }, report); SetupPerceptionSystemSlots(sensorHub, new[] { "aggro", "los" }, report);
report.Add("★ 将此对象放置于天花板下方;EnemyAbilityTrigger 在出生时(executeOnSpawn)自动、或被场景触发器调用 Trigger() 时执行下坠能力(e003_fall)。"); report.Add("★ 将此对象放置于天花板下方;EnemyAbilityTrigger 在出生时(executeOnSpawn)自动、或被场景触发器调用 Trigger() 时执行下坠能力(e003_fall)。");
report.Add("★ AI(BrainGraph)该敌人尚无 AI 定义——先写 [AiDefinition(\"<id>\")] 的 AiScript 子类" + report.Add("★ AI(BrainGraph)用菜单 BaseGames/AI/Enemy AI Recipe Wizard 建一个配方资产" +
"再挂 EnemyAiBrain 并把 _definitionId 设为该 id。"); "在 Inspector 选两层模块(未发现/交战),再挂 EnemyAiBrain 组件并把 _recipe 指向该资产。" +
"有独门机制的敌人才写 [AiDefinition] 的 AiScript 子类并改用 _definitionId。");
Undo.CollapseUndoOperations(undoGroup); Undo.CollapseUndoOperations(undoGroup);
Selection.activeGameObject = go; Selection.activeGameObject = go;
@@ -815,8 +830,9 @@ namespace BaseGames.Editor
SetupPerceptionSystemSlots(sensorHub, new[] { "aggro", "attack_melee", "attack_range", "sight" }, report); SetupPerceptionSystemSlots(sensorHub, new[] { "aggro", "attack_melee", "attack_range", "sight" }, report);
report.Add("★ AppearAbility._appearClip / FacePlayerAbility._faceClip 等动画 Clip 待美术接入后在 Inspector 指定。"); report.Add("★ AppearAbility._appearClip / FacePlayerAbility._faceClip 等动画 Clip 待美术接入后在 Inspector 指定。");
report.Add("★ 在 EnemyDeathSequence._deathPreClip 配置死亡前摇动画(两阶段死亡 Death_Pre 无敌)。"); report.Add("★ 在 EnemyDeathSequence._deathPreClip 配置死亡前摇动画(两阶段死亡 Death_Pre 无敌)。");
report.Add("★ AI(BrainGraph)该敌人尚无 AI 定义——先写 [AiDefinition(\"<id>\")] 的 AiScript 子类" + report.Add("★ AI(BrainGraph)用菜单 BaseGames/AI/Enemy AI Recipe Wizard 建一个配方资产" +
"再挂 EnemyAiBrain 并把 _definitionId 设为该 id。"); "在 Inspector 选两层模块(未发现/交战),再挂 EnemyAiBrain 组件并把 _recipe 指向该资产。" +
"有独门机制的敌人才写 [AiDefinition] 的 AiScript 子类并改用 _definitionId。");
Undo.CollapseUndoOperations(undoGroup); Undo.CollapseUndoOperations(undoGroup);
Selection.activeGameObject = go; Selection.activeGameObject = go;
@@ -925,8 +941,9 @@ namespace BaseGames.Editor
SetupPerceptionSystemSlots(sensorHub, new[] { "aggro", "attack_melee", "attack_range", "los" }, report); SetupPerceptionSystemSlots(sensorHub, new[] { "aggro", "attack_melee", "attack_range", "los" }, report);
report.Add("★ 在 EnemyDeathSequence._deathPreClip 上添加 AnimationEvent 调用 SpawnProjectile(\"spawn_e003\"),由 EnemySpawnerOnEvent 从对象池生成幼蛭(ENM_YouZhi)。"); report.Add("★ 在 EnemyDeathSequence._deathPreClip 上添加 AnimationEvent 调用 SpawnProjectile(\"spawn_e003\"),由 EnemySpawnerOnEvent 从对象池生成幼蛭(ENM_YouZhi)。");
report.Add("★ AI(BrainGraph)该敌人尚无 AI 定义——先写 [AiDefinition(\"<id>\")] 的 AiScript 子类" + report.Add("★ AI(BrainGraph)用菜单 BaseGames/AI/Enemy AI Recipe Wizard 建一个配方资产" +
"再挂 EnemyAiBrain 并把 _definitionId 设为该 id。"); "在 Inspector 选两层模块(未发现/交战),再挂 EnemyAiBrain 组件并把 _recipe 指向该资产。" +
"有独门机制的敌人才写 [AiDefinition] 的 AiScript 子类并改用 _definitionId。");
Undo.CollapseUndoOperations(undoGroup); Undo.CollapseUndoOperations(undoGroup);
Selection.activeGameObject = go; Selection.activeGameObject = go;
@@ -1024,8 +1041,9 @@ namespace BaseGames.Editor
} }
SetupPerceptionSystemSlots(sensorHub, new[] { "aggro", "los" }, report); SetupPerceptionSystemSlots(sensorHub, new[] { "aggro", "los" }, report);
report.Add("★ AI(BrainGraph)E006 尚无 AI 定义——先写 [AiDefinition(\"E006\")] 的 AiScript 子类" + report.Add("★ AI(BrainGraph)用菜单 BaseGames/AI/Enemy AI Recipe Wizard 建一个配方资产" +
"再挂 EnemyAiBrain 并设 _definitionId=E006。"); "在 Inspector 选两层模块(未发现/交战),再挂 EnemyAiBrain 组件并把 _recipe 指向该资产。" +
"有独门机制的敌人才写 [AiDefinition] 的 AiScript 子类并改用 _definitionId。");
Undo.CollapseUndoOperations(undoGroup); Undo.CollapseUndoOperations(undoGroup);
Selection.activeGameObject = go; Selection.activeGameObject = go;
@@ -1168,8 +1186,9 @@ namespace BaseGames.Editor
report.Add("★ FanCombo 三段 HitBox 与 Tornado HitBox 已挂入 BossSkillExecutor._hitBoxes。"); report.Add("★ FanCombo 三段 HitBox 与 Tornado HitBox 已挂入 BossSkillExecutor._hitBoxes。");
report.Add("★ 将 BoomerangMuzzle / TornadoMuzzle / WindStoneMuzzle 拖入对应 BossSkillSO 的发射点字段(如有)。"); report.Add("★ 将 BoomerangMuzzle / TornadoMuzzle / WindStoneMuzzle 拖入对应 BossSkillSO 的发射点字段(如有)。");
report.Add("★ 回旋扇收招/阶段过渡/击败演出等动画 Clip 待美术接入后在 ChaoFengBoss Inspector 指定。"); report.Add("★ 回旋扇收招/阶段过渡/击败演出等动画 Clip 待美术接入后在 ChaoFengBoss Inspector 指定。");
report.Add("★ AI(BrainGraph)嘲风尚无 AI 定义——先写 [AiDefinition(\"ChaoFeng\")] 的 AiScript 子类" + report.Add("★ AI(BrainGraph)用菜单 BaseGames/AI/Enemy AI Recipe Wizard 建一个配方资产" +
"再挂 EnemyAiBrain 并设 _definitionId=ChaoFeng(技能执行仍由 BossSkillExecutor 负责)。"); "在 Inspector 选两层模块(未发现/交战),再挂 EnemyAiBrain 组件并把 _recipe 指向该资产" +
"(技能执行仍由 BossSkillExecutor 负责)。有独门机制的敌人才写 [AiDefinition] 的 AiScript 子类并改用 _definitionId。");
Undo.CollapseUndoOperations(undoGroup); Undo.CollapseUndoOperations(undoGroup);
Selection.activeGameObject = go; Selection.activeGameObject = go;
@@ -1,19 +0,0 @@
using BaseGames.AI;
namespace BaseGames.Enemies
{
/// <summary>
/// E001(草蛭)AI —— 纯决策层,组装两层模块。
/// 出生伪装静止(伪装成石头),一旦交战过就只回巡逻;追击=带冷却的接触冲锋,
/// 起手即锁定、打完回巡逻走冷却;死亡演出走物理状态机(EnemyBase.PerformDeath)。
/// AI 只决策;移动 / 朝向 / 速度 / 动画 / 伤害全部在能力与 EnemyLocomotion 里实现。
/// 依据 Docs/Game/敌人/小怪/E001_草蛭.md。
/// </summary>
[AiDefinition("E001")]
public sealed class E001CaoZhiAi : AiScript
{
protected override void Build(BrainBuilder b) => PerceptionSkeleton.Add(b,
unaware: new DisguiseThenPatrol(),
engagement: new RushEngagement("e001_chase", RushExit.Committed));
}
}
@@ -1,11 +0,0 @@
fileFormatVersion: 2
guid: 82a595c9b77505c448845d8cd1a56064
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant: