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