com.alicizax.unity.ui.exten.../Editor/UX/Controller/UXControllerStateRecorderEditor.cs
2026-03-11 15:23:44 +08:00

1122 lines
41 KiB
C#
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

#if UNITY_EDITOR
using System;
using System.Collections.Generic;
using System.Linq;
using System.Reflection;
using UnityEditor;
using UnityEditor.Experimental.GraphView;
using UnityEditor.Experimental.SceneManagement;
using UnityEngine;
using AlicizaX.UI;
using TMPro;
using UnityEditor.SceneManagement;
using UnityEngine.UI;
namespace AlicizaX.UI
{
[CustomEditor(typeof(UXControllerStateRecorder))]
public class UXControllerStateRecorderEditor : UnityEditor.Editor
{
private SerializedProperty _stateEntriesProp;
private SerializedProperty _controller;
private SerializedProperty _id;
private UXController _cacheController;
private Dictionary<int, bool> _stateFoldouts = new Dictionary<int, bool>();
private bool _showControllerInfo = true;
private bool _compactMode = false;
private GUIStyle _headerStyle;
private GUIStyle _toolbarStyle;
private GUIStyle _stateHeaderStyle;
private void OnEnable()
{
_controller = serializedObject.FindProperty("_controller");
_id = serializedObject.FindProperty("_id");
_stateEntriesProp = serializedObject.FindProperty("_stateEntries");
}
private void OnDestroy()
{
if (target == null)
{
UnRegisterSelf();
}
}
private void InitStyles()
{
if (_headerStyle == null)
{
_headerStyle = new GUIStyle(EditorStyles.boldLabel)
{
fontSize = 13,
fontStyle = FontStyle.Bold
};
}
if (_toolbarStyle == null)
{
_toolbarStyle = new GUIStyle(EditorStyles.toolbar)
{
fixedHeight = 25
};
}
if (_stateHeaderStyle == null)
{
_stateHeaderStyle = new GUIStyle(EditorStyles.foldout)
{
fontStyle = FontStyle.Bold
};
}
}
public override void OnInspectorGUI()
{
InitStyles();
serializedObject.Update();
var recorder = target as UXControllerStateRecorder;
UXController prefabCtrl = GetPrefabStageController();
recorder.GenerateID();
// Header
DrawHeader(recorder);
EditorGUILayout.Space(5);
// Controller Reference
DrawControllerSection(recorder, prefabCtrl);
EditorGUILayout.Space(5);
// Toolbar
DrawToolbar();
EditorGUILayout.Space(5);
// States List
DrawStatesList(recorder);
serializedObject.ApplyModifiedProperties();
}
private void DrawHeader(UXControllerStateRecorder recorder)
{
EditorGUILayout.BeginVertical(EditorStyles.helpBox);
EditorGUILayout.BeginHorizontal();
EditorGUILayout.LabelField("UX 状态记录器", _headerStyle, GUILayout.Width(150));
EditorGUILayout.LabelField($"ID: {_id.intValue}", EditorStyles.miniLabel);
GUILayout.FlexibleSpace();
if (GUILayout.Button("刷新", EditorStyles.miniButtonLeft, GUILayout.Width(60)))
{
recorder.Initialize();
EditorUtility.SetDirty(recorder);
}
Color oldColor = GUI.backgroundColor;
if (_compactMode) GUI.backgroundColor = Color.green;
if (GUILayout.Button("紧凑", EditorStyles.miniButtonRight, GUILayout.Width(60)))
{
_compactMode = !_compactMode;
_stateFoldouts.Clear();
}
GUI.backgroundColor = oldColor;
EditorGUILayout.EndHorizontal();
EditorGUILayout.EndVertical();
}
private void DrawControllerSection(UXControllerStateRecorder recorder, UXController prefabCtrl)
{
if (prefabCtrl != null && prefabCtrl != _controller.objectReferenceValue)
{
_controller.objectReferenceValue = prefabCtrl;
recorder.SetController(prefabCtrl);
}
if (prefabCtrl != null && !prefabCtrl.HasRecorder(recorder))
{
RegisterSelfToController(prefabCtrl);
}
if (_controller.objectReferenceValue != null)
{
_cacheController = _controller.objectReferenceValue as UXController;
}
EditorGUILayout.BeginVertical(EditorStyles.helpBox);
EditorGUILayout.BeginHorizontal();
_showControllerInfo = EditorGUILayout.Foldout(_showControllerInfo, "控制器引用", true);
EditorGUILayout.EndHorizontal();
// Controller dropdown
var availableControllers = GetAvailableControllers(recorder);
EditorGUILayout.BeginHorizontal();
EditorGUILayout.LabelField("控制器", GUILayout.Width(70));
if (availableControllers.Count > 0)
{
int currentIndex = -1;
var currentController = _controller.objectReferenceValue as UXController;
if (currentController != null)
{
currentIndex = availableControllers.IndexOf(currentController);
}
string[] options = new string[availableControllers.Count];
for (int i = 0; i < availableControllers.Count; i++)
{
var ctrl = availableControllers[i];
string path = GetGameObjectPath(ctrl.gameObject, recorder.gameObject);
options[i] = $"{ctrl.gameObject.name} ({path})";
}
EditorGUI.BeginChangeCheck();
int newIndex = EditorGUILayout.Popup(currentIndex, options);
if (EditorGUI.EndChangeCheck() && newIndex >= 0 && newIndex < availableControllers.Count)
{
_controller.objectReferenceValue = availableControllers[newIndex];
recorder.SetController(availableControllers[newIndex]);
if (!availableControllers[newIndex].HasRecorder(recorder))
{
RegisterSelfToController(availableControllers[newIndex]);
}
}
if (currentController != null && GUILayout.Button("选择", GUILayout.Width(60)))
{
Selection.activeGameObject = currentController.gameObject;
}
}
else
{
EditorGUILayout.LabelField("(在父对象中未找到控制器)", EditorStyles.miniLabel);
}
EditorGUILayout.EndHorizontal();
// Controller info
if (_showControllerInfo && _controller.objectReferenceValue != null)
{
UXController ctl = _controller.objectReferenceValue as UXController;
EditorGUI.indentLevel++;
EditorGUILayout.LabelField($"控制器数量: {ctl.ControllerCount}", EditorStyles.miniLabel);
for (int i = 0; i < ctl.ControllerCount; i++)
{
var handle = ctl.GetControllerAt(i);
if (handle != null)
{
string desc = string.IsNullOrEmpty(handle.Description) ? "" : $" - {handle.Description}";
EditorGUILayout.LabelField($" [{i}] {handle.Name} (长度: {handle.Length}){desc}", EditorStyles.miniLabel);
}
}
EditorGUI.indentLevel--;
}
if (_controller.objectReferenceValue == null)
{
EditorGUILayout.HelpBox("未分配控制器。请在父对象上添加 UXController。", MessageType.Warning);
}
EditorGUILayout.EndVertical();
}
private void DrawToolbar()
{
EditorGUILayout.BeginHorizontal(_toolbarStyle);
if (GUILayout.Button("+ 添加状态", EditorStyles.toolbarButton, GUILayout.Width(100)))
{
ShowAddStateMenu();
}
EditorGUI.BeginDisabledGroup(_stateEntriesProp.arraySize == 0);
if (GUILayout.Button("清空全部", EditorStyles.toolbarButton, GUILayout.Width(70)))
{
if (EditorUtility.DisplayDialog("清空所有状态",
"移除所有状态?", "清空", "取消"))
{
ClearAllStates();
}
}
EditorGUI.EndDisabledGroup();
if (GUILayout.Button("全部展开", EditorStyles.toolbarButton, GUILayout.Width(70)))
{
for (int i = 0; i < _stateEntriesProp.arraySize; i++)
{
_stateFoldouts[i] = true;
}
}
if (GUILayout.Button("全部折叠", EditorStyles.toolbarButton, GUILayout.Width(75)))
{
_stateFoldouts.Clear();
}
GUILayout.FlexibleSpace();
EditorGUILayout.LabelField($"状态数: {_stateEntriesProp.arraySize}", EditorStyles.miniLabel, GUILayout.Width(70));
EditorGUILayout.EndHorizontal();
}
private void DrawStatesList(UXControllerStateRecorder recorder)
{
if (_stateEntriesProp.arraySize == 0)
{
EditorGUILayout.HelpBox("未定义状态。点击 '+ 添加状态' 开始。", MessageType.Info);
return;
}
for (int i = 0; i < _stateEntriesProp.arraySize; i++)
{
DrawStateEntry(recorder, i);
EditorGUILayout.Space(3);
}
}
private void DrawStateEntry(UXControllerStateRecorder recorder, int index)
{
if (index < 0 || index >= _stateEntriesProp.arraySize)
{
return;
}
var entryProp = _stateEntriesProp.GetArrayElementAtIndex(index);
if (entryProp == null) return;
var stateProp = entryProp.FindPropertyRelative("State");
var controllerNameProp = entryProp.FindPropertyRelative("ControllerName");
var controllerIndexProp = entryProp.FindPropertyRelative("ControllerIndex");
// Validate all properties
if (controllerNameProp == null || controllerIndexProp == null)
{
return;
}
if (stateProp == null || stateProp.managedReferenceValue == null)
{
DrawInvalidStateEntry(index);
return;
}
var stateInstance = stateProp.managedReferenceValue as ControllerStateBase;
var stateType = stateProp.managedReferenceValue.GetType();
var attr = stateType.GetCustomAttribute<ControlerStateNameAttribute>();
var stateName = attr != null ? attr.StateName : stateType.Name;
bool isValid = ValidateState(recorder, stateInstance);
bool isFolded = _stateFoldouts.ContainsKey(index) && _stateFoldouts[index];
// State box with validation color
Color boxColor = GUI.backgroundColor;
if (!isValid) GUI.backgroundColor = new Color(1f, 0.5f, 0.5f);
EditorGUILayout.BeginVertical(EditorStyles.helpBox);
GUI.backgroundColor = boxColor;
// Header
DrawStateHeader(index, stateName, ref isFolded);
_stateFoldouts[index] = isFolded;
// Compact mode: show binding info
if (_compactMode && !isFolded)
{
EditorGUI.indentLevel++;
try
{
string bindingText = string.IsNullOrEmpty(controllerNameProp.stringValue)
? "未绑定"
: $"绑定到: {controllerNameProp.stringValue}[{controllerIndexProp.intValue}]";
EditorGUILayout.LabelField(bindingText, EditorStyles.miniLabel);
// 在紧凑模式下也允许快速修改索引
if (!string.IsNullOrEmpty(controllerNameProp.stringValue))
{
EditorGUILayout.BeginHorizontal();
EditorGUILayout.LabelField("索引", GUILayout.Width(50));
int ctrlLen = GetSelectedControllerLength(recorder, controllerNameProp.stringValue);
if (ctrlLen <= 0) ctrlLen = 1;
string[] idxOptions = new string[ctrlLen];
for (int j = 0; j < ctrlLen; j++) idxOptions[j] = j.ToString();
EditorGUI.BeginChangeCheck();
int newIdx = EditorGUILayout.Popup(controllerIndexProp.intValue, idxOptions, GUILayout.Width(60));
if (EditorGUI.EndChangeCheck())
{
controllerIndexProp.intValue = newIdx;
}
EditorGUILayout.EndHorizontal();
}
}
catch (ObjectDisposedException)
{
EditorGUILayout.LabelField("绑定: (刷新中...)", EditorStyles.miniLabel);
}
EditorGUI.indentLevel--;
}
// Expanded content
if (isFolded && !_compactMode)
{
EditorGUI.indentLevel++;
// Binding
DrawStateBinding(recorder, controllerNameProp, controllerIndexProp);
EditorGUILayout.Space(3);
// Properties
if (stateInstance != null)
{
EditorGUILayout.BeginHorizontal();
EditorGUILayout.LabelField("属性", EditorStyles.boldLabel);
// Set to Current Value button
if (GUILayout.Button("设为当前值", GUILayout.Width(110)))
{
SetStateToCurrentValue(recorder, stateInstance, stateProp);
}
EditorGUILayout.EndHorizontal();
SerializedClassDrawer.DrawSerializableProperty(stateProp);
// Description
EditorGUILayout.Space(2);
var description = stateInstance.GetDescription();
if (!string.IsNullOrEmpty(description))
{
EditorGUILayout.LabelField($" {description}", EditorStyles.miniLabel);
}
}
// Validation warning
if (!isValid)
{
EditorGUILayout.HelpBox("状态验证失败。请检查所需组件。", MessageType.Error);
}
EditorGUI.indentLevel--;
}
EditorGUILayout.EndVertical();
}
private void DrawStateHeader(int index, string stateName, ref bool isFolded)
{
EditorGUILayout.BeginHorizontal();
// Foldout with name
isFolded = EditorGUILayout.Foldout(isFolded, $"[{index}] {stateName}", true, _stateHeaderStyle);
GUILayout.FlexibleSpace();
// Buttons
if (GUILayout.Button("▲", EditorStyles.miniButtonLeft, GUILayout.Width(25)) && index > 0)
{
MoveStateUp(index);
}
if (GUILayout.Button("▼", EditorStyles.miniButtonMid, GUILayout.Width(25)) && index < _stateEntriesProp.arraySize - 1)
{
MoveStateDown(index);
}
if (GUILayout.Button("⎘", EditorStyles.miniButtonMid, GUILayout.Width(25)))
{
DuplicateState(index);
}
if (GUILayout.Button("×", EditorStyles.miniButtonRight, GUILayout.Width(25)))
{
DeleteStateAtIndex(index);
}
EditorGUILayout.EndHorizontal();
}
private void DrawStateBinding(UXControllerStateRecorder recorder,
SerializedProperty controllerNameProp, SerializedProperty controllerIndexProp)
{
// Safety check: ensure properties are valid
if (controllerNameProp == null || controllerIndexProp == null)
{
EditorGUILayout.HelpBox("绑定属性无效。请尝试刷新。", MessageType.Warning);
return;
}
EditorGUILayout.BeginVertical(EditorStyles.helpBox);
EditorGUILayout.LabelField("绑定", EditorStyles.boldLabel);
var controllerNames = GetControllerNamesForRecorder(recorder);
// Controller dropdown
EditorGUILayout.BeginHorizontal();
EditorGUILayout.LabelField("控制器", GUILayout.Width(70));
if (controllerNames.Count > 0)
{
string currentName = "";
try
{
currentName = controllerNameProp.stringValue;
}
catch (ObjectDisposedException)
{
// Property has been disposed, exit gracefully
EditorGUILayout.EndHorizontal();
EditorGUILayout.EndVertical();
return;
}
int selIdx = controllerNames.IndexOf(currentName);
if (selIdx < 0) selIdx = 0;
EditorGUI.BeginChangeCheck();
int newSel = EditorGUILayout.Popup(selIdx, controllerNames.ToArray());
if (EditorGUI.EndChangeCheck() && newSel >= 0 && newSel < controllerNames.Count)
{
controllerNameProp.stringValue = controllerNames[newSel];
}
}
else
{
EditorGUILayout.LabelField("(无可用控制器)", EditorStyles.miniLabel);
}
EditorGUILayout.EndHorizontal();
// Index dropdown
if (controllerNames.Count > 0 && !string.IsNullOrEmpty(controllerNameProp.stringValue))
{
EditorGUILayout.BeginHorizontal();
EditorGUILayout.LabelField("索引", GUILayout.Width(70));
int ctrlLen = GetSelectedControllerLength(recorder, controllerNameProp.stringValue);
if (ctrlLen <= 0) ctrlLen = 1;
controllerIndexProp.intValue = Mathf.Clamp(controllerIndexProp.intValue, 0, ctrlLen - 1);
string[] idxOptions = new string[ctrlLen];
for (int j = 0; j < ctrlLen; j++) idxOptions[j] = j.ToString();
int curIdx = controllerIndexProp.intValue;
EditorGUI.BeginChangeCheck();
int newIdx = EditorGUILayout.Popup(curIdx, idxOptions, GUILayout.Width(100));
if (EditorGUI.EndChangeCheck())
{
controllerIndexProp.intValue = newIdx;
}
// Preview button
if (_controller.objectReferenceValue != null && !Application.isPlaying)
{
if (GUILayout.Button("预览", GUILayout.Width(60)))
{
PreviewState(controllerNameProp.stringValue, controllerIndexProp.intValue);
}
}
EditorGUILayout.EndHorizontal();
}
EditorGUILayout.EndVertical();
}
private void DrawInvalidStateEntry(int index)
{
Color oldColor = GUI.backgroundColor;
GUI.backgroundColor = new Color(1f, 0.5f, 0.5f);
EditorGUILayout.BeginVertical(EditorStyles.helpBox);
GUI.backgroundColor = oldColor;
EditorGUILayout.BeginHorizontal();
EditorGUILayout.LabelField($"[{index}] 无效状态", EditorStyles.boldLabel);
if (GUILayout.Button("移除", GUILayout.Width(60)))
{
DeleteStateAtIndex(index);
}
EditorGUILayout.EndHorizontal();
EditorGUILayout.HelpBox("状态数据已损坏。请移除并重新添加。", MessageType.Error);
EditorGUILayout.EndVertical();
}
private void DeleteStateAtIndex(int index)
{
if (index < 0 || index >= _stateEntriesProp.arraySize)
{
Debug.LogWarning($"Invalid state index: {index}");
return;
}
Undo.RecordObject(serializedObject.targetObject, "删除状态");
// Get the entry before deleting
var entryProp = _stateEntriesProp.GetArrayElementAtIndex(index);
var stateProp = entryProp?.FindPropertyRelative("State");
// If the state reference is not null, we need to delete twice
// First deletion clears the reference, second removes the array element
if (stateProp != null && stateProp.managedReferenceValue != null)
{
stateProp.managedReferenceValue = null;
}
// Now delete the array element
_stateEntriesProp.DeleteArrayElementAtIndex(index);
// Clean up foldout dictionary
_stateFoldouts.Remove(index);
// Rebuild foldout dictionary with corrected indices
var newFoldouts = new Dictionary<int, bool>();
foreach (var kvp in _stateFoldouts)
{
if (kvp.Key < index)
{
newFoldouts[kvp.Key] = kvp.Value;
}
else if (kvp.Key > index)
{
newFoldouts[kvp.Key - 1] = kvp.Value;
}
}
_stateFoldouts = newFoldouts;
serializedObject.ApplyModifiedProperties();
EditorUtility.SetDirty(serializedObject.targetObject);
// Exit GUI to prevent using disposed SerializedProperty
GUIUtility.ExitGUI();
}
private void ClearAllStates()
{
Undo.RecordObject(serializedObject.targetObject, "清空所有状态");
// Clear all managed references first
for (int i = _stateEntriesProp.arraySize - 1; i >= 0; i--)
{
var entryProp = _stateEntriesProp.GetArrayElementAtIndex(i);
var stateProp = entryProp?.FindPropertyRelative("State");
if (stateProp != null && stateProp.managedReferenceValue != null)
{
stateProp.managedReferenceValue = null;
}
}
// Clear the array
_stateEntriesProp.ClearArray();
_stateFoldouts.Clear();
serializedObject.ApplyModifiedProperties();
EditorUtility.SetDirty(serializedObject.targetObject);
// Exit GUI to prevent using disposed SerializedProperty
GUIUtility.ExitGUI();
}
private void PreviewState(string controllerName, int index)
{
if (_controller.objectReferenceValue != null)
{
var ctl = _controller.objectReferenceValue as UXController;
ctl.SetControllerIndex(controllerName, index);
EditorUtility.SetDirty(ctl);
SceneView.RepaintAll();
}
}
private void DuplicateState(int index)
{
if (index < 0 || index >= _stateEntriesProp.arraySize)
{
Debug.LogWarning($"Invalid state index: {index}");
return;
}
Undo.RecordObject(serializedObject.targetObject, "复制状态");
_stateEntriesProp.InsertArrayElementAtIndex(index);
// Update foldout dictionary
var newFoldouts = new Dictionary<int, bool>();
foreach (var kvp in _stateFoldouts)
{
if (kvp.Key <= index)
{
newFoldouts[kvp.Key] = kvp.Value;
}
else
{
newFoldouts[kvp.Key + 1] = kvp.Value;
}
}
_stateFoldouts = newFoldouts;
_stateFoldouts[index + 1] = true;
serializedObject.ApplyModifiedProperties();
EditorUtility.SetDirty(serializedObject.targetObject);
// Exit GUI to prevent using disposed SerializedProperty
GUIUtility.ExitGUI();
}
private void MoveStateUp(int index)
{
if (index <= 0 || index >= _stateEntriesProp.arraySize)
{
return;
}
Undo.RecordObject(serializedObject.targetObject, "上移状态");
_stateEntriesProp.MoveArrayElement(index, index - 1);
// Update foldout dictionary
if (_stateFoldouts.ContainsKey(index))
{
bool wasFolded = _stateFoldouts[index];
_stateFoldouts.Remove(index);
_stateFoldouts[index - 1] = wasFolded;
}
serializedObject.ApplyModifiedProperties();
EditorUtility.SetDirty(serializedObject.targetObject);
// Exit GUI to prevent using disposed SerializedProperty
GUIUtility.ExitGUI();
}
private void MoveStateDown(int index)
{
if (index < 0 || index >= _stateEntriesProp.arraySize - 1)
{
return;
}
Undo.RecordObject(serializedObject.targetObject, "下移状态");
_stateEntriesProp.MoveArrayElement(index, index + 1);
// Update foldout dictionary
if (_stateFoldouts.ContainsKey(index))
{
bool wasFolded = _stateFoldouts[index];
_stateFoldouts.Remove(index);
_stateFoldouts[index + 1] = wasFolded;
}
serializedObject.ApplyModifiedProperties();
EditorUtility.SetDirty(serializedObject.targetObject);
// Exit GUI to prevent using disposed SerializedProperty
GUIUtility.ExitGUI();
}
private bool ValidateState(UXControllerStateRecorder recorder, ControllerStateBase stateInstance)
{
if (stateInstance == null) return false;
try
{
return stateInstance.Valid(recorder);
}
catch
{
return false;
}
}
private void ShowAddStateMenu()
{
var provider = ScriptableObject.CreateInstance<StateTypeSearchProvider>();
provider.Init(OnTypeSelected);
SearchWindow.Open(new SearchWindowContext(GUIUtility.GUIToScreenPoint(Event.current.mousePosition)), provider);
}
private void OnTypeSelected(Type type)
{
if (type == null) return;
Undo.RecordObject(serializedObject.targetObject, "添加控制器状态");
serializedObject.Update();
int idx = _stateEntriesProp.arraySize;
_stateEntriesProp.InsertArrayElementAtIndex(idx);
var entryProp = _stateEntriesProp.GetArrayElementAtIndex(idx);
var stateProp = entryProp.FindPropertyRelative("State");
var controllerNameProp = entryProp.FindPropertyRelative("ControllerName");
var controllerIndexProp = entryProp.FindPropertyRelative("ControllerIndex");
ControllerStateBase instance = null;
try
{
instance = (ControllerStateBase)Activator.CreateInstance(type);
}
catch (Exception ex)
{
Debug.LogException(ex);
return;
}
var recorder = serializedObject.targetObject as UXControllerStateRecorder;
if (instance != null)
{
instance.Init(recorder);
}
if (stateProp != null)
{
stateProp.managedReferenceValue = instance;
stateProp.isExpanded = true;
}
var controllerNames = GetControllerNamesForRecorder(recorder);
if (controllerNames.Count > 0)
{
controllerNameProp.stringValue = controllerNames[0];
controllerIndexProp.intValue = 0;
}
else
{
controllerNameProp.stringValue = "";
controllerIndexProp.intValue = 0;
}
serializedObject.ApplyModifiedProperties();
EditorUtility.SetDirty(serializedObject.targetObject);
_stateFoldouts[idx] = true;
}
private void UnRegisterSelf()
{
UXController controller = (_cacheController != null) ? _cacheController : GetPrefabStageController();
if (controller == null) return;
var so = new SerializedObject(controller);
var recorders = so.FindProperty("_recorders");
for (int i = recorders.arraySize - 1; i >= 0; i--)
{
var el = recorders.GetArrayElementAtIndex(i);
if (el.objectReferenceValue == null)
{
recorders.DeleteArrayElementAtIndex(i);
}
}
so.ApplyModifiedProperties();
EditorUtility.SetDirty(controller);
}
private void RegisterSelfToController(UXController controller)
{
var so = new SerializedObject(controller);
var recorders = so.FindProperty("_recorders");
int newIndex = recorders.arraySize;
recorders.InsertArrayElementAtIndex(newIndex);
var el = recorders.GetArrayElementAtIndex(newIndex);
el.objectReferenceValue = target;
so.ApplyModifiedProperties();
EditorUtility.SetDirty(controller);
}
private UXController GetPrefabStageController()
{
var stage = PrefabStageUtility.GetCurrentPrefabStage();
if (stage == null) return null;
var root = stage.prefabContentsRoot;
if (root == null) return null;
return root.GetComponentInChildren<UXController>();
}
private List<string> GetControllerNamesForRecorder(UXControllerStateRecorder recorder)
{
var names = new List<string>();
var prefabCtrl = GetPrefabStageController();
if (prefabCtrl != null)
{
foreach (var cd in prefabCtrl.Controllers) names.Add(cd.Name);
return names;
}
var assigned = recorder.Controller;
if (assigned != null)
{
foreach (var cd in assigned.Controllers) names.Add(cd.Name);
return names;
}
var inScene = recorder.gameObject.GetComponentInParent<UXController>();
if (inScene != null)
{
foreach (var cd in inScene.Controllers) names.Add(cd.Name);
return names;
}
return names;
}
private int GetSelectedControllerLength(UXControllerStateRecorder recorder, string controllerName)
{
var prefabCtrl = GetPrefabStageController();
if (prefabCtrl != null)
{
var ch = prefabCtrl.Controllers.ToList().Find(x => x.Name == controllerName);
if (ch != null) return ch.Length;
}
var assigned = recorder.Controller;
if (assigned != null)
{
var ch = assigned.Controllers.ToList().Find(x => x.Name == controllerName);
if (ch != null) return ch.Length;
}
var inScene = recorder.gameObject.GetComponentInParent<UXController>();
if (inScene != null)
{
var ch = inScene.Controllers.ToList().Find(x => x.Name == controllerName);
if (ch != null) return ch.Length;
}
return 1;
}
private List<UXController> GetAvailableControllers(UXControllerStateRecorder recorder)
{
var controllers = new List<UXController>();
// Search upwards through all parent transforms
Transform current = recorder.transform.parent;
while (current != null)
{
var ctrl = current.GetComponent<UXController>();
if (ctrl != null && !controllers.Contains(ctrl))
{
controllers.Add(ctrl);
}
current = current.parent;
}
return controllers;
}
private string GetGameObjectPath(GameObject target, GameObject from)
{
if (target == null || from == null) return "";
List<string> path = new List<string>();
Transform current = from.transform.parent;
while (current != null)
{
if (current.gameObject == target)
{
return path.Count == 0 ? "Parent" : string.Join("/", path);
}
path.Insert(0, "..");
current = current.parent;
}
return "Unknown";
}
private void SetStateToCurrentValue(UXControllerStateRecorder recorder, ControllerStateBase state, SerializedProperty stateProp)
{
if (recorder == null || state == null) return;
Undo.RecordObject(recorder, "设置状态为当前值");
// Use reflection to set current values based on state type
var stateType = state.GetType();
if (state is TransformPositionState)
{
if (recorder.TryGetComponent<RectTransform>(out var rect))
{
var posField = stateType.GetField("_position", BindingFlags.NonPublic | BindingFlags.Instance);
if (posField != null)
{
posField.SetValue(state, rect.anchoredPosition);
serializedObject.ApplyModifiedProperties();
serializedObject.Update();
EditorUtility.SetDirty(recorder);
}
}
}
else if (state is TransformScaleState)
{
if (recorder.transform != null)
{
var scaleField = stateType.GetField("_scale", BindingFlags.NonPublic | BindingFlags.Instance);
if (scaleField != null)
{
scaleField.SetValue(state, recorder.transform.localScale);
serializedObject.ApplyModifiedProperties();
serializedObject.Update();
EditorUtility.SetDirty(recorder);
}
}
}
else if (state is TransformRotationState)
{
if (recorder.transform != null)
{
var rotField = stateType.GetField("_rotation", BindingFlags.NonPublic | BindingFlags.Instance);
if (rotField != null)
{
rotField.SetValue(state, recorder.transform.localEulerAngles);
serializedObject.ApplyModifiedProperties();
serializedObject.Update();
EditorUtility.SetDirty(recorder);
}
}
}
else if (state is GraphicColorState)
{
if (recorder.TryGetComponent<Graphic>(out var graphic))
{
var colorField = stateType.GetField("_color", BindingFlags.NonPublic | BindingFlags.Instance);
if (colorField != null)
{
colorField.SetValue(state, graphic.color);
serializedObject.ApplyModifiedProperties();
serializedObject.Update();
EditorUtility.SetDirty(recorder);
}
}
}
else if (state is GraphicMaterialState)
{
if (recorder.TryGetComponent<Graphic>(out var graphic))
{
var matField = stateType.GetField("_material", BindingFlags.NonPublic | BindingFlags.Instance);
if (matField != null)
{
matField.SetValue(state, graphic.material);
serializedObject.ApplyModifiedProperties();
serializedObject.Update();
EditorUtility.SetDirty(recorder);
}
}
}
else if (state is ImageSpriteState)
{
if (recorder.TryGetComponent<Image>(out var image))
{
var spriteField = stateType.GetField("_sprite", BindingFlags.NonPublic | BindingFlags.Instance);
if (spriteField != null)
{
spriteField.SetValue(state, image.sprite);
serializedObject.ApplyModifiedProperties();
serializedObject.Update();
EditorUtility.SetDirty(recorder);
}
}
}
else if (state is TextContentState)
{
string currentText = "";
if (recorder.TryGetComponent<Text>(out var text))
{
currentText = text.text;
}
else if (recorder.TryGetComponent<TextMeshProUGUI>(out var tmp))
{
currentText = tmp.text;
}
var textField = stateType.GetField("_text", BindingFlags.NonPublic | BindingFlags.Instance);
if (textField != null)
{
textField.SetValue(state, currentText);
serializedObject.ApplyModifiedProperties();
serializedObject.Update();
EditorUtility.SetDirty(recorder);
}
}
else if (state is TextColorState)
{
Color currentColor = Color.white;
if (recorder.TryGetComponent<Text>(out var text))
{
currentColor = text.color;
}
else if (recorder.TryGetComponent<TextMeshProUGUI>(out var tmp))
{
currentColor = tmp.color;
}
var colorField = stateType.GetField("_color", BindingFlags.NonPublic | BindingFlags.Instance);
if (colorField != null)
{
colorField.SetValue(state, currentColor);
serializedObject.ApplyModifiedProperties();
serializedObject.Update();
EditorUtility.SetDirty(recorder);
}
}
else if (state is CanvasGroupAlphaState)
{
if (recorder.TryGetComponent<CanvasGroup>(out var canvasGroup))
{
var alphaField = stateType.GetField("_alpha", BindingFlags.NonPublic | BindingFlags.Instance);
if (alphaField != null)
{
alphaField.SetValue(state, canvasGroup.alpha);
serializedObject.ApplyModifiedProperties();
serializedObject.Update();
EditorUtility.SetDirty(recorder);
}
}
}
else if (state is CanvasGroupInteractableState)
{
if (recorder.TryGetComponent<CanvasGroup>(out var canvasGroup))
{
var interField = stateType.GetField("_interactable", BindingFlags.NonPublic | BindingFlags.Instance);
var blockField = stateType.GetField("_blocksRaycasts", BindingFlags.NonPublic | BindingFlags.Instance);
if (interField != null && blockField != null)
{
interField.SetValue(state, canvasGroup.interactable);
blockField.SetValue(state, canvasGroup.blocksRaycasts);
serializedObject.ApplyModifiedProperties();
serializedObject.Update();
EditorUtility.SetDirty(recorder);
}
}
}
else if (state is GameObjectActiveState)
{
var activeField = stateType.GetField("_active", BindingFlags.NonPublic | BindingFlags.Instance);
if (activeField != null)
{
activeField.SetValue(state, recorder.gameObject.activeSelf);
serializedObject.ApplyModifiedProperties();
serializedObject.Update();
EditorUtility.SetDirty(recorder);
}
}
Debug.Log($"Set {stateType.Name} to current value");
}
}
}
#endif