com.alicizax.unity.ui.exten.../Editor/UX/Controller/UXControllerStateRecorderEditor.cs
2026-03-11 15:04:33 +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 State Recorder", _headerStyle, GUILayout.Width(150));
EditorGUILayout.LabelField($"ID: {_id.intValue}", EditorStyles.miniLabel);
GUILayout.FlexibleSpace();
if (GUILayout.Button("Refresh", EditorStyles.miniButtonLeft, GUILayout.Width(60)))
{
recorder.Initialize();
EditorUtility.SetDirty(recorder);
}
Color oldColor = GUI.backgroundColor;
if (_compactMode) GUI.backgroundColor = Color.green;
if (GUILayout.Button("Compact", 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, "Controller Reference", true);
EditorGUILayout.EndHorizontal();
// Controller dropdown
var availableControllers = GetAvailableControllers(recorder);
EditorGUILayout.BeginHorizontal();
EditorGUILayout.LabelField("Controller", 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("Select", GUILayout.Width(60)))
{
Selection.activeGameObject = currentController.gameObject;
}
}
else
{
EditorGUILayout.LabelField("(No controllers found in parents)", EditorStyles.miniLabel);
}
EditorGUILayout.EndHorizontal();
// Controller info
if (_showControllerInfo && _controller.objectReferenceValue != null)
{
UXController ctl = _controller.objectReferenceValue as UXController;
EditorGUI.indentLevel++;
EditorGUILayout.LabelField($"Controllers: {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} (Length: {handle.Length}){desc}", EditorStyles.miniLabel);
}
}
EditorGUI.indentLevel--;
}
if (_controller.objectReferenceValue == null)
{
EditorGUILayout.HelpBox("No controller assigned. Add UXController to parent GameObject.", MessageType.Warning);
}
EditorGUILayout.EndVertical();
}
private void DrawToolbar()
{
EditorGUILayout.BeginHorizontal(_toolbarStyle);
if (GUILayout.Button("+ Add State", EditorStyles.toolbarButton, GUILayout.Width(100)))
{
ShowAddStateMenu();
}
EditorGUI.BeginDisabledGroup(_stateEntriesProp.arraySize == 0);
if (GUILayout.Button("Clear All", EditorStyles.toolbarButton, GUILayout.Width(70)))
{
if (EditorUtility.DisplayDialog("Clear All States",
"Remove all states?", "Clear", "Cancel"))
{
ClearAllStates();
}
}
EditorGUI.EndDisabledGroup();
if (GUILayout.Button("Expand All", EditorStyles.toolbarButton, GUILayout.Width(70)))
{
for (int i = 0; i < _stateEntriesProp.arraySize; i++)
{
_stateFoldouts[i] = true;
}
}
if (GUILayout.Button("Collapse All", EditorStyles.toolbarButton, GUILayout.Width(75)))
{
_stateFoldouts.Clear();
}
GUILayout.FlexibleSpace();
EditorGUILayout.LabelField($"States: {_stateEntriesProp.arraySize}", EditorStyles.miniLabel, GUILayout.Width(70));
EditorGUILayout.EndHorizontal();
}
private void DrawStatesList(UXControllerStateRecorder recorder)
{
if (_stateEntriesProp.arraySize == 0)
{
EditorGUILayout.HelpBox("No states defined. Click '+ Add State' to begin.", 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)
? "Not Bound"
: $"Bound to: {controllerNameProp.stringValue}[{controllerIndexProp.intValue}]";
EditorGUILayout.LabelField(bindingText, EditorStyles.miniLabel);
// 在紧凑模式下也允许快速修改索引
if (!string.IsNullOrEmpty(controllerNameProp.stringValue))
{
EditorGUILayout.BeginHorizontal();
EditorGUILayout.LabelField("Index", 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("Binding: (refreshing...)", 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("Properties", EditorStyles.boldLabel);
// Set to Current Value button
if (GUILayout.Button("Set to Current", 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("State validation failed. Check required components.", 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("Binding properties are invalid. Try refreshing.", MessageType.Warning);
return;
}
EditorGUILayout.BeginVertical(EditorStyles.helpBox);
EditorGUILayout.LabelField("Binding", EditorStyles.boldLabel);
var controllerNames = GetControllerNamesForRecorder(recorder);
// Controller dropdown
EditorGUILayout.BeginHorizontal();
EditorGUILayout.LabelField("Controller", 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("(No controllers available)", EditorStyles.miniLabel);
}
EditorGUILayout.EndHorizontal();
// Index dropdown
if (controllerNames.Count > 0 && !string.IsNullOrEmpty(controllerNameProp.stringValue))
{
EditorGUILayout.BeginHorizontal();
EditorGUILayout.LabelField("Index", 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("Preview", 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}] Invalid State", EditorStyles.boldLabel);
if (GUILayout.Button("Remove", GUILayout.Width(60)))
{
DeleteStateAtIndex(index);
}
EditorGUILayout.EndHorizontal();
EditorGUILayout.HelpBox("State data is corrupted. Remove and re-add.", 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, "Delete State");
// 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 States");
// 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, "Duplicate State");
_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, "Move State Up");
_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, "Move State Down");
_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, "Add Controller State");
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, "Set State to Current Value");
// 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