com.alicizax.unity.ui.exten.../Editor/UX/Controller/UXControllerStateRecorderEditor.cs

359 lines
12 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 AlicizaX.UI.Runtime;
using UnityEditor.SceneManagement;
using Random = UnityEngine.Random;
[CustomEditor(typeof(UXControllerStateRecorder))]
public class UXControllerStateRecorderEditor : Editor
{
private SerializedProperty _stateEntriesProp;
private SerializedProperty _controller;
private SerializedProperty _id;
private GUIContent _removeIcon;
private UXController _cacheController;
private void OnEnable()
{
_controller = serializedObject.FindProperty("_controller");
_id = serializedObject.FindProperty("_id");
_stateEntriesProp = serializedObject.FindProperty("_stateEntries");
_removeIcon = EditorGUIUtility.IconContent("d_winbtn_win_close");
}
private void OnDestroy()
{
if (target == null)
{
UnRegisterSelf();
}
}
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);
break;
}
}
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);
}
public override void OnInspectorGUI()
{
serializedObject.Update();
EditorGUILayout.Space();
var recorder = target as UXControllerStateRecorder;
UXController prefabCtrl = GetPrefabStageController();
if (_id.intValue <= 0)
{
_id.intValue = Random.Range(10000000, 99999999);
}
EditorGUILayout.LabelField($"ID:{_id.intValue}", EditorStyles.helpBox);
EditorGUILayout.BeginHorizontal();
if (prefabCtrl != null && prefabCtrl != _controller.objectReferenceValue)
{
_controller.objectReferenceValue = prefabCtrl;
}
if (!prefabCtrl.HasRecorder(recorder))
{
RegisterSelfToController(prefabCtrl);
}
if (_controller.objectReferenceValue != null)
{
_cacheController = _controller.objectReferenceValue as UXController;
}
if (_controller.objectReferenceValue != null)
{
UXController ctl = _controller.objectReferenceValue as UXController;
EditorGUILayout.LabelField($"{ctl.name} ({ctl.Controllers.Count} controllers)", EditorStyles.helpBox);
}
else
EditorGUILayout.LabelField("(None)", EditorStyles.helpBox);
EditorGUILayout.EndHorizontal();
EditorGUILayout.Space();
Rect addRect = EditorGUILayout.GetControlRect(false, GUILayout.Height(22));
if (GUI.Button(addRect, "Add State"))
{
var provider = ScriptableObject.CreateInstance<StateTypeSearchProvider>();
provider.Init(OnTypeSelected);
var screenPos = GUIUtility.GUIToScreenPoint(new Vector2(addRect.x, addRect.yMax));
SearchWindow.Open(new SearchWindowContext(screenPos), provider);
}
EditorGUILayout.Space();
for (int i = 0; i < _stateEntriesProp.arraySize; i++)
{
var entryProp = _stateEntriesProp.GetArrayElementAtIndex(i);
if (entryProp == null) continue;
var stateProp = entryProp.FindPropertyRelative("State");
var controllerNameProp = entryProp.FindPropertyRelative("ControllerName");
var controllerIndexProp = entryProp.FindPropertyRelative("ControllerIndex");
if (stateProp == null)
{
EditorGUILayout.HelpBox($"Unable to find 'State' on entry {i}.", MessageType.Error);
if (GUILayout.Button("Remove"))
{
_stateEntriesProp.DeleteArrayElementAtIndex(i);
break;
}
continue;
}
EditorGUILayout.BeginVertical(EditorStyles.helpBox);
var t = stateProp.managedReferenceValue.GetType();
var attr = t.GetCustomAttribute<ControlerStateNameAttribute>();
var tag = attr != null ? attr.StateName : t.Name;
EditorGUILayout.LabelField($"State:{tag}", EditorStyles.helpBox);
EditorGUILayout.BeginHorizontal();
var controllerNames = GetControllerNamesForRecorder(recorder);
int selIdx = -1;
if (!string.IsNullOrEmpty(controllerNameProp.stringValue))
{
selIdx = controllerNames.IndexOf(controllerNameProp.stringValue);
}
GUILayoutOption ctrlWidth = GUILayout.Width(140);
if (controllerNames.Count > 0)
{
int newSel = EditorGUILayout.Popup(selIdx, controllerNames.ToArray(), ctrlWidth);
if (newSel >= 0 && newSel < controllerNames.Count)
controllerNameProp.stringValue = controllerNames[newSel];
}
else
{
if (!string.IsNullOrEmpty(controllerNameProp.stringValue))
{
EditorGUI.BeginDisabledGroup(true);
EditorGUILayout.HelpBox($"绑定控制器 [{controllerNameProp.stringValue}] 不存在", MessageType.Error);
EditorGUI.EndDisabledGroup();
}
else
{
EditorGUILayout.HelpBox($"暂无控制器", MessageType.Warning);
}
}
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 = Mathf.Clamp(controllerIndexProp.intValue, 0, ctrlLen - 1);
if (controllerNames.Count > 0)
{
int newIdx = EditorGUILayout.Popup(curIdx, idxOptions, GUILayout.Width(60));
if (newIdx != curIdx) controllerIndexProp.intValue = newIdx;
}
GUILayout.FlexibleSpace();
if (GUILayout.Button(_removeIcon, GUILayout.Width(28), GUILayout.Height(20)))
{
_stateEntriesProp.DeleteArrayElementAtIndex(i);
break;
}
EditorGUILayout.EndHorizontal();
EditorGUILayout.Space(4);
if (stateProp.managedReferenceValue == null)
{
EditorGUILayout.HelpBox("State instance is null. Remove and re-add.", MessageType.Warning);
}
else
{
EditorGUILayout.BeginVertical(EditorStyles.helpBox);
EditorGUILayout.LabelField(new GUIContent("属性"), EditorStyles.boldLabel);
SerializedClassDrawer.DrawSerializableProperty(stateProp);
EditorGUILayout.EndVertical();
}
bool valid = true;
try
{
var inst = stateProp.managedReferenceValue as ControllerStateBase;
if (inst != null) valid = inst.Valid(recorder);
}
catch (Exception ex)
{
valid = false;
Debug.LogException(ex);
}
if (!valid)
{
EditorGUILayout.HelpBox($"当前状态核验失败 请查看{stateProp.managedReferenceValue.GetType().FullName}脚本Valid", MessageType.Error);
}
EditorGUILayout.EndVertical();
EditorGUILayout.Space();
}
serializedObject.ApplyModifiedProperties();
}
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");
var defaultValueProp = entryProp.FindPropertyRelative("DefaultValue");
ControllerStateBase instance = null;
try
{
instance = (ControllerStateBase)Activator.CreateInstance(type);
}
catch (Exception ex)
{
Debug.LogException(ex);
}
if (instance != null)
{
var recorder = serializedObject.targetObject as UXControllerStateRecorder;
instance.Init(recorder);
}
// 把实例写进 SerializedProperty需保证你的 State 字段用了 [SerializeReference]
if (stateProp != null) stateProp.managedReferenceValue = instance;
if (stateProp != null) stateProp.isExpanded = true;
controllerNameProp.stringValue = "";
controllerIndexProp.intValue = 0;
serializedObject.ApplyModifiedProperties();
EditorUtility.SetDirty(serializedObject.targetObject);
}
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;
}
}
#endif