fix(editor): EnemyAiBrain 检视器支持多选——mixed 值不再误写,违规对象逐个点名

This commit is contained in:
2026-07-30 08:46:41 +08:00
parent b36ac96ac7
commit aaca138774
@@ -36,8 +36,8 @@ namespace BaseGames.Editor.AI
DrawInvariantHelp();
// 其余字段(若将来新增)照常绘制
DrawPropertiesExcluding(serializedObject, "m_Script", "_recipe", "_definitionId");
// 其余字段(若将来新增)照常绘制;m_Script 与项目其他自定义检视器惯例一致,不排除
DrawPropertiesExcluding(serializedObject, "_recipe", "_definitionId");
serializedObject.ApplyModifiedProperties();
}
@@ -50,19 +50,26 @@ namespace BaseGames.Editor.AI
string current = _definitionId.stringValue;
int index = string.IsNullOrEmpty(current) ? 0 : ids.IndexOf(current);
// 已配的 id 在注册表里找不到(脚本被删/改名)——保留原值并显式标出,不静默清空
bool missing = index < 0;
bool mixedRecipe = _recipe.hasMultipleDifferentValues;
bool mixedId = _definitionId.hasMultipleDifferentValues;
// 已配的 id 在注册表里找不到(脚本被删/改名)——保留原值并显式标出,不静默清空。
// mixed 时 current 只是其中一个对象的值,拿它去查注册表会误报,跳过该判定。
bool missing = !mixedId && index < 0;
if (missing)
{
ids.Add($"{current} ⚠ 注册表中不存在");
index = ids.Count - 1;
}
using (new EditorGUI.DisabledScope(_recipe.objectReferenceValue != null))
// 选中对象里只要有任何一个已配方,就不该从这里批量写 id
using (new EditorGUI.DisabledScope(_recipe.objectReferenceValue != null || mixedRecipe))
{
EditorGUI.showMixedValue = mixedId;
int picked = EditorGUILayout.Popup(
new GUIContent("定制脚本 id", "[AiDefinition(id)] 的 AiScript 子类 id。与配方资产二选一"),
index, ids.Select(s => new GUIContent(s)).ToArray());
EditorGUI.showMixedValue = false;
if (picked != index)
_definitionId.stringValue = picked == 0 ? string.Empty : ids[picked];
@@ -76,21 +83,31 @@ namespace BaseGames.Editor.AI
void DrawInvariantHelp()
{
if (_recipe.hasMultipleDifferentValues || _definitionId.hasMultipleDifferentValues)
return; // 多选且不一致时不下断言
// hasMultipleDifferentValues 时 SerializedProperty.objectReferenceValue /
// stringValue 只返回其中一个对象的值,不能整体判断——逐对象检查,把违规的点名。
var bothSet = new List<string>();
var neither = new List<string>();
bool hasRecipe = _recipe.objectReferenceValue != null;
bool hasId = !string.IsNullOrEmpty(_definitionId.stringValue);
foreach (var obj in serializedObject.targetObjects)
{
if (obj == null) continue;
var so = new SerializedObject(obj);
bool hasRecipe = so.FindProperty("_recipe").objectReferenceValue != null;
bool hasId = !string.IsNullOrEmpty(so.FindProperty("_definitionId").stringValue);
if (hasRecipe && hasId) bothSet.Add(obj.name);
else if (!hasRecipe && !hasId) neither.Add(obj.name);
}
if (hasRecipe && hasId)
if (bothSet.Count > 0)
EditorGUILayout.HelpBox(
"配方资产与定制脚本 id 同时配置了。二者互斥,运行时 Awake 会报错并禁用本组件。" +
"请清掉其中一项。", MessageType.Error);
else if (!hasRecipe && !hasId)
$"以下对象同时配置了配方资产与定制脚本 id{string.Join("", bothSet)}。" +
"二者互斥,运行时 Awake 会报错并禁用组件。请清掉其中一项。", MessageType.Error);
if (neither.Count > 0)
EditorGUILayout.HelpBox(
"尚未配置 AI 来源。约 95% 的敌人应指定配方资产(用菜单 " +
"BaseGames/AI/Enemy AI Recipe Wizard 创建);有独门机制的敌人才写 " +
"[AiDefinition] 的 AiScript 子类并在此选它的 id。", MessageType.Error);
$"以下对象尚未配置 AI 来源{string.Join("", neither)}。" +
"约 95% 的敌人应指定配方资产(用菜单 BaseGames/AI/Enemy AI Recipe Wizard 创建);" +
"有独门机制的敌人才写 [AiDefinition] 的 AiScript 子类并在此选它的 id。", MessageType.Error);
}
}
}