feat(enemy): AI 配方接入 SO 校验——漏配/歧义/零冷却 Committed 在编辑期与构建期报错

This commit is contained in:
2026-07-29 17:20:12 +08:00
parent 6df4a259b7
commit da217af113
6 changed files with 124 additions and 1 deletions
@@ -1,5 +1,7 @@
using System;
using System.Collections.Generic;
using BaseGames.AI;
using BaseGames.Core;
namespace BaseGames.Enemies
{
@@ -40,5 +42,9 @@ namespace BaseGames.Enemies
.OnExit (x => x.Combat.InterruptAbilities())
.To(Approach).When(x => !x.Combat.IsAbilityRunning(), "attackDone");
}
// 本模块无自身配置:招式的射程/冷却/权重全归敌人身上的 EnemyAttackSelector
// 而那是 GameObject 上的组件,配方资产看不到,无法在此校验。
public IEnumerable<ValidationResult> Validate() => Array.Empty<ValidationResult>();
}
}
@@ -1,6 +1,8 @@
using System;
using System.Collections.Generic;
using UnityEngine;
using BaseGames.AI;
using BaseGames.Core;
using BaseGames.Enemies.Abilities;
namespace BaseGames.Enemies
@@ -52,6 +54,27 @@ namespace BaseGames.Enemies
s.To(rest).When(AiStateFragments.LostAllZones, "leftAllZones");
}
public IEnumerable<ValidationResult> Validate()
{
if (_ability.IsEmpty)
{
yield return ValidationResult.Error("RushEngagement:未配置冲锋能力。");
yield break;
}
if (_ability.HasConflict)
yield return ValidationResult.Warning(
"RushEngagement:能力引用同时配了资产和字符串 id,资产优先、字符串被忽略。");
// Committed 的防抖动保证依赖能力自身有冷却:cooldown==0 时能力结束的下一帧
// CanUseAbility 即恢复为真,冲锋态↔未发现态每帧抖动照旧出现。
if (_exit == RushExit.Committed && _ability.Asset != null && _ability.Asset.cooldown <= 0f)
yield return ValidationResult.Error(
$"RushEngagement:脱战语义为 Committed,但所引用能力 '{_ability.Asset.name}' 的 cooldown 为 " +
$"{_ability.Asset.cooldown}。Committed 的防抖动保证依赖 cooldown > 0,否则冲锋打完下一帧" +
"就会再次触发,出现每帧抖动。请给该能力配置冷却,或把脱战语义改为 OnLostTarget。");
}
// 校验放这里而非构造函数:反序列化路径不走 ctor,漏配必须在建图时暴露。
string RequireAbilityId()
{
@@ -1,5 +1,7 @@
using System;
using System.Collections.Generic;
using BaseGames.AI;
using BaseGames.Core;
namespace BaseGames.Enemies
{
@@ -31,5 +33,9 @@ namespace BaseGames.Enemies
/// <summary>CanEngage 的可读标签(trace / 图导出用)。</summary>
string EngageLabel { get; }
/// <summary>校验本模块自身的配置。由 PerceptionRecipeSO.Validate() 汇总上报。
/// 无可失效配置的模块返回空序列。</summary>
IEnumerable<ValidationResult> Validate();
}
}