feat(enemy): EnemyAiBrain 支持配方资产/AiScript id 二选一解析

This commit is contained in:
2026-07-29 14:56:54 +08:00
parent c76eba8a0e
commit 57cbaacf2c
2 changed files with 27 additions and 6 deletions
@@ -57,6 +57,16 @@ namespace BaseGames.AI
return script.GetOrBuildGraph(); return script.GetOrBuildGraph();
} }
/// <summary>取 id 对应的定义;不存在则显式抛错(根因暴露,不做兜底)。</summary>
public static IAiDefinition GetDefinition(string id)
{
EnsureBuilt();
if (!_byId.TryGetValue(id, out var script))
throw new InvalidOperationException(
$"AiDefinitionRegistry: 未找到 AI 定义 id '{id}'。请确认存在 [AiDefinition(\"{id}\")] 的 AiScript 子类。");
return script;
}
public static IEnumerable<string> Ids public static IEnumerable<string> Ids
{ {
get { EnsureBuilt(); return _byId.Keys; } get { EnsureBuilt(); return _byId.Keys; }
@@ -11,14 +11,17 @@ namespace BaseGames.Enemies
[RequireComponent(typeof(EnemyBase))] [RequireComponent(typeof(EnemyBase))]
public sealed class EnemyAiBrain : MonoBehaviour public sealed class EnemyAiBrain : MonoBehaviour
{ {
[Tooltip("对应 [AiDefinition(id)] AI id E001")] [Tooltip("配方路径(约 95% 的敌人):直接引用 AI 配方资产")]
[SerializeField] AiRecipeSO _recipe;
[Tooltip("定制路径(约 5% 的敌人):[AiDefinition(id)] AiScript id")]
[SerializeField] string _definitionId; [SerializeField] string _definitionId;
EnemyBase _enemy; EnemyBase _enemy;
EnemyBrainContext _context; EnemyBrainContext _context;
AiRuntime _runtime; AiRuntime _runtime;
public string DefinitionId => _definitionId; public string DefinitionId => _recipe != null ? _recipe.name : _definitionId;
public string CurrentStateName => _runtime != null ? _runtime.CurrentStateName : "(none)"; public string CurrentStateName => _runtime != null ? _runtime.CurrentStateName : "(none)";
public bool IsSuspended => _runtime != null && _runtime.IsSuspended; public bool IsSuspended => _runtime != null && _runtime.IsSuspended;
public AiRuntime Runtime => _runtime; public AiRuntime Runtime => _runtime;
@@ -26,10 +29,15 @@ namespace BaseGames.Enemies
void Awake() void Awake()
{ {
_enemy = GetComponent<EnemyBase>(); _enemy = GetComponent<EnemyBase>();
if (string.IsNullOrEmpty(_definitionId))
bool hasRecipe = _recipe != null;
bool hasId = !string.IsNullOrEmpty(_definitionId);
if (hasRecipe == hasId) // 都配了 或 都没配
{ {
// 根因暴露:漏配 id 直接报错,不静默兜底。 // 根因暴露:配置歧义 / 漏配直接报错,不静默兜底。
Debug.LogError($"EnemyAiBrain 未配置 _definitionId{name}", this); Debug.LogError(
$"EnemyAiBrain 必须且只能配置一项:_recipe(配方资产)或 _definitionIdAiScript id)。当前 recipe={(hasRecipe ? _recipe.name : "")}, id='{_definitionId}'{name}",
this);
enabled = false; enabled = false;
} }
} }
@@ -38,7 +46,10 @@ namespace BaseGames.Enemies
// 再触发入口状态 OnEnter(其会声明 locomotion 意图)。所有 Awake 先于任一 Start 执行。 // 再触发入口状态 OnEnter(其会声明 locomotion 意图)。所有 Awake 先于任一 Start 执行。
void Start() void Start()
{ {
var graph = AiDefinitionRegistry.GetGraph(_definitionId); // 不存在则抛异常 IAiDefinition definition = _recipe != null
? (IAiDefinition)_recipe
: AiDefinitionRegistry.GetDefinition(_definitionId); // 不存在则抛异常
var graph = definition.GetOrBuildGraph();
_context = new EnemyBrainContext(_enemy); _context = new EnemyBrainContext(_enemy);
_runtime = new AiRuntime(graph, _context); _runtime = new AiRuntime(graph, _context);
} }