#if UNITY_EDITOR using System; using System.Collections.Generic; using System.Linq; using UnityEditor; using UnityEngine; using BaseGames.Equipment; namespace BaseGames.Editor.Equipment { /// /// 为 CharmSO.effects(List<ICharmEffect>)提供友好的 Inspector 体验(架构 09_ProgressionModule §4.1)。 /// - 下拉菜单选类型(显示中文名而非 C# 全称) /// - 每条效果展开显示字段 + GetEffectDescription() 预览文字 /// - 支持单条删除 /// [CustomEditor(typeof(CharmSO))] public class CharmSOEditor : UnityEditor.Editor { // 已注册的所有 ICharmEffect 实现类型(反射收集) private static readonly Type[] _effectTypes = CollectEffectTypes(); // 策划友好名称映射 private static readonly Dictionary _typeLabels = new() { { typeof(StatModifierEffect), "属性加成" }, { typeof(AttackSpeedEffect), "攻击速度" }, { typeof(OnHitEffect), "命中触发" }, { typeof(SoulSpellEffect), "灵魂法术" }, { typeof(SkillNumericModifierEffect), "技能数值修改" }, { typeof(SkillSlotOverrideEffect), "技能插槽替换" }, { typeof(WeaponOverrideEffect), "武器替换" }, }; private SerializedProperty _effectsProp; private void OnEnable() => _effectsProp = serializedObject.FindProperty("effects"); public override void OnInspectorGUI() { serializedObject.Update(); DrawPropertiesExcluding(serializedObject, "effects"); EditorGUILayout.Space(8); EditorGUILayout.LabelField("Effects", EditorStyles.boldLabel); if (_effectsProp != null) { for (int i = 0; i < _effectsProp.arraySize; i++) { var elemProp = _effectsProp.GetArrayElementAtIndex(i); var effect = elemProp.managedReferenceValue as ICharmEffect; string label = effect != null && _typeLabels.TryGetValue(effect.GetType(), out var n) ? n : (effect?.GetType().Name ?? "null"); EditorGUILayout.BeginVertical(EditorStyles.helpBox); EditorGUILayout.BeginHorizontal(); EditorGUILayout.LabelField(label, EditorStyles.boldLabel); if (GUILayout.Button("✕", GUILayout.Width(24))) { _effectsProp.DeleteArrayElementAtIndex(i); serializedObject.ApplyModifiedProperties(); break; } EditorGUILayout.EndHorizontal(); EditorGUILayout.PropertyField(elemProp, GUIContent.none, true); if (effect != null) EditorGUILayout.LabelField(effect.GetEffectDescription(), EditorStyles.miniLabel); EditorGUILayout.EndVertical(); EditorGUILayout.Space(2); } } // 添加效果按钮(下拉菜单) if (GUILayout.Button("+ 添加效果")) { var menu = new GenericMenu(); foreach (var t in _effectTypes) { var captured = t; string menuLabel = _typeLabels.GetValueOrDefault(t, t.Name); menu.AddItem(new GUIContent(menuLabel), false, () => { if (_effectsProp == null) return; _effectsProp.arraySize++; _effectsProp .GetArrayElementAtIndex(_effectsProp.arraySize - 1) .managedReferenceValue = Activator.CreateInstance(captured); serializedObject.ApplyModifiedProperties(); }); } menu.ShowAsContext(); } serializedObject.ApplyModifiedProperties(); } private static Type[] CollectEffectTypes() { var baseType = typeof(ICharmEffect); return AppDomain.CurrentDomain.GetAssemblies() .SelectMany(a => { try { return a.GetTypes(); } catch { return Array.Empty(); } }) .Where(t => t.IsClass && !t.IsAbstract && baseType.IsAssignableFrom(t)) .ToArray(); } } } #endif