using System.Collections.Generic; using UnityEditor; using UnityEngine; using BaseGames.Progression; namespace BaseGames.Editor.Achievements { /// /// AchievementSO 自定义 Inspector(架构 16_SupportingModules §2.4)。 /// 在 conditions 数组中内联展示各 AchievementCondition SO 的关键字段, /// 并在头部显示条件类型的中文名,提供 Ping 和删除按钮。 /// [CustomEditor(typeof(AchievementSO))] public class AchievementSOEditor : UnityEditor.Editor { private static readonly Dictionary _conditionLabels = new() { { "DefeatedBossCondition", "击败 Boss" }, { "DefeatedAllBossesCondition", "击败全部 Boss" }, { "EnteredRegionCondition", "到达区域" }, { "MapExplorationCondition", "地图探索 %" }, { "CollectedItemCondition", "收集物品" }, { "CollectedAllCharmsCondition", "集满全部 Charm" }, { "UnlockedAllAbilitiesCondition", "解锁全部能力" }, { "NoHealRunCondition", "无治疗通关" }, { "TimedBossKillCondition", "限时击败 Boss" }, { "ParryCountCondition", "弹反 N 次" }, { "NailClashCountCondition", "拼刀 N 次" }, { "EventTriggeredCondition", "监听事件" }, }; private SerializedProperty _conditionsProp; private void OnEnable() { _conditionsProp = serializedObject.FindProperty("conditions"); } public override void OnInspectorGUI() { serializedObject.Update(); // 绘制除 conditions 之外的所有默认字段 DrawPropertiesExcluding(serializedObject, "conditions"); EditorGUILayout.Space(8); EditorGUILayout.LabelField("解锁条件(AND 全部满足)", EditorStyles.boldLabel); for (int i = 0; i < _conditionsProp.arraySize; i++) { var elemProp = _conditionsProp.GetArrayElementAtIndex(i); var condSO = elemProp.objectReferenceValue as AchievementCondition; string typeName = condSO?.GetType().Name ?? ""; string label = condSO != null && _conditionLabels.TryGetValue(typeName, out var n) ? $"{n} [{condSO.name}]" : (condSO != null ? $"{typeName} [{condSO.name}]" : "(未指定条件 SO)"); EditorGUILayout.BeginVertical(EditorStyles.helpBox); // 标题行 EditorGUILayout.BeginHorizontal(); EditorGUILayout.LabelField(label, EditorStyles.boldLabel); if (condSO != null && GUILayout.Button("↗", GUILayout.Width(24))) EditorGUIUtility.PingObject(condSO); var prevColor = GUI.color; GUI.color = Color.red * 0.9f; if (GUILayout.Button("✕", GUILayout.Width(24))) { GUI.color = prevColor; // 先将引用置空再删除,避免删除保留引用的 Unity 行为 _conditionsProp.GetArrayElementAtIndex(i).objectReferenceValue = null; _conditionsProp.DeleteArrayElementAtIndex(i); serializedObject.ApplyModifiedProperties(); break; } GUI.color = prevColor; EditorGUILayout.EndHorizontal(); // 内联展开 SO 字段(可编辑) if (condSO != null) { var innerSO = new SerializedObject(condSO); innerSO.Update(); var prop = innerSO.GetIterator(); prop.NextVisible(true); // 跳过 m_Script while (prop.NextVisible(false)) EditorGUILayout.PropertyField(prop, true); if (innerSO.ApplyModifiedProperties()) EditorUtility.SetDirty(condSO); } else { EditorGUILayout.PropertyField(elemProp, GUIContent.none); } EditorGUILayout.EndVertical(); EditorGUILayout.Space(2); } // 添加按钮 EditorGUILayout.BeginHorizontal(); if (GUILayout.Button("+ 添加条件 SO 引用")) _conditionsProp.arraySize++; EditorGUILayout.EndHorizontal(); serializedObject.ApplyModifiedProperties(); } } }