409 lines
14 KiB
C#
409 lines
14 KiB
C#
using System.Collections.Generic;
|
||
using UnityEditor;
|
||
using UnityEngine;
|
||
|
||
[CustomPropertyDrawer(typeof(ActionInfo))]
|
||
public class ActionInfoDrawer : PropertyDrawer
|
||
{
|
||
public override void OnGUI(Rect position, SerializedProperty property, GUIContent label)
|
||
{
|
||
var actionIdProp = property.FindPropertyRelative("ActionId");
|
||
var actionTypeProp = actionIdProp?.FindPropertyRelative("ActionType");
|
||
|
||
string actionTypeName = (actionTypeProp != null && actionTypeProp.propertyType == SerializedPropertyType.Enum)
|
||
? actionTypeProp.enumDisplayNames[actionTypeProp.enumValueIndex]
|
||
: "Unknown";
|
||
|
||
string secondaryName = "None";
|
||
|
||
if (actionIdProp != null)
|
||
{
|
||
SerializedProperty subProp = actionIdProp.Copy();
|
||
SerializedProperty endProp = subProp.GetEndProperty();
|
||
|
||
bool enterChildren = true;
|
||
if (subProp.NextVisible(enterChildren))
|
||
{
|
||
do
|
||
{
|
||
if (SerializedProperty.EqualContents(subProp, endProp))
|
||
break;
|
||
|
||
if (subProp.propertyType == SerializedPropertyType.Enum &&
|
||
subProp.name != "ActionType")
|
||
{
|
||
if (subProp.enumValueIndex != 0 && subProp.enumValueIndex < subProp.enumDisplayNames.Length)
|
||
{
|
||
secondaryName = subProp.enumDisplayNames[subProp.enumValueIndex];
|
||
break;
|
||
}
|
||
}
|
||
} while (subProp.NextVisible(false));
|
||
}
|
||
}
|
||
|
||
// 构造标题(不修改原始label,创建新的GUIContent)
|
||
string rawLabel = label.text;
|
||
string index = rawLabel.Replace("Element", "").Trim();
|
||
GUIContent newLabel = new GUIContent($"[{actionTypeName}: {secondaryName}] {index}");
|
||
|
||
// Foldout
|
||
property.isExpanded = EditorGUI.Foldout(
|
||
new Rect(position.x, position.y, position.width, EditorGUIUtility.singleLineHeight),
|
||
property.isExpanded,
|
||
newLabel
|
||
);
|
||
|
||
// 展开所有字段
|
||
if (property.isExpanded)
|
||
{
|
||
EditorGUI.indentLevel++;
|
||
float y = position.y + EditorGUIUtility.singleLineHeight;
|
||
|
||
SerializedProperty childProp = property.Copy();
|
||
SerializedProperty end = childProp.GetEndProperty();
|
||
|
||
if (childProp.NextVisible(true))
|
||
{
|
||
do
|
||
{
|
||
if (SerializedProperty.EqualContents(childProp, end))
|
||
break;
|
||
|
||
float h = EditorGUI.GetPropertyHeight(childProp, true);
|
||
EditorGUI.PropertyField(new Rect(position.x, y, position.width, h), childProp, true);
|
||
y += h + EditorGUIUtility.standardVerticalSpacing;
|
||
|
||
} while (childProp.NextVisible(false));
|
||
}
|
||
|
||
EditorGUI.indentLevel--;
|
||
}
|
||
}
|
||
|
||
public override float GetPropertyHeight(SerializedProperty property, GUIContent label)
|
||
{
|
||
float height = EditorGUIUtility.singleLineHeight;
|
||
|
||
if (property.isExpanded)
|
||
{
|
||
SerializedProperty childProp = property.Copy();
|
||
SerializedProperty end = childProp.GetEndProperty();
|
||
|
||
if (childProp.NextVisible(true))
|
||
{
|
||
do
|
||
{
|
||
if (SerializedProperty.EqualContents(childProp, end))
|
||
break;
|
||
|
||
height += EditorGUI.GetPropertyHeight(childProp, true) + EditorGUIUtility.standardVerticalSpacing;
|
||
} while (childProp.NextVisible(false));
|
||
}
|
||
}
|
||
|
||
return height;
|
||
}
|
||
}
|
||
|
||
// =================================== ActionDataAssets 自定义编辑器(带搜索功能) ===================================
|
||
|
||
[CustomEditor(typeof(ActionDataAssets))]
|
||
public class ActionDataAssetsEditor : Editor
|
||
{
|
||
private string _searchFilter = "";
|
||
private List<int> _filteredIndices = new List<int>();
|
||
private SerializedProperty _actionListProperty;
|
||
private Vector2 _scrollPosition;
|
||
private bool _isSearching = false;
|
||
|
||
private void OnEnable()
|
||
{
|
||
_actionListProperty = serializedObject.FindProperty("ActionList");
|
||
}
|
||
|
||
public override void OnInspectorGUI()
|
||
{
|
||
serializedObject.Update();
|
||
|
||
// 绘制默认字段(排除ActionList)
|
||
DrawPropertiesExcluding(serializedObject, "ActionList");
|
||
|
||
EditorGUILayout.Space(10);
|
||
EditorGUILayout.LabelField("行动列表", EditorStyles.boldLabel);
|
||
|
||
// 新增Action按钮(搜索模式下禁用)
|
||
using (new EditorGUI.DisabledGroupScope(_isSearching))
|
||
{
|
||
if (GUILayout.Button("新增 Action", GUILayout.Height(30)))
|
||
{
|
||
_actionListProperty.arraySize++;
|
||
var newElement = _actionListProperty.GetArrayElementAtIndex(_actionListProperty.arraySize - 1);
|
||
newElement.isExpanded = true;
|
||
serializedObject.ApplyModifiedProperties();
|
||
_scrollPosition.y = float.MaxValue;
|
||
}
|
||
}
|
||
|
||
EditorGUILayout.Space(5);
|
||
|
||
// 搜索框
|
||
EditorGUILayout.BeginHorizontal();
|
||
EditorGUILayout.LabelField("搜索:", GUILayout.Width(100));
|
||
string newFilter = EditorGUILayout.TextField(_searchFilter);
|
||
if (GUILayout.Button("清除", GUILayout.Width(50)) && !string.IsNullOrEmpty(_searchFilter))
|
||
{
|
||
newFilter = "";
|
||
GUI.FocusControl(null);
|
||
}
|
||
EditorGUILayout.EndHorizontal();
|
||
|
||
// 检测搜索变化
|
||
if (newFilter != _searchFilter)
|
||
{
|
||
_searchFilter = newFilter;
|
||
_isSearching = !string.IsNullOrEmpty(_searchFilter);
|
||
UpdateFilteredIndices();
|
||
}
|
||
|
||
// 显示筛选状态
|
||
if (_isSearching)
|
||
{
|
||
EditorGUILayout.LabelField($"筛选结果: {_filteredIndices.Count} 个行动", EditorStyles.miniLabel);
|
||
}
|
||
|
||
EditorGUILayout.Space(5);
|
||
|
||
// 绘制ActionList
|
||
if (_actionListProperty != null)
|
||
{
|
||
_actionListProperty.isExpanded = EditorGUILayout.Foldout(
|
||
_actionListProperty.isExpanded,
|
||
$"Action List ({(_isSearching ? _filteredIndices.Count : _actionListProperty.arraySize)} items)",
|
||
true
|
||
);
|
||
|
||
if (_actionListProperty.isExpanded)
|
||
{
|
||
_scrollPosition = EditorGUILayout.BeginScrollView(_scrollPosition, GUILayout.Height(500));
|
||
|
||
if (_isSearching)
|
||
{
|
||
if (_filteredIndices.Count == 0)
|
||
{
|
||
EditorGUILayout.HelpBox("没有找到匹配的行动", MessageType.Info);
|
||
}
|
||
else
|
||
{
|
||
foreach (int index in _filteredIndices)
|
||
{
|
||
DrawActionItem(index, false);
|
||
}
|
||
}
|
||
}
|
||
else
|
||
{
|
||
DrawActionListReverse();
|
||
}
|
||
|
||
EditorGUILayout.EndScrollView();
|
||
}
|
||
}
|
||
|
||
serializedObject.ApplyModifiedProperties();
|
||
}
|
||
|
||
private void DrawActionItem(int index, bool showDelete)
|
||
{
|
||
SerializedProperty element = _actionListProperty.GetArrayElementAtIndex(index);
|
||
string displayName = GetActionDisplayName(element);
|
||
|
||
EditorGUILayout.BeginVertical(GUI.skin.box);
|
||
|
||
EditorGUILayout.BeginHorizontal();
|
||
|
||
string title = $"[{index}] {displayName}";
|
||
bool wasExpanded = element.isExpanded;
|
||
element.isExpanded = EditorGUILayout.Foldout(wasExpanded, title, true);
|
||
|
||
// 删除按钮(非搜索模式下显示)
|
||
if (showDelete)
|
||
{
|
||
if (GUILayout.Button("-", GUILayout.Width(25), GUILayout.Height(18)))
|
||
{
|
||
_actionListProperty.DeleteArrayElementAtIndex(index);
|
||
serializedObject.ApplyModifiedProperties();
|
||
GUIUtility.ExitGUI();
|
||
return;
|
||
}
|
||
}
|
||
|
||
EditorGUILayout.EndHorizontal();
|
||
|
||
// 用切换前的值决定是否绘制子属性,确保Layout和Repaint阶段控件数一致
|
||
if (wasExpanded)
|
||
{
|
||
EditorGUI.indentLevel++;
|
||
EditorGUILayout.PropertyField(element, GUIContent.none, true);
|
||
EditorGUI.indentLevel--;
|
||
}
|
||
|
||
EditorGUILayout.EndVertical();
|
||
}
|
||
|
||
private void DrawActionListReverse()
|
||
{
|
||
int count = _actionListProperty.arraySize;
|
||
for (int i = count - 1; i >= 0; i--)
|
||
{
|
||
DrawActionItem(i, true);
|
||
}
|
||
}
|
||
|
||
private void UpdateFilteredIndices()
|
||
{
|
||
_filteredIndices.Clear();
|
||
|
||
if (_actionListProperty == null || string.IsNullOrEmpty(_searchFilter))
|
||
return;
|
||
|
||
string filter = _searchFilter.ToLower();
|
||
int count = _actionListProperty.arraySize;
|
||
|
||
for (int i = 0; i < count; i++)
|
||
{
|
||
SerializedProperty element = _actionListProperty.GetArrayElementAtIndex(i);
|
||
if (MatchesFilter(element, filter))
|
||
_filteredIndices.Add(i);
|
||
}
|
||
}
|
||
|
||
private bool MatchesFilter(SerializedProperty element, string filter)
|
||
{
|
||
string displayName = GetActionDisplayName(element);
|
||
if (displayName.ToLower().Contains(filter))
|
||
return true;
|
||
|
||
// 搜索ActionId中的所有字段
|
||
var actionIdProp = element.FindPropertyRelative("ActionId");
|
||
if (actionIdProp != null)
|
||
{
|
||
SerializedProperty iterator = actionIdProp.Copy();
|
||
SerializedProperty end = iterator.GetEndProperty();
|
||
if (iterator.NextVisible(true))
|
||
{
|
||
do
|
||
{
|
||
if (SerializedProperty.EqualContents(iterator, end))
|
||
break;
|
||
|
||
// 搜索枚举类型的显示名称
|
||
if (iterator.propertyType == SerializedPropertyType.Enum)
|
||
{
|
||
if (iterator.enumValueIndex >= 0 && iterator.enumValueIndex < iterator.enumDisplayNames.Length)
|
||
{
|
||
string enumName = iterator.enumDisplayNames[iterator.enumValueIndex];
|
||
if (enumName.ToLower().Contains(filter))
|
||
return true;
|
||
}
|
||
}
|
||
// 搜索string/int/uint等基础类型
|
||
else if (iterator.propertyType == SerializedPropertyType.String &&
|
||
!string.IsNullOrEmpty(iterator.stringValue))
|
||
{
|
||
if (iterator.stringValue.ToLower().Contains(filter))
|
||
return true;
|
||
}
|
||
else if (iterator.propertyType == SerializedPropertyType.Integer)
|
||
{
|
||
if (iterator.intValue.ToString() == filter)
|
||
return true;
|
||
}
|
||
} while (iterator.NextVisible(false));
|
||
}
|
||
}
|
||
|
||
// 搜索ActionInfo中的字段
|
||
var actionNameProp = element.FindPropertyRelative("ActionName");
|
||
if (actionNameProp != null && !string.IsNullOrEmpty(actionNameProp.stringValue))
|
||
{
|
||
if (actionNameProp.stringValue.ToLower().Contains(filter))
|
||
return true;
|
||
}
|
||
|
||
var descProp = element.FindPropertyRelative("Desc");
|
||
if (descProp != null && !string.IsNullOrEmpty(descProp.stringValue))
|
||
{
|
||
if (descProp.stringValue.ToLower().Contains(filter))
|
||
return true;
|
||
}
|
||
|
||
return false;
|
||
}
|
||
|
||
private string GetActionDisplayName(SerializedProperty element)
|
||
{
|
||
var actionIdProp = element.FindPropertyRelative("ActionId");
|
||
if (actionIdProp == null)
|
||
return "Unknown";
|
||
|
||
var actionTypeProp = actionIdProp.FindPropertyRelative("ActionType");
|
||
string actionTypeName = actionTypeProp?.enumDisplayNames[actionTypeProp.enumValueIndex] ?? "Unknown";
|
||
|
||
// 查找首个非None/0的参数
|
||
string secondaryParam = "";
|
||
SerializedProperty iterator = actionIdProp.Copy();
|
||
SerializedProperty end = iterator.GetEndProperty();
|
||
|
||
if (iterator.NextVisible(true))
|
||
{
|
||
do
|
||
{
|
||
if (SerializedProperty.EqualContents(iterator, end))
|
||
break;
|
||
|
||
if (iterator.name == "ActionType")
|
||
continue;
|
||
|
||
// 检查枚举类型
|
||
if (iterator.propertyType == SerializedPropertyType.Enum)
|
||
{
|
||
if (iterator.enumValueIndex != 0 &&
|
||
iterator.enumValueIndex < iterator.enumDisplayNames.Length)
|
||
{
|
||
string paramName = iterator.enumDisplayNames[iterator.enumValueIndex];
|
||
if (paramName != "None")
|
||
{
|
||
secondaryParam = paramName;
|
||
break;
|
||
}
|
||
}
|
||
}
|
||
// 检查整数类型(非0)
|
||
else if (iterator.propertyType == SerializedPropertyType.Integer)
|
||
{
|
||
if (iterator.intValue != 0)
|
||
{
|
||
secondaryParam = iterator.name + ":" + iterator.intValue;
|
||
break;
|
||
}
|
||
}
|
||
// 检查uint/uint32类型
|
||
else if (iterator.propertyType == SerializedPropertyType.Integer && iterator.name == "UnitLevel")
|
||
{
|
||
// uint类型的判断,通过value不为0
|
||
if ((uint)iterator.intValue != 0)
|
||
{
|
||
secondaryParam = iterator.name + ":" + iterator.intValue;
|
||
break;
|
||
}
|
||
}
|
||
} while (iterator.NextVisible(false));
|
||
}
|
||
|
||
if (!string.IsNullOrEmpty(secondaryParam) && secondaryParam != "None")
|
||
return $"{actionTypeName} [{secondaryParam}]";
|
||
|
||
return actionTypeName;
|
||
}
|
||
} |