diff --git a/Assets/_Game/Scripts/AI/AiDefinitionRegistry.cs b/Assets/_Game/Scripts/AI/AiDefinitionRegistry.cs index ddf4a9b0..5cada826 100644 --- a/Assets/_Game/Scripts/AI/AiDefinitionRegistry.cs +++ b/Assets/_Game/Scripts/AI/AiDefinitionRegistry.cs @@ -57,6 +57,16 @@ namespace BaseGames.AI return script.GetOrBuildGraph(); } + /// 取 id 对应的定义;不存在则显式抛错(根因暴露,不做兜底)。 + 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 Ids { get { EnsureBuilt(); return _byId.Keys; } diff --git a/Assets/_Game/Scripts/Enemies/AIBrain/EnemyAiBrain.cs b/Assets/_Game/Scripts/Enemies/AIBrain/EnemyAiBrain.cs index 20123148..da443b0d 100644 --- a/Assets/_Game/Scripts/Enemies/AIBrain/EnemyAiBrain.cs +++ b/Assets/_Game/Scripts/Enemies/AIBrain/EnemyAiBrain.cs @@ -11,14 +11,17 @@ namespace BaseGames.Enemies [RequireComponent(typeof(EnemyBase))] 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; EnemyBase _enemy; EnemyBrainContext _context; AiRuntime _runtime; - public string DefinitionId => _definitionId; + public string DefinitionId => _recipe != null ? _recipe.name : _definitionId; public string CurrentStateName => _runtime != null ? _runtime.CurrentStateName : "(none)"; public bool IsSuspended => _runtime != null && _runtime.IsSuspended; public AiRuntime Runtime => _runtime; @@ -26,10 +29,15 @@ namespace BaseGames.Enemies void Awake() { _enemy = GetComponent(); - 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(配方资产)或 _definitionId(AiScript id)。当前 recipe={(hasRecipe ? _recipe.name : "空")}, id='{_definitionId}':{name}", + this); enabled = false; } } @@ -38,7 +46,10 @@ namespace BaseGames.Enemies // 再触发入口状态 OnEnter(其会声明 locomotion 意图)。所有 Awake 先于任一 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); _runtime = new AiRuntime(graph, _context); }